mirror of
https://github.com/rocky-linux/peridot.git
synced 2024-10-19 07:55:07 +00:00
242 lines
8.0 KiB
Go
242 lines
8.0 KiB
Go
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package json
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
timeDurationType = reflect.TypeOf((*time.Duration)(nil)).Elem()
|
||
|
timeTimeType = reflect.TypeOf((*time.Time)(nil)).Elem()
|
||
|
)
|
||
|
|
||
|
func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler {
|
||
|
// Ideally, time types would implement MarshalerV2 and UnmarshalerV2,
|
||
|
// but that would incur a dependency on package json from package time.
|
||
|
// Given how widely used time is, it is more acceptable that we incur a
|
||
|
// dependency on time from json.
|
||
|
//
|
||
|
// Injecting the arshaling functionality like this will not be identical
|
||
|
// to actually declaring methods on the time types since embedding of the
|
||
|
// time types will not be able to forward this functionality.
|
||
|
switch t {
|
||
|
case timeDurationType:
|
||
|
fncs.nonDefault = true
|
||
|
marshalNanos := fncs.marshal
|
||
|
fncs.marshal = func(mo MarshalOptions, enc *Encoder, va addressableValue) error {
|
||
|
if mo.format != "" && mo.formatDepth == enc.tokens.depth() {
|
||
|
if mo.format == "nanos" {
|
||
|
mo.format = ""
|
||
|
return marshalNanos(mo, enc, va)
|
||
|
} else {
|
||
|
return newInvalidFormatError("marshal", t, mo.format)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
td := va.Interface().(time.Duration)
|
||
|
b := enc.UnusedBuffer()
|
||
|
b = append(b, '"')
|
||
|
b = append(b, td.String()...) // never contains special characters
|
||
|
b = append(b, '"')
|
||
|
return enc.WriteValue(b)
|
||
|
}
|
||
|
unmarshalNanos := fncs.unmarshal
|
||
|
fncs.unmarshal = func(uo UnmarshalOptions, dec *Decoder, va addressableValue) error {
|
||
|
// TODO: Should there be a flag that specifies that we can unmarshal
|
||
|
// from either form since there would be no ambiguity?
|
||
|
if uo.format != "" && uo.formatDepth == dec.tokens.depth() {
|
||
|
if uo.format == "nanos" {
|
||
|
uo.format = ""
|
||
|
return unmarshalNanos(uo, dec, va)
|
||
|
} else {
|
||
|
return newInvalidFormatError("unmarshal", t, uo.format)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var flags valueFlags
|
||
|
td := va.Addr().Interface().(*time.Duration)
|
||
|
val, err := dec.readValue(&flags)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
switch k := val.Kind(); k {
|
||
|
case 'n':
|
||
|
*td = time.Duration(0)
|
||
|
return nil
|
||
|
case '"':
|
||
|
val = unescapeStringMayCopy(val, flags.isVerbatim())
|
||
|
td2, err := time.ParseDuration(string(val))
|
||
|
if err != nil {
|
||
|
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t, Err: err}
|
||
|
}
|
||
|
*td = td2
|
||
|
return nil
|
||
|
default:
|
||
|
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t}
|
||
|
}
|
||
|
}
|
||
|
case timeTimeType:
|
||
|
fncs.nonDefault = true
|
||
|
fncs.marshal = func(mo MarshalOptions, enc *Encoder, va addressableValue) error {
|
||
|
format := time.RFC3339Nano
|
||
|
isRFC3339 := true
|
||
|
if mo.format != "" && mo.formatDepth == enc.tokens.depth() {
|
||
|
var err error
|
||
|
format, isRFC3339, err = checkTimeFormat(mo.format)
|
||
|
if err != nil {
|
||
|
return &SemanticError{action: "marshal", GoType: t, Err: err}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tt := va.Interface().(time.Time)
|
||
|
b := enc.UnusedBuffer()
|
||
|
b = append(b, '"')
|
||
|
b = tt.AppendFormat(b, format)
|
||
|
b = append(b, '"')
|
||
|
if isRFC3339 {
|
||
|
// Not all Go timestamps can be represented as valid RFC 3339.
|
||
|
// Explicitly check for these edge cases.
|
||
|
// See https://go.dev/issue/4556 and https://go.dev/issue/54580.
|
||
|
var err error
|
||
|
switch b := b[len(`"`) : len(b)-len(`"`)]; {
|
||
|
case b[len("9999")] != '-': // year must be exactly 4 digits wide
|
||
|
err = errors.New("year outside of range [0,9999]")
|
||
|
case b[len(b)-1] != 'Z':
|
||
|
c := b[len(b)-len("Z07:00")]
|
||
|
if ('0' <= c && c <= '9') || parseDec2(b[len(b)-len("07:00"):]) >= 24 {
|
||
|
err = errors.New("timezone hour outside of range [0,23]")
|
||
|
}
|
||
|
}
|
||
|
if err != nil {
|
||
|
return &SemanticError{action: "marshal", GoType: t, Err: err}
|
||
|
}
|
||
|
return enc.WriteValue(b) // RFC 3339 never needs JSON escaping
|
||
|
}
|
||
|
// The format may contain special characters that need escaping.
|
||
|
// Verify that the result is a valid JSON string (common case),
|
||
|
// otherwise escape the string correctly (slower case).
|
||
|
if consumeSimpleString(b) != len(b) {
|
||
|
b, _ = appendString(nil, string(b[len(`"`):len(b)-len(`"`)]), true, nil)
|
||
|
}
|
||
|
return enc.WriteValue(b)
|
||
|
}
|
||
|
fncs.unmarshal = func(uo UnmarshalOptions, dec *Decoder, va addressableValue) error {
|
||
|
format := time.RFC3339
|
||
|
isRFC3339 := true
|
||
|
if uo.format != "" && uo.formatDepth == dec.tokens.depth() {
|
||
|
var err error
|
||
|
format, isRFC3339, err = checkTimeFormat(uo.format)
|
||
|
if err != nil {
|
||
|
return &SemanticError{action: "unmarshal", GoType: t, Err: err}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var flags valueFlags
|
||
|
tt := va.Addr().Interface().(*time.Time)
|
||
|
val, err := dec.readValue(&flags)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
k := val.Kind()
|
||
|
switch k {
|
||
|
case 'n':
|
||
|
*tt = time.Time{}
|
||
|
return nil
|
||
|
case '"':
|
||
|
val = unescapeStringMayCopy(val, flags.isVerbatim())
|
||
|
tt2, err := time.Parse(format, string(val))
|
||
|
if isRFC3339 && err == nil {
|
||
|
// TODO(https://go.dev/issue/54580): RFC 3339 specifies
|
||
|
// the exact grammar of a valid timestamp. However,
|
||
|
// the parsing functionality in "time" is too loose and
|
||
|
// incorrectly accepts invalid timestamps as valid.
|
||
|
// Remove these manual checks when "time" checks it for us.
|
||
|
newParseError := func(layout, value, layoutElem, valueElem, message string) error {
|
||
|
return &time.ParseError{Layout: layout, Value: value, LayoutElem: layoutElem, ValueElem: valueElem, Message: message}
|
||
|
}
|
||
|
switch {
|
||
|
case val[len("2006-01-02T")+1] == ':': // hour must be two digits
|
||
|
err = newParseError(format, string(val), "15", string(val[len("2006-01-02T"):][:1]), "")
|
||
|
case val[len("2006-01-02T15:04:05")] == ',': // sub-second separator must be a period
|
||
|
err = newParseError(format, string(val), ".", ",", "")
|
||
|
case val[len(val)-1] != 'Z':
|
||
|
switch {
|
||
|
case parseDec2(val[len(val)-len("07:00"):]) >= 24: // timezone hour must be in range
|
||
|
err = newParseError(format, string(val), "Z07:00", string(val[len(val)-len("Z07:00"):]), ": timezone hour out of range")
|
||
|
case parseDec2(val[len(val)-len("00"):]) >= 60: // timezone minute must be in range
|
||
|
err = newParseError(format, string(val), "Z07:00", string(val[len(val)-len("Z07:00"):]), ": timezone minute out of range")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if err != nil {
|
||
|
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t, Err: err}
|
||
|
}
|
||
|
*tt = tt2
|
||
|
return nil
|
||
|
default:
|
||
|
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return fncs
|
||
|
}
|
||
|
|
||
|
func checkTimeFormat(format string) (string, bool, error) {
|
||
|
// We assume that an exported constant in the time package will
|
||
|
// always start with an uppercase ASCII letter.
|
||
|
if len(format) > 0 && 'A' <= format[0] && format[0] <= 'Z' {
|
||
|
switch format {
|
||
|
case "ANSIC":
|
||
|
return time.ANSIC, false, nil
|
||
|
case "UnixDate":
|
||
|
return time.UnixDate, false, nil
|
||
|
case "RubyDate":
|
||
|
return time.RubyDate, false, nil
|
||
|
case "RFC822":
|
||
|
return time.RFC822, false, nil
|
||
|
case "RFC822Z":
|
||
|
return time.RFC822Z, false, nil
|
||
|
case "RFC850":
|
||
|
return time.RFC850, false, nil
|
||
|
case "RFC1123":
|
||
|
return time.RFC1123, false, nil
|
||
|
case "RFC1123Z":
|
||
|
return time.RFC1123Z, false, nil
|
||
|
case "RFC3339":
|
||
|
return time.RFC3339, true, nil
|
||
|
case "RFC3339Nano":
|
||
|
return time.RFC3339Nano, true, nil
|
||
|
case "Kitchen":
|
||
|
return time.Kitchen, false, nil
|
||
|
case "Stamp":
|
||
|
return time.Stamp, false, nil
|
||
|
case "StampMilli":
|
||
|
return time.StampMilli, false, nil
|
||
|
case "StampMicro":
|
||
|
return time.StampMicro, false, nil
|
||
|
case "StampNano":
|
||
|
return time.StampNano, false, nil
|
||
|
default:
|
||
|
// Reject any format that is an exported Go identifier in case
|
||
|
// new format constants are added to the time package.
|
||
|
if strings.TrimFunc(format, isLetterOrDigit) == "" {
|
||
|
return "", false, fmt.Errorf("undefined format layout: %v", format)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return format, false, nil
|
||
|
}
|
||
|
|
||
|
// parseDec2 parses b as an unsigned, base-10, 2-digit number.
|
||
|
// It panics if len(b) < 2. The result is undefined if digits are not base-10.
|
||
|
func parseDec2(b []byte) byte {
|
||
|
return 10*(b[0]-'0') + (b[1] - '0')
|
||
|
}
|