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  }