package json
import (
)
var (
timeDurationType = reflect.TypeFor[time.Duration]()
timeTimeType = reflect.TypeFor[time.Time]()
)
func ( *arshaler, reflect.Type) *arshaler {
switch {
case timeDurationType:
.nonDefault = true
:= .marshal
.marshal = func( *jsontext.Encoder, addressableValue, *jsonopts.Struct) error {
:= export.Encoder()
var durationArshaler
if .Flags.Has(jsonflags.FormatTag) {
if !.initFormat(.Format) {
return newInvalidFormatError(, )
}
} else if .Flags.Get(jsonflags.FormatDurationAsNano) {
return (, , )
} else {
var string
if .Flags.Get(jsonflags.FormatTagSupported) {
= "; specify an explicit format"
}
return newMarshalErrorBefore(, , errors.New("no default representation"+))
}
if .Flags.Get(jsonflags.StringTag) && !.isNumeric() && !.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
return newMarshalErrorBefore(, , errInvalidStringTag)
}
.td, _ = reflect.TypeAssert[time.Duration](.Value)
:= stringOrNumberKind(!.isNumeric() || .Tokens.Last.NeedObjectName() || .Flags.Get(jsonflags.StringifyNumbers|jsonflags.StringTag))
if := .AppendRaw(, true, .appendMarshal); != nil {
if !isSyntacticError() && !export.IsIOError() {
= newMarshalErrorBefore(, , )
}
return
}
return nil
}
:= .unmarshal
.unmarshal = func( *jsontext.Decoder, addressableValue, *jsonopts.Struct) error {
:= export.Decoder()
var durationArshaler
if .Flags.Has(jsonflags.FormatTag) {
if !.initFormat(.Format) {
return newInvalidFormatError(, )
}
} else if .Flags.Get(jsonflags.FormatDurationAsNano) {
return (, , )
} else {
var string
if .Flags.Get(jsonflags.FormatTagSupported) {
= "; specify an explicit format"
}
return newUnmarshalErrorBeforeWithSkipping(, , errors.New("no default representation"+))
}
if .Flags.Get(jsonflags.StringTag) && !.isNumeric() && !.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
return newUnmarshalErrorBeforeWithSkipping(, , errInvalidStringTag)
}
:= !.isNumeric() || .Tokens.Last.NeedObjectName() || .Flags.Get(jsonflags.StringifyNumbers|jsonflags.StringTag)
var jsonwire.ValueFlags
, := reflect.TypeAssert[*time.Duration](.Addr())
, := .ReadValue(&)
if != nil {
return
}
switch := .Kind(); {
case 'n':
if !.Flags.Get(jsonflags.MergeWithLegacySemantics) {
* = time.Duration(0)
}
return nil
case '"':
if ! {
break
}
= jsonwire.UnquoteMayCopy(, .IsVerbatim())
if := .unmarshal(); != nil {
return newUnmarshalErrorAfter(, , )
}
* = .td
return nil
case '0':
if {
break
}
if := .unmarshal(); != nil {
return newUnmarshalErrorAfter(, , )
}
* = .td
return nil
}
return newUnmarshalErrorAfter(, , nil)
}
case timeTimeType:
.nonDefault = true
.marshal = func( *jsontext.Encoder, addressableValue, *jsonopts.Struct) ( error) {
:= export.Encoder()
var timeArshaler
if .Flags.Has(jsonflags.FormatTag) {
if !.initFormat(.Format) {
return newInvalidFormatError(, )
}
}
if .Flags.Get(jsonflags.StringTag) && !.isNumeric() && !.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
return newMarshalErrorBefore(, , errInvalidStringTag)
}
.tt, _ = reflect.TypeAssert[time.Time](.Value)
:= stringOrNumberKind(!.isNumeric() || .Tokens.Last.NeedObjectName() || .Flags.Get(jsonflags.StringifyNumbers|jsonflags.StringTag))
if := .AppendRaw(, !.hasCustomFormat(), .appendMarshal); != nil {
if .Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
return internal.NewMarshalerError(.Addr().Interface(), , "MarshalJSON")
}
if !isSyntacticError() && !export.IsIOError() {
= newMarshalErrorBefore(, , )
}
return
}
return nil
}
.unmarshal = func( *jsontext.Decoder, addressableValue, *jsonopts.Struct) ( error) {
:= export.Decoder()
var timeArshaler
if .Flags.Has(jsonflags.FormatTag) {
if !.initFormat(.Format) {
return newInvalidFormatError(, )
}
} else if .Flags.Get(jsonflags.ParseTimeWithLooseRFC3339) {
.looseRFC3339 = true
}
if .Flags.Get(jsonflags.StringTag) && !.isNumeric() && !.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
return newUnmarshalErrorBeforeWithSkipping(, , errInvalidStringTag)
}
:= !.isNumeric() || .Tokens.Last.NeedObjectName() || .Flags.Get(jsonflags.StringifyNumbers|jsonflags.StringTag)
var jsonwire.ValueFlags
, := reflect.TypeAssert[*time.Time](.Addr())
, := .ReadValue(&)
if != nil {
return
}
switch := .Kind(); {
case 'n':
if !.Flags.Get(jsonflags.MergeWithLegacySemantics) {
* = time.Time{}
}
return nil
case '"':
if ! {
break
}
= jsonwire.UnquoteMayCopy(, .IsVerbatim())
if := .unmarshal(); != nil {
if .Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
return
}
return newUnmarshalErrorAfter(, , )
}
* = .tt
return nil
case '0':
if {
break
}
if := .unmarshal(); != nil {
if .Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
return
}
return newUnmarshalErrorAfter(, , )
}
* = .tt
return nil
}
return newUnmarshalErrorAfter(, , nil)
}
}
return
}
type durationArshaler struct {
td time.Duration
base uint64
}
func ( *durationArshaler) ( string) ( bool) {
switch {
case "units":
.base = 0
case "sec":
.base = 1e9
case "milli":
.base = 1e6
case "micro":
.base = 1e3
case "nano":
.base = 1e0
case "iso8601":
.base = 8601
default:
return false
}
return true
}
func ( *durationArshaler) () bool {
return .base != 0 && .base != 8601
}
func ( *durationArshaler) ( []byte) ([]byte, error) {
switch .base {
case 0:
return append(, .td.String()...), nil
case 8601:
return appendDurationISO8601(, .td), nil
default:
return appendDurationBase10(, .td, .base), nil
}
}
func ( *durationArshaler) ( []byte) ( error) {
switch .base {
case 0:
.td, = time.ParseDuration(string())
case 8601:
.td, = parseDurationISO8601()
default:
.td, = parseDurationBase10(, .base)
}
return
}
type timeArshaler struct {
tt time.Time
base uint64
format string
looseRFC3339 bool
}
func ( *timeArshaler) ( string) bool {
if len() == 0 {
return false
}
.base = math.MaxUint
if := [0]; !('a' <= && <= 'z') && !('A' <= && <= 'Z') {
.format =
return true
}
switch {
case "ANSIC":
.format = time.ANSIC
case "UnixDate":
.format = time.UnixDate
case "RubyDate":
.format = time.RubyDate
case "RFC822":
.format = time.RFC822
case "RFC822Z":
.format = time.RFC822Z
case "RFC850":
.format = time.RFC850
case "RFC1123":
.format = time.RFC1123
case "RFC1123Z":
.format = time.RFC1123Z
case "RFC3339":
.base = 0
.format = time.RFC3339
case "RFC3339Nano":
.base = 0
.format = time.RFC3339Nano
case "Kitchen":
.format = time.Kitchen
case "Stamp":
.format = time.Stamp
case "StampMilli":
.format = time.StampMilli
case "StampMicro":
.format = time.StampMicro
case "StampNano":
.format = time.StampNano
case "DateTime":
.format = time.DateTime
case "DateOnly":
.format = time.DateOnly
case "TimeOnly":
.format = time.TimeOnly
case "unix":
.base = 1e0
case "unixmilli":
.base = 1e3
case "unixmicro":
.base = 1e6
case "unixnano":
.base = 1e9
default:
if strings.TrimFunc(, isLetterOrDigit) == "" {
return false
}
.format =
}
return true
}
func ( *timeArshaler) () bool {
return int(.base) > 0
}
func ( *timeArshaler) () bool {
return .base == math.MaxUint
}
func ( *timeArshaler) ( []byte) ([]byte, error) {
switch .base {
case 0:
:= cmp.Or(.format, time.RFC3339Nano)
:= len()
= .tt.AppendFormat(, )
switch := [:]; {
case [len("9999")] != '-':
return , errors.New("year outside of range [0,9999]")
case [len()-1] != 'Z':
:= [len()-len("Z07:00")]
if ('0' <= && <= '9') || parseDec2([len()-len("07:00"):]) >= 24 {
return , errors.New("timezone hour outside of range [0,23]")
}
}
return , nil
case math.MaxUint:
return .tt.AppendFormat(, .format), nil
default:
return appendTimeUnix(, .tt, .base), nil
}
}
func ( *timeArshaler) ( []byte) ( error) {
switch .base {
case 0:
if := .tt.UnmarshalText(); != nil {
return
}
:= func(, , , , string) error {
return &time.ParseError{Layout: , Value: , LayoutElem: , ValueElem: , Message: }
}
switch {
case .looseRFC3339:
return nil
case [len("2006-01-02T")+1] == ':':
return (time.RFC3339, string(), "15", string([len("2006-01-02T"):][:1]), "")
case [len("2006-01-02T15:04:05")] == ',':
return (time.RFC3339, string(), ".", ",", "")
case [len()-1] != 'Z':
switch {
case parseDec2([len()-len("07:00"):]) >= 24:
return (time.RFC3339, string(), "Z07:00", string([len()-len("Z07:00"):]), ": timezone hour out of range")
case parseDec2([len()-len("00"):]) >= 60:
return (time.RFC3339, string(), "Z07:00", string([len()-len("Z07:00"):]), ": timezone minute out of range")
}
}
return nil
case math.MaxUint:
.tt, = time.Parse(.format, string())
return
default:
.tt, = parseTimeUnix(, .base)
return
}
}
func ( []byte, time.Duration, uint64) []byte {
, := mayAppendDurationSign(, )
, := bits.Div64(0, , uint64())
= strconv.AppendUint(, , 10)
return appendFracBase10(, , )
}
func ( []byte, uint64) (time.Duration, error) {
, := consumeSign(, false)
, := bytesCutByte(, '.', true)
, := jsonwire.ParseUint()
, := parseFracBase10(, )
, := bits.Mul64(, uint64())
, := bits.Add64(, uint64(), 0)
switch := mayApplyDurationSign(, ); {
case (! && != math.MaxUint64) || !:
return 0, fmt.Errorf("invalid duration %q: %w", , strconv.ErrSyntax)
case ! || > 0 || > 0 || != ( < 0):
return 0, fmt.Errorf("invalid duration %q: %w", , strconv.ErrRange)
default:
return , nil
}
}
func ( []byte, time.Duration) []byte {
if == 0 {
return append(, "PT0S"...)
}
, := mayAppendDurationSign(, )
= append(, "PT"...)
, := bits.Div64(0, , 1e9)
, := bits.Div64(0, , 60)
, := bits.Div64(0, , 60)
if > 0 {
= append(strconv.AppendUint(, , 10), 'H')
}
if > 0 {
= append(strconv.AppendUint(, , 10), 'M')
}
if > 0 || > 0 {
= append(appendFracBase10(strconv.AppendUint(, , 10), , 1e9), 'S')
}
return
}
const daysPerYear = 365.2425
var errInaccurateDateUnits = errors.New("inaccurate year, month, week, or day units")
func ( []byte) (time.Duration, error) {
var , , , bool
var , , uint64
:= func( []byte, , byte) (, []byte, bool) {
for , := range {
if == || == {
return [:], [+1:], true
}
}
return , nil, false
}
:= func( []byte, , byte, time.Duration) []byte {
, , := (, , )
if ! || {
return
}
, , := (, '.', ',')
if {
= true
= || len() == len("") || > time.Hour
if == time.Second {
, = parsePaddedBase10(, uint64(time.Second))
= || !
} else {
, := strconv.ParseFloat("0."+string(), 64)
= || != nil || len(bytes.Trim([len("."):], "0123456789")) > 0
= uint64(math.Round( * float64()))
}
, = bits.Add64(, , 0)
= || > 0
}
for len() > 1 && [0] == '0' {
= [len("0"):]
}
, := jsonwire.ParseUint()
, := bits.Mul64(, uint64())
, = bits.Add64(, , 0)
= || (! && != math.MaxUint64)
= || (! && == math.MaxUint64) || > 0 || > 0
= || > time.Hour
return
}
, := consumeSign(, true)
, , := (, 'P', 'p')
, , := (, 'T', 't')
= || len() > 0 || ! || ( && len() == 0) || len()+len() == 0
if len() > 0 {
= (, 'Y', 'y', time.Duration(daysPerYear*24*60*60*1e9))
= (, 'M', 'm', time.Duration(daysPerYear/12*24*60*60*1e9))
= (, 'W', 'w', time.Duration(7*24*60*60*1e9))
= (, 'D', 'd', time.Duration(24*60*60*1e9))
= || len() > 0
}
if len() > 0 {
= (, 'H', 'h', time.Duration(60*60*1e9))
= (, 'M', 'm', time.Duration(60*1e9))
= (, 'S', 's', time.Duration(1e9))
= || len() > 0
}
:= mayApplyDurationSign(, )
= || ( != ( < 0) && != 0)
switch {
case :
return 0, fmt.Errorf("invalid ISO 8601 duration %q: %w", , strconv.ErrSyntax)
case :
return 0, fmt.Errorf("invalid ISO 8601 duration %q: %w", , strconv.ErrRange)
case :
return , fmt.Errorf("invalid ISO 8601 duration %q: %w", , errInaccurateDateUnits)
default:
return , nil
}
}
func ( []byte, time.Duration) ([]byte, uint64) {
if < 0 {
= append(, '-')
*= -1
}
return , uint64()
}
func ( uint64, bool) time.Duration {
if {
return -1 * time.Duration()
} else {
return +1 * time.Duration()
}
}
func ( []byte, time.Time, uint64) []byte {
, := .Unix(), int64(.Nanosecond())
if < 0 {
= append(, '-')
, = negateSecNano(, )
}
switch {
case == 1e0:
= strconv.AppendUint(, uint64(), 10)
return appendFracBase10(, uint64(), 1e9)
case uint64() < 1e9:
= strconv.AppendUint(, uint64()*uint64()+uint64(uint64()/(1e9/)), 10)
return appendFracBase10(, (uint64()*)%1e9, 1e9)
default:
= strconv.AppendUint(, uint64(), 10)
= appendPaddedBase10(, uint64()/(1e9/), )
return appendFracBase10(, (uint64()*)%1e9, 1e9)
}
}
func ( []byte, uint64) (time.Time, error) {
, := consumeSign(, false)
, := bytesCutByte(, '.', true)
, := jsonwire.ParseUint()
, := parseFracBase10(, 1e9/)
var , int64
switch {
case == 1e0:
= int64()
= int64()
case :
= int64( / )
= int64((%)*(1e9/) + )
case ! && == math.MaxUint64:
:= int(math.Log10(float64()))
, = jsonwire.ParseUint([:len()-])
, := parsePaddedBase10([len()-:], )
= int64()
= int64(*(1e9/) + )
}
if {
, = negateSecNano(, )
}
switch := time.Unix(, ).UTC(); {
case (! && != math.MaxUint64) || !:
return time.Time{}, fmt.Errorf("invalid time %q: %w", , strconv.ErrSyntax)
case ! || != (.Unix() < 0):
return time.Time{}, fmt.Errorf("invalid time %q: %w", , strconv.ErrRange)
default:
return , nil
}
}
func (, int64) (int64, int64) {
= ^
= - + 1e9
+= int64( / 1e9)
%= 1e9
return ,
}
func ( []byte, , uint64) []byte {
if == 0 {
return
}
return bytes.TrimRight(appendPaddedBase10(append(, '.'), , ), "0")
}
func ( []byte, uint64) ( uint64, bool) {
switch {
case len() == 0:
return 0, true
case len() < len(".0") || [0] != '.':
return 0, false
}
return parsePaddedBase10([len("."):], )
}
func ( []byte, , uint64) []byte {
if < /10 {
:= len()
= strconv.AppendUint(, +/10, 10)
[]--
return
}
return strconv.AppendUint(, , 10)
}
func ( []byte, uint64) ( uint64, bool) {
:= uint64(1)
for < {
*= 10
if len() > 0 {
if [0] < '0' || '9' < [0] {
return , false
}
+= uint64([0] - '0')
= [1:]
}
*= 10
}
if len() > 0 && len(bytes.TrimRight(, "0123456789")) > 0 {
return , false
}
return , true
}
func ( []byte, bool) ([]byte, bool) {
if len() > 0 {
if [0] == '-' {
return [len("-"):], true
} else if [0] == '+' && {
return [len("+"):], false
}
}
return , false
}
func ( []byte, byte, bool) ([]byte, []byte) {
if := bytes.IndexByte(, ); >= 0 {
if {
return [:], [:]
}
return [:], [+1:]
}
return , nil
}
func ( []byte) byte {
if len() < 2 {
return 0
}
return 10*([0]-'0') + ([1] - '0')
}