k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/internal/third_party/go-json-experiment/json/arshal_time.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package json 6 7 import ( 8 "errors" 9 "fmt" 10 "reflect" 11 "strings" 12 "time" 13 ) 14 15 var ( 16 timeDurationType = reflect.TypeOf((*time.Duration)(nil)).Elem() 17 timeTimeType = reflect.TypeOf((*time.Time)(nil)).Elem() 18 ) 19 20 func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler { 21 // Ideally, time types would implement MarshalerV2 and UnmarshalerV2, 22 // but that would incur a dependency on package json from package time. 23 // Given how widely used time is, it is more acceptable that we incur a 24 // dependency on time from json. 25 // 26 // Injecting the arshaling functionality like this will not be identical 27 // to actually declaring methods on the time types since embedding of the 28 // time types will not be able to forward this functionality. 29 switch t { 30 case timeDurationType: 31 fncs.nonDefault = true 32 marshalNanos := fncs.marshal 33 fncs.marshal = func(mo MarshalOptions, enc *Encoder, va addressableValue) error { 34 if mo.format != "" && mo.formatDepth == enc.tokens.depth() { 35 if mo.format == "nanos" { 36 mo.format = "" 37 return marshalNanos(mo, enc, va) 38 } else { 39 return newInvalidFormatError("marshal", t, mo.format) 40 } 41 } 42 43 td := va.Interface().(time.Duration) 44 b := enc.UnusedBuffer() 45 b = append(b, '"') 46 b = append(b, td.String()...) // never contains special characters 47 b = append(b, '"') 48 return enc.WriteValue(b) 49 } 50 unmarshalNanos := fncs.unmarshal 51 fncs.unmarshal = func(uo UnmarshalOptions, dec *Decoder, va addressableValue) error { 52 // TODO: Should there be a flag that specifies that we can unmarshal 53 // from either form since there would be no ambiguity? 54 if uo.format != "" && uo.formatDepth == dec.tokens.depth() { 55 if uo.format == "nanos" { 56 uo.format = "" 57 return unmarshalNanos(uo, dec, va) 58 } else { 59 return newInvalidFormatError("unmarshal", t, uo.format) 60 } 61 } 62 63 var flags valueFlags 64 td := va.Addr().Interface().(*time.Duration) 65 val, err := dec.readValue(&flags) 66 if err != nil { 67 return err 68 } 69 switch k := val.Kind(); k { 70 case 'n': 71 *td = time.Duration(0) 72 return nil 73 case '"': 74 val = unescapeStringMayCopy(val, flags.isVerbatim()) 75 td2, err := time.ParseDuration(string(val)) 76 if err != nil { 77 return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t, Err: err} 78 } 79 *td = td2 80 return nil 81 default: 82 return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t} 83 } 84 } 85 case timeTimeType: 86 fncs.nonDefault = true 87 fncs.marshal = func(mo MarshalOptions, enc *Encoder, va addressableValue) error { 88 format := time.RFC3339Nano 89 isRFC3339 := true 90 if mo.format != "" && mo.formatDepth == enc.tokens.depth() { 91 var err error 92 format, isRFC3339, err = checkTimeFormat(mo.format) 93 if err != nil { 94 return &SemanticError{action: "marshal", GoType: t, Err: err} 95 } 96 } 97 98 tt := va.Interface().(time.Time) 99 b := enc.UnusedBuffer() 100 b = append(b, '"') 101 b = tt.AppendFormat(b, format) 102 b = append(b, '"') 103 if isRFC3339 { 104 // Not all Go timestamps can be represented as valid RFC 3339. 105 // Explicitly check for these edge cases. 106 // See https://go.dev/issue/4556 and https://go.dev/issue/54580. 107 var err error 108 switch b := b[len(`"`) : len(b)-len(`"`)]; { 109 case b[len("9999")] != '-': // year must be exactly 4 digits wide 110 err = errors.New("year outside of range [0,9999]") 111 case b[len(b)-1] != 'Z': 112 c := b[len(b)-len("Z07:00")] 113 if ('0' <= c && c <= '9') || parseDec2(b[len(b)-len("07:00"):]) >= 24 { 114 err = errors.New("timezone hour outside of range [0,23]") 115 } 116 } 117 if err != nil { 118 return &SemanticError{action: "marshal", GoType: t, Err: err} 119 } 120 return enc.WriteValue(b) // RFC 3339 never needs JSON escaping 121 } 122 // The format may contain special characters that need escaping. 123 // Verify that the result is a valid JSON string (common case), 124 // otherwise escape the string correctly (slower case). 125 if consumeSimpleString(b) != len(b) { 126 b, _ = appendString(nil, string(b[len(`"`):len(b)-len(`"`)]), true, nil) 127 } 128 return enc.WriteValue(b) 129 } 130 fncs.unmarshal = func(uo UnmarshalOptions, dec *Decoder, va addressableValue) error { 131 format := time.RFC3339 132 isRFC3339 := true 133 if uo.format != "" && uo.formatDepth == dec.tokens.depth() { 134 var err error 135 format, isRFC3339, err = checkTimeFormat(uo.format) 136 if err != nil { 137 return &SemanticError{action: "unmarshal", GoType: t, Err: err} 138 } 139 } 140 141 var flags valueFlags 142 tt := va.Addr().Interface().(*time.Time) 143 val, err := dec.readValue(&flags) 144 if err != nil { 145 return err 146 } 147 k := val.Kind() 148 switch k { 149 case 'n': 150 *tt = time.Time{} 151 return nil 152 case '"': 153 val = unescapeStringMayCopy(val, flags.isVerbatim()) 154 tt2, err := time.Parse(format, string(val)) 155 if isRFC3339 && err == nil { 156 // TODO(https://go.dev/issue/54580): RFC 3339 specifies 157 // the exact grammar of a valid timestamp. However, 158 // the parsing functionality in "time" is too loose and 159 // incorrectly accepts invalid timestamps as valid. 160 // Remove these manual checks when "time" checks it for us. 161 newParseError := func(layout, value, layoutElem, valueElem, message string) error { 162 return &time.ParseError{Layout: layout, Value: value, LayoutElem: layoutElem, ValueElem: valueElem, Message: message} 163 } 164 switch { 165 case val[len("2006-01-02T")+1] == ':': // hour must be two digits 166 err = newParseError(format, string(val), "15", string(val[len("2006-01-02T"):][:1]), "") 167 case val[len("2006-01-02T15:04:05")] == ',': // sub-second separator must be a period 168 err = newParseError(format, string(val), ".", ",", "") 169 case val[len(val)-1] != 'Z': 170 switch { 171 case parseDec2(val[len(val)-len("07:00"):]) >= 24: // timezone hour must be in range 172 err = newParseError(format, string(val), "Z07:00", string(val[len(val)-len("Z07:00"):]), ": timezone hour out of range") 173 case parseDec2(val[len(val)-len("00"):]) >= 60: // timezone minute must be in range 174 err = newParseError(format, string(val), "Z07:00", string(val[len(val)-len("Z07:00"):]), ": timezone minute out of range") 175 } 176 } 177 } 178 if err != nil { 179 return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t, Err: err} 180 } 181 *tt = tt2 182 return nil 183 default: 184 return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t} 185 } 186 } 187 } 188 return fncs 189 } 190 191 func checkTimeFormat(format string) (string, bool, error) { 192 // We assume that an exported constant in the time package will 193 // always start with an uppercase ASCII letter. 194 if len(format) > 0 && 'A' <= format[0] && format[0] <= 'Z' { 195 switch format { 196 case "ANSIC": 197 return time.ANSIC, false, nil 198 case "UnixDate": 199 return time.UnixDate, false, nil 200 case "RubyDate": 201 return time.RubyDate, false, nil 202 case "RFC822": 203 return time.RFC822, false, nil 204 case "RFC822Z": 205 return time.RFC822Z, false, nil 206 case "RFC850": 207 return time.RFC850, false, nil 208 case "RFC1123": 209 return time.RFC1123, false, nil 210 case "RFC1123Z": 211 return time.RFC1123Z, false, nil 212 case "RFC3339": 213 return time.RFC3339, true, nil 214 case "RFC3339Nano": 215 return time.RFC3339Nano, true, nil 216 case "Kitchen": 217 return time.Kitchen, false, nil 218 case "Stamp": 219 return time.Stamp, false, nil 220 case "StampMilli": 221 return time.StampMilli, false, nil 222 case "StampMicro": 223 return time.StampMicro, false, nil 224 case "StampNano": 225 return time.StampNano, false, nil 226 default: 227 // Reject any format that is an exported Go identifier in case 228 // new format constants are added to the time package. 229 if strings.TrimFunc(format, isLetterOrDigit) == "" { 230 return "", false, fmt.Errorf("undefined format layout: %v", format) 231 } 232 } 233 } 234 return format, false, nil 235 } 236 237 // parseDec2 parses b as an unsigned, base-10, 2-digit number. 238 // It panics if len(b) < 2. The result is undefined if digits are not base-10. 239 func parseDec2(b []byte) byte { 240 return 10*(b[0]-'0') + (b[1] - '0') 241 }