add ISODuration type for ISO8061 duration compatibility
This commit is contained in:
+99
@@ -0,0 +1,99 @@
|
||||
package clocktime
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/sosodev/duration"
|
||||
)
|
||||
|
||||
// ISODuration wraps a time.Duration to provide custom JSON and SQL
|
||||
// serialization and full ISO8061 compatibility. Note: ISODuration is limited
|
||||
// to the same range as time.Duration despite ISO8061 supporting larger durations.
|
||||
type ISODuration time.Duration
|
||||
|
||||
// NewISODuration creates a new duration from numeric components.
|
||||
func NewISODuration(hours, minutes, seconds int) ISODuration {
|
||||
return ISODuration(
|
||||
time.Hour*time.Duration(hours) +
|
||||
time.Minute*time.Duration(minutes) +
|
||||
time.Second*time.Duration(seconds),
|
||||
)
|
||||
}
|
||||
|
||||
// DurationFromISOString parses an ISO8601 duration string (e.g., "PT1H30M45S")
|
||||
// and returns an ISODuration.
|
||||
func DurationFromISOString(s string) (ISODuration, error) {
|
||||
d, err := duration.Parse(s)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error parsing ISO8061 duration: %w", err)
|
||||
}
|
||||
return ISODuration(d.ToTimeDuration()), nil
|
||||
}
|
||||
|
||||
// ISODurationFromDuration converts a time.Duration to an ISODuration.
|
||||
func ISODurationFromDuration(d time.Duration) ISODuration {
|
||||
return ISODuration(d)
|
||||
}
|
||||
|
||||
// String returns the ISO8601 string representation of the ISODuration.
|
||||
func (d ISODuration) String() string {
|
||||
td := time.Duration(d)
|
||||
isoDur := duration.FromTimeDuration(td)
|
||||
return isoDur.String()
|
||||
}
|
||||
|
||||
// Duration returns the time.Duration representation of the ISODuration.
|
||||
func (d ISODuration) Duration() time.Duration {
|
||||
return time.Duration(d)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface for ISODuration,
|
||||
// serializing it as an ISO8601 duration string.
|
||||
func (d ISODuration) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal("\"" + d.String() + "\"")
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface for ISODuration,
|
||||
// parsing an ISO8601 duration string.
|
||||
func (d *ISODuration) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
parsedDur, err := DurationFromISOString(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*d = parsedDur
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalGQL implements the graphql.Marshaler interface for ISODuration,
|
||||
// serializing it as an ISO8601 duration string.
|
||||
func (d ISODuration) MarshalGQL(w io.Writer) {
|
||||
fmt.Fprint(w, "\""+d.String()+"\"")
|
||||
}
|
||||
|
||||
// UnmarshalGQL implements the graphql.Unmarshaler interface for ISODuration,
|
||||
// parsing an ISO8601 duration string. nil values are treated as zero duration.
|
||||
func (d *ISODuration) UnmarshalGQL(value any) error {
|
||||
if value == nil {
|
||||
*d = ISODuration(0)
|
||||
return nil
|
||||
}
|
||||
|
||||
s, ok := value.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("ISODuration must be a string")
|
||||
}
|
||||
|
||||
parsedDur, err := DurationFromISOString(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*d = parsedDur
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user