initial commit
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
# clocktime
|
||||||
|
|
||||||
|
clocktime provides a type that holds a calendar-independent time of day value in 24-hour format.
|
||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
package clocktime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClockTime represents a time of day without a date component in 24-hour format.
|
||||||
|
// It is used to represent times in a way that is independent of any specific date.
|
||||||
|
type ClockTime struct {
|
||||||
|
Hour int `json:"hour"`
|
||||||
|
Minute int `json:"minute"`
|
||||||
|
Second int `json:"second"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClockTime creates a new ClockTime instance.
|
||||||
|
func NewClockTime(hour, minute, second int) ClockTime {
|
||||||
|
return ClockTime{
|
||||||
|
Hour: hour,
|
||||||
|
Minute: minute,
|
||||||
|
Second: second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClockTimeFromString parses a ClockTime from a string in the format "HH:MM:SS" (Subset of RFC 8601).
|
||||||
|
func ClockTimeFromString(s string) (ClockTime, error) {
|
||||||
|
var hour, minute, second int
|
||||||
|
n, err := fmt.Sscanf(s, "%d:%d:%d", &hour, &minute, &second)
|
||||||
|
if err != nil || n != 3 {
|
||||||
|
return ClockTime{}, fmt.Errorf("invalid time format: %s", s)
|
||||||
|
}
|
||||||
|
if hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59 {
|
||||||
|
return ClockTime{}, fmt.Errorf("time out of range: %s", s)
|
||||||
|
}
|
||||||
|
return NewClockTime(hour, minute, second), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClockTimeFromTime converts a time.Time to a ClockTime.
|
||||||
|
func ClockTimeFromTime(t time.Time) ClockTime {
|
||||||
|
return NewClockTime(t.Hour(), t.Minute(), t.Second())
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the ClockTime in "HH:MM:SS" format.
|
||||||
|
func (t ClockTime) String() string {
|
||||||
|
return fmt.Sprintf("%02d:%02d:%02d", t.Hour, t.Minute, t.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time returns a time.Time representation of the ClockTime.
|
||||||
|
// It uses a fixed date (January 1, 1970) to create a time.Time object.
|
||||||
|
func (t ClockTime) Time() time.Time {
|
||||||
|
return time.Date(1970, 1, 1, t.Hour, t.Minute, t.Second, 0, time.UTC)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements the json.Marshaler interface for ClockTime.
|
||||||
|
func (t ClockTime) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(t.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Unmarshaler interface for ClockTime.
|
||||||
|
func (t *ClockTime) UnmarshalJSON(data []byte) error {
|
||||||
|
var timeString string
|
||||||
|
if err := json.Unmarshal(data, &timeString); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
parsedTime, err := ClockTimeFromString(timeString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*t = parsedTime
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalGQL implements the graphql.Marshaler interface for ClockTime.
|
||||||
|
func (t ClockTime) MarshalGQL(w io.Writer) {
|
||||||
|
fmt.Fprint(w, t.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalGQL implements the graphql.Unmarshaler interface for ClockTime.
|
||||||
|
func (t *ClockTime) UnmarshalGQL(value any) error {
|
||||||
|
if value == nil {
|
||||||
|
*t = ClockTime{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
str, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("ClockTime must be a string, got %T", value)
|
||||||
|
}
|
||||||
|
parsedTime, err := ClockTimeFromString(str)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*t = parsedTime
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the database/sql/driver.Valuer interface for ClockTime.
|
||||||
|
// Marshals the ClockTime to a time.Time for database storage.
|
||||||
|
func (t ClockTime) Value() (driver.Value, error) {
|
||||||
|
return t.Time(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements the database/sql.Scanner interface for ClockTime.
|
||||||
|
func (t *ClockTime) Scan(value any) error {
|
||||||
|
if value == nil {
|
||||||
|
*t = ClockTime{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rawTime, ok := value.(time.Time)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("ClockTime must be a time.Time, got %T", value)
|
||||||
|
}
|
||||||
|
*t = ClockTimeFromTime(rawTime)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user