types/jsonformat/duration.go
Rama main · réplica de lectura del repositorio original.
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
package jsonformat
import (
"time"
"github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
)
// DurationNano is a [time.Duration] that represents a duration as
// a JSON number of nanoseconds. If [json.StringifyNumbers] is specified,
// then it represents the duration as a JSON number within a JSON string.
//
// This format is what v1 [encoding/json] historically used for durations.
// It is not recommended that newly defined types represent durations
// using this format, as a standalone JSON number lacks context
// as to the units of the duration.
type DurationNano struct{ time.Duration }
func (d DurationNano) MarshalJSON() ([]byte, error) {
return json.Marshal(d)
}
func (d DurationNano) MarshalJSONTo(enc *jsontext.Encoder) error {
// Marshaling as an int64 directly allows us to properly handle
// the [json.StringifyNumbers] option.
return json.MarshalEncode(enc, int64(d.Duration))
}
func (d *DurationNano) UnmarshalJSON(b []byte) error {
return json.Unmarshal(b, d)
}
func (d *DurationNano) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
// Unmarshaling into an int64 directly allows us to properly handle
// the [json.StringifyNumbers] option.
return json.UnmarshalDecode(dec, (*int64)(&d.Duration))
}
// DurationUnits is a [time.Duration] that represents a duration as
// a JSON string using the format of [time.Duration.String].
//
// While this format is human-readable (e.g., "43m17.152s"),
// it is also highly specific to the Go ecosystem
// (and not used elsewhere in the industry).
type DurationUnits struct{ time.Duration }
func (d DurationUnits) MarshalJSON() ([]byte, error) {
return json.Marshal(d)
}
func (d DurationUnits) MarshalJSONTo(enc *jsontext.Encoder) error {
b := enc.AvailableBuffer()
b = append(b, '"')
b = append(b, d.Duration.String()...)
b = append(b, '"')
return enc.WriteValue(b)
}
func (d *DurationUnits) UnmarshalJSON(b []byte) error {
return json.Unmarshal(b, d)
}
func (d *DurationUnits) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
isNull := dec.PeekKind() == 'n'
var s string
if err := json.UnmarshalDecode(dec, &s); err != nil {
return err
}
if isNull {
d.Duration = 0
return nil
}
var err error
d.Duration, err = time.ParseDuration(s)
return err
}
// DurationISO8601 is a [time.Duration] that represents a duration as
// a JSON string using a subset of the ISO 8601 format.
// In particular, it uses the exact grammar of ISO 8601 that JavaScript
// uses for its Temporal.Duration type.
//
// While this format is relatively novel in the Go ecosystem,
// the fact that JSON finds its heritage in JavaScript and that
// JavaScript has adopted ISO 8601 as the JSON representation for durations
// suggests that JavaScript's particular flavor of ISO 8601 may well become
// the de facto standard for representing durations across the industry.
type DurationISO8601 struct{ time.Duration }
func (d DurationISO8601) MarshalJSON() ([]byte, error) {
return json.Marshal(d)
}
func (d DurationISO8601) MarshalJSONTo(enc *jsontext.Encoder) error {
b := enc.AvailableBuffer()
b = append(b, '"')
b = appendDurationISO8601(b, d.Duration)
b = append(b, '"')
return enc.WriteValue(b)
}
func (d *DurationISO8601) UnmarshalJSON(b []byte) error {
return json.Unmarshal(b, d)
}
func (d *DurationISO8601) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
isNull := dec.PeekKind() == 'n'
var s string
if err := json.UnmarshalDecode(dec, &s); err != nil {
return err
}
if isNull {
d.Duration = 0
return nil
}
var err error
d.Duration, err = parseDurationISO8601([]byte(s))
return err
}