Help us shape the future of Go! Take the 2026 Go Developer Survey

Source file src/encoding/json/v2/arshal_time.go

     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  //go:build goexperiment.jsonv2
     6  
     7  package json
     8  
     9  import (
    10  	"bytes"
    11  	"cmp"
    12  	"errors"
    13  	"fmt"
    14  	"math"
    15  	"math/bits"
    16  	"reflect"
    17  	"strconv"
    18  	"strings"
    19  	"time"
    20  
    21  	"encoding/json/internal"
    22  	"encoding/json/internal/jsonflags"
    23  	"encoding/json/internal/jsonopts"
    24  	"encoding/json/internal/jsonwire"
    25  	"encoding/json/jsontext"
    26  )
    27  
    28  var (
    29  	timeDurationType = reflect.TypeFor[time.Duration]()
    30  	timeTimeType     = reflect.TypeFor[time.Time]()
    31  )
    32  
    33  func makeTimeMarshaler(fncs *marshaler, t reflect.Type) *marshaler {
    34  	// Ideally, time types would implement MarshalerTo,
    35  	// but that would incur a dependency on package json from package time.
    36  	// Given how widely used time is, it is more acceptable that we incur a
    37  	// dependency on time from json.
    38  	//
    39  	// Injecting the arshaling functionality like this will not be identical
    40  	// to actually declaring methods on the time types since embedding of the
    41  	// time types will not be able to forward this functionality.
    42  	switch t {
    43  	case timeDurationType:
    44  		fncs.nonDefault = true
    45  		marshalNano := fncs.fnc
    46  		fncs.fnc = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
    47  			xe := export.Encoder(enc)
    48  			var m durationArshaler
    49  			if mo.Flags.Has(jsonflags.FormatTag) {
    50  				if !m.initFormat(mo.Format) {
    51  					return newInvalidFormatError(enc, t)
    52  				}
    53  			} else if mo.Flags.Get(jsonflags.FormatDurationAsNano) {
    54  				return marshalNano(enc, va, mo)
    55  			} else {
    56  				// TODO(https://go.dev/issue/71631): Decide on default duration representation.
    57  				var workaround string
    58  				if mo.Flags.Get(jsonflags.FormatTagSupported) {
    59  					workaround = "; specify an explicit format"
    60  				}
    61  				return newMarshalErrorBefore(enc, t, errors.New("no default representation"+workaround))
    62  			}
    63  			if mo.Flags.Get(jsonflags.StringTag) && !m.isNumeric() && !mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
    64  				return newMarshalErrorBefore(enc, t, errInvalidStringTag)
    65  			}
    66  
    67  			m.td, _ = reflect.TypeAssert[time.Duration](va.Value)
    68  			k := stringOrNumberKind(!m.isNumeric() || xe.Tokens.Last.NeedObjectName() || mo.Flags.Get(jsonflags.StringifyNumbers|jsonflags.StringTag))
    69  			if err := xe.AppendRaw(k, true, m.appendMarshal); err != nil {
    70  				if !isSyntacticError(err) && !export.IsIOError(err) {
    71  					err = newMarshalErrorBefore(enc, t, err)
    72  				}
    73  				return err
    74  			}
    75  			return nil
    76  		}
    77  	case timeTimeType:
    78  		fncs.nonDefault = true
    79  		fncs.fnc = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) (err error) {
    80  			xe := export.Encoder(enc)
    81  			var m timeArshaler
    82  			if mo.Flags.Has(jsonflags.FormatTag) {
    83  				if !m.initFormat(mo.Format) {
    84  					return newInvalidFormatError(enc, t)
    85  				}
    86  			}
    87  			if mo.Flags.Get(jsonflags.StringTag) && !m.isNumeric() && !mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
    88  				return newMarshalErrorBefore(enc, t, errInvalidStringTag)
    89  			}
    90  
    91  			m.tt, _ = reflect.TypeAssert[time.Time](va.Value)
    92  			k := stringOrNumberKind(!m.isNumeric() || xe.Tokens.Last.NeedObjectName() || mo.Flags.Get(jsonflags.StringifyNumbers|jsonflags.StringTag))
    93  			if err := xe.AppendRaw(k, !m.hasCustomFormat(), m.appendMarshal); err != nil {
    94  				if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
    95  					return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalJSON") // unlike unmarshal, always wrapped
    96  				}
    97  				if !isSyntacticError(err) && !export.IsIOError(err) {
    98  					err = newMarshalErrorBefore(enc, t, err)
    99  				}
   100  				return err
   101  			}
   102  			return nil
   103  		}
   104  	}
   105  	return fncs
   106  }
   107  
   108  func makeTimeUnmarshaler(fncs *unmarshaler, t reflect.Type) *unmarshaler {
   109  	// Ideally, time types would implement UnmarshalerFrom,
   110  	// but that would incur a dependency on package json from package time.
   111  	// Given how widely used time is, it is more acceptable that we incur a
   112  	// dependency on time from json.
   113  	//
   114  	// Injecting the arshaling functionality like this will not be identical
   115  	// to actually declaring methods on the time types since embedding of the
   116  	// time types will not be able to forward this functionality.
   117  	switch t {
   118  	case timeDurationType:
   119  		fncs.nonDefault = true
   120  		unmarshalNano := fncs.fnc
   121  		fncs.fnc = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
   122  			xd := export.Decoder(dec)
   123  			var u durationArshaler
   124  			if uo.Flags.Has(jsonflags.FormatTag) {
   125  				if !u.initFormat(uo.Format) {
   126  					return newInvalidFormatError(dec, t)
   127  				}
   128  			} else if uo.Flags.Get(jsonflags.FormatDurationAsNano) {
   129  				return unmarshalNano(dec, va, uo)
   130  			} else {
   131  				// TODO(https://go.dev/issue/71631): Decide on default duration representation.
   132  				var workaround string
   133  				if uo.Flags.Get(jsonflags.FormatTagSupported) {
   134  					workaround = "; specify an explicit format"
   135  				}
   136  				return newUnmarshalErrorBeforeWithSkipping(dec, t, errors.New("no default representation"+workaround))
   137  			}
   138  			if uo.Flags.Get(jsonflags.StringTag) && !u.isNumeric() && !uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
   139  				return newUnmarshalErrorBeforeWithSkipping(dec, t, errInvalidStringTag)
   140  			}
   141  
   142  			stringify := !u.isNumeric() || xd.Tokens.Last.NeedObjectName() || uo.Flags.Get(jsonflags.StringifyNumbers|jsonflags.StringTag)
   143  			var flags jsonwire.ValueFlags
   144  			td, _ := reflect.TypeAssert[*time.Duration](va.Addr())
   145  			val, err := xd.ReadValue(&flags)
   146  			if err != nil {
   147  				return err
   148  			}
   149  			switch k := val.Kind(); k {
   150  			case 'n':
   151  				if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
   152  					*td = time.Duration(0)
   153  				}
   154  				return nil
   155  			case '"':
   156  				if !stringify {
   157  					break
   158  				}
   159  				val = jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
   160  				if err := u.unmarshal(val); err != nil {
   161  					return newUnmarshalErrorAfter(dec, t, err)
   162  				}
   163  				*td = u.td
   164  				return nil
   165  			case '0':
   166  				if stringify {
   167  					break
   168  				}
   169  				if err := u.unmarshal(val); err != nil {
   170  					return newUnmarshalErrorAfter(dec, t, err)
   171  				}
   172  				*td = u.td
   173  				return nil
   174  			}
   175  			return newUnmarshalErrorAfter(dec, t, nil)
   176  		}
   177  	case timeTimeType:
   178  		fncs.nonDefault = true
   179  		fncs.fnc = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) (err error) {
   180  			xd := export.Decoder(dec)
   181  			var u timeArshaler
   182  			if uo.Flags.Has(jsonflags.FormatTag) {
   183  				if !u.initFormat(uo.Format) {
   184  					return newInvalidFormatError(dec, t)
   185  				}
   186  			} else if uo.Flags.Get(jsonflags.ParseTimeWithLooseRFC3339) {
   187  				u.looseRFC3339 = true
   188  			}
   189  			if uo.Flags.Get(jsonflags.StringTag) && !u.isNumeric() && !uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
   190  				return newUnmarshalErrorBeforeWithSkipping(dec, t, errInvalidStringTag)
   191  			}
   192  
   193  			stringify := !u.isNumeric() || xd.Tokens.Last.NeedObjectName() || uo.Flags.Get(jsonflags.StringifyNumbers|jsonflags.StringTag)
   194  			var flags jsonwire.ValueFlags
   195  			tt, _ := reflect.TypeAssert[*time.Time](va.Addr())
   196  			val, err := xd.ReadValue(&flags)
   197  			if err != nil {
   198  				return err
   199  			}
   200  			switch k := val.Kind(); k {
   201  			case 'n':
   202  				if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
   203  					*tt = time.Time{}
   204  				}
   205  				return nil
   206  			case '"':
   207  				if !stringify {
   208  					break
   209  				}
   210  				val = jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
   211  				if err := u.unmarshal(val); err != nil {
   212  					if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
   213  						return err // unlike marshal, never wrapped
   214  					}
   215  					return newUnmarshalErrorAfter(dec, t, err)
   216  				}
   217  				*tt = u.tt
   218  				return nil
   219  			case '0':
   220  				if stringify {
   221  					break
   222  				}
   223  				if err := u.unmarshal(val); err != nil {
   224  					if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
   225  						return err // unlike marshal, never wrapped
   226  					}
   227  					return newUnmarshalErrorAfter(dec, t, err)
   228  				}
   229  				*tt = u.tt
   230  				return nil
   231  			}
   232  			return newUnmarshalErrorAfter(dec, t, nil)
   233  		}
   234  	}
   235  	return fncs
   236  }
   237  
   238  type durationArshaler struct {
   239  	td time.Duration
   240  
   241  	// base records the representation where:
   242  	//   - 0 uses time.Duration.String
   243  	//   - 1e0, 1e3, 1e6, or 1e9 use a decimal encoding of the duration as
   244  	//     nanoseconds, microseconds, milliseconds, or seconds.
   245  	//   - 8601 uses ISO 8601
   246  	base uint64
   247  }
   248  
   249  func (a *durationArshaler) initFormat(format string) (ok bool) {
   250  	switch format {
   251  	case "units":
   252  		a.base = 0
   253  	case "sec":
   254  		a.base = 1e9
   255  	case "milli":
   256  		a.base = 1e6
   257  	case "micro":
   258  		a.base = 1e3
   259  	case "nano":
   260  		a.base = 1e0
   261  	case "iso8601":
   262  		a.base = 8601
   263  	default:
   264  		return false
   265  	}
   266  	return true
   267  }
   268  
   269  func (a *durationArshaler) isNumeric() bool {
   270  	return a.base != 0 && a.base != 8601
   271  }
   272  
   273  func (a *durationArshaler) appendMarshal(b []byte) ([]byte, error) {
   274  	switch a.base {
   275  	case 0:
   276  		return append(b, a.td.String()...), nil
   277  	case 8601:
   278  		return appendDurationISO8601(b, a.td), nil
   279  	default:
   280  		return appendDurationBase10(b, a.td, a.base), nil
   281  	}
   282  }
   283  
   284  func (a *durationArshaler) unmarshal(b []byte) (err error) {
   285  	switch a.base {
   286  	case 0:
   287  		a.td, err = time.ParseDuration(string(b))
   288  	case 8601:
   289  		a.td, err = parseDurationISO8601(b)
   290  	default:
   291  		a.td, err = parseDurationBase10(b, a.base)
   292  	}
   293  	return err
   294  }
   295  
   296  type timeArshaler struct {
   297  	tt time.Time
   298  
   299  	// base records the representation where:
   300  	//   - 0 uses RFC 3339 encoding of the timestamp
   301  	//   - 1e0, 1e3, 1e6, or 1e9 use a decimal encoding of the timestamp as
   302  	//     seconds, milliseconds, microseconds, or nanoseconds since Unix epoch.
   303  	//   - math.MaxUint uses time.Time.Format to encode the timestamp
   304  	base   uint64
   305  	format string // time format passed to time.Parse
   306  
   307  	looseRFC3339 bool
   308  }
   309  
   310  func (a *timeArshaler) initFormat(format string) bool {
   311  	// We assume that an exported constant in the time package will
   312  	// always start with an uppercase ASCII letter.
   313  	if len(format) == 0 {
   314  		return false
   315  	}
   316  	a.base = math.MaxUint // implies custom format
   317  	if c := format[0]; !('a' <= c && c <= 'z') && !('A' <= c && c <= 'Z') {
   318  		a.format = format
   319  		return true
   320  	}
   321  	switch format {
   322  	case "ANSIC":
   323  		a.format = time.ANSIC
   324  	case "UnixDate":
   325  		a.format = time.UnixDate
   326  	case "RubyDate":
   327  		a.format = time.RubyDate
   328  	case "RFC822":
   329  		a.format = time.RFC822
   330  	case "RFC822Z":
   331  		a.format = time.RFC822Z
   332  	case "RFC850":
   333  		a.format = time.RFC850
   334  	case "RFC1123":
   335  		a.format = time.RFC1123
   336  	case "RFC1123Z":
   337  		a.format = time.RFC1123Z
   338  	case "RFC3339":
   339  		a.base = 0
   340  		a.format = time.RFC3339
   341  	case "RFC3339Nano":
   342  		a.base = 0
   343  		a.format = time.RFC3339Nano
   344  	case "Kitchen":
   345  		a.format = time.Kitchen
   346  	case "Stamp":
   347  		a.format = time.Stamp
   348  	case "StampMilli":
   349  		a.format = time.StampMilli
   350  	case "StampMicro":
   351  		a.format = time.StampMicro
   352  	case "StampNano":
   353  		a.format = time.StampNano
   354  	case "DateTime":
   355  		a.format = time.DateTime
   356  	case "DateOnly":
   357  		a.format = time.DateOnly
   358  	case "TimeOnly":
   359  		a.format = time.TimeOnly
   360  	case "unix":
   361  		a.base = 1e0
   362  	case "unixmilli":
   363  		a.base = 1e3
   364  	case "unixmicro":
   365  		a.base = 1e6
   366  	case "unixnano":
   367  		a.base = 1e9
   368  	default:
   369  		// Reject any Go identifier in case new constants are supported.
   370  		if strings.TrimFunc(format, isLetterOrDigit) == "" {
   371  			return false
   372  		}
   373  		a.format = format
   374  	}
   375  	return true
   376  }
   377  
   378  func (a *timeArshaler) isNumeric() bool {
   379  	return int(a.base) > 0
   380  }
   381  
   382  func (a *timeArshaler) hasCustomFormat() bool {
   383  	return a.base == math.MaxUint
   384  }
   385  
   386  func (a *timeArshaler) appendMarshal(b []byte) ([]byte, error) {
   387  	switch a.base {
   388  	case 0:
   389  		format := cmp.Or(a.format, time.RFC3339Nano)
   390  		n0 := len(b)
   391  		b = a.tt.AppendFormat(b, format)
   392  		// Not all Go timestamps can be represented as valid RFC 3339.
   393  		// Explicitly check for these edge cases.
   394  		// See https://go.dev/issue/4556 and https://go.dev/issue/54580.
   395  		switch b := b[n0:]; {
   396  		case b[len("9999")] != '-': // year must be exactly 4 digits wide
   397  			return b, errors.New("year outside of range [0,9999]")
   398  		case b[len(b)-1] != 'Z':
   399  			c := b[len(b)-len("Z07:00")]
   400  			if ('0' <= c && c <= '9') || parseDec2(b[len(b)-len("07:00"):]) >= 24 {
   401  				return b, errors.New("timezone hour outside of range [0,23]")
   402  			}
   403  		}
   404  		return b, nil
   405  	case math.MaxUint:
   406  		return a.tt.AppendFormat(b, a.format), nil
   407  	default:
   408  		return appendTimeUnix(b, a.tt, a.base), nil
   409  	}
   410  }
   411  
   412  func (a *timeArshaler) unmarshal(b []byte) (err error) {
   413  	switch a.base {
   414  	case 0:
   415  		// Use time.Time.UnmarshalText to avoid possible string allocation.
   416  		if err := a.tt.UnmarshalText(b); err != nil {
   417  			return err
   418  		}
   419  		// TODO(https://go.dev/issue/57912):
   420  		// RFC 3339 specifies the grammar for a valid timestamp.
   421  		// However, the parsing functionality in "time" is too loose and
   422  		// incorrectly accepts invalid timestamps as valid.
   423  		// Remove these manual checks when "time" checks it for us.
   424  		newParseError := func(layout, value, layoutElem, valueElem, message string) error {
   425  			return &time.ParseError{Layout: layout, Value: value, LayoutElem: layoutElem, ValueElem: valueElem, Message: message}
   426  		}
   427  		switch {
   428  		case a.looseRFC3339:
   429  			return nil
   430  		case b[len("2006-01-02T")+1] == ':': // hour must be two digits
   431  			return newParseError(time.RFC3339, string(b), "15", string(b[len("2006-01-02T"):][:1]), "")
   432  		case b[len("2006-01-02T15:04:05")] == ',': // sub-second separator must be a period
   433  			return newParseError(time.RFC3339, string(b), ".", ",", "")
   434  		case b[len(b)-1] != 'Z':
   435  			switch {
   436  			case parseDec2(b[len(b)-len("07:00"):]) >= 24: // timezone hour must be in range
   437  				return newParseError(time.RFC3339, string(b), "Z07:00", string(b[len(b)-len("Z07:00"):]), ": timezone hour out of range")
   438  			case parseDec2(b[len(b)-len("00"):]) >= 60: // timezone minute must be in range
   439  				return newParseError(time.RFC3339, string(b), "Z07:00", string(b[len(b)-len("Z07:00"):]), ": timezone minute out of range")
   440  			}
   441  		}
   442  		return nil
   443  	case math.MaxUint:
   444  		a.tt, err = time.Parse(a.format, string(b))
   445  		return err
   446  	default:
   447  		a.tt, err = parseTimeUnix(b, a.base)
   448  		return err
   449  	}
   450  }
   451  
   452  // appendDurationBase10 appends d formatted as a decimal fractional number,
   453  // where pow10 is a power-of-10 used to scale down the number.
   454  func appendDurationBase10(b []byte, d time.Duration, pow10 uint64) []byte {
   455  	b, n := mayAppendDurationSign(b, d)            // append sign
   456  	whole, frac := bits.Div64(0, n, uint64(pow10)) // compute whole and frac fields
   457  	b = strconv.AppendUint(b, whole, 10)           // append whole field
   458  	return appendFracBase10(b, frac, pow10)        // append frac field
   459  }
   460  
   461  // parseDurationBase10 parses d from a decimal fractional number,
   462  // where pow10 is a power-of-10 used to scale up the number.
   463  func parseDurationBase10(b []byte, pow10 uint64) (time.Duration, error) {
   464  	suffix, neg := consumeSign(b, false)                     // consume sign
   465  	wholeBytes, fracBytes := bytesCutByte(suffix, '.', true) // consume whole and frac fields
   466  	whole, okWhole := jsonwire.ParseUint(wholeBytes)         // parse whole field; may overflow
   467  	frac, okFrac := parseFracBase10(fracBytes, pow10)        // parse frac field
   468  	hi, lo := bits.Mul64(whole, uint64(pow10))               // overflow if hi > 0
   469  	sum, co := bits.Add64(lo, uint64(frac), 0)               // overflow if co > 0
   470  	switch d := mayApplyDurationSign(sum, neg); {            // overflow if neg != (d < 0)
   471  	case (!okWhole && whole != math.MaxUint64) || !okFrac:
   472  		return 0, fmt.Errorf("invalid duration %q: %w", b, strconv.ErrSyntax)
   473  	case !okWhole || hi > 0 || co > 0 || neg != (d < 0):
   474  		return 0, fmt.Errorf("invalid duration %q: %w", b, strconv.ErrRange)
   475  	default:
   476  		return d, nil
   477  	}
   478  }
   479  
   480  // appendDurationISO8601 appends an ISO 8601 duration with a restricted grammar,
   481  // where leading and trailing zeroes and zero-value designators are omitted.
   482  // It only uses hour, minute, and second designators since ISO 8601 defines
   483  // those as being "accurate", while year, month, week, and day are "nominal".
   484  func appendDurationISO8601(b []byte, d time.Duration) []byte {
   485  	if d == 0 {
   486  		return append(b, "PT0S"...)
   487  	}
   488  	b, n := mayAppendDurationSign(b, d)
   489  	b = append(b, "PT"...)
   490  	n, nsec := bits.Div64(0, n, 1e9)  // compute nsec field
   491  	n, sec := bits.Div64(0, n, 60)    // compute sec field
   492  	hour, min := bits.Div64(0, n, 60) // compute hour and min fields
   493  	if hour > 0 {
   494  		b = append(strconv.AppendUint(b, hour, 10), 'H')
   495  	}
   496  	if min > 0 {
   497  		b = append(strconv.AppendUint(b, min, 10), 'M')
   498  	}
   499  	if sec > 0 || nsec > 0 {
   500  		b = append(appendFracBase10(strconv.AppendUint(b, sec, 10), nsec, 1e9), 'S')
   501  	}
   502  	return b
   503  }
   504  
   505  // daysPerYear is the exact average number of days in a year according to
   506  // the Gregorian calendar, which has an extra day each year that is
   507  // a multiple of 4, unless it is evenly divisible by 100 but not by 400.
   508  // This does not take into account leap seconds, which are not deterministic.
   509  const daysPerYear = 365.2425
   510  
   511  var errInaccurateDateUnits = errors.New("inaccurate year, month, week, or day units")
   512  
   513  // parseDurationISO8601 parses a duration according to ISO 8601-1:2019,
   514  // section 5.5.2.2 and 5.5.2.3 with the following restrictions or extensions:
   515  //
   516  //   - A leading minus sign is permitted for negative duration according
   517  //     to ISO 8601-2:2019, section 4.4.1.9. We do not permit negative values
   518  //     for each "time scale component", which is permitted by section 4.4.1.1,
   519  //     but rarely supported by parsers.
   520  //
   521  //   - A leading plus sign is permitted (and ignored).
   522  //     This is not required by ISO 8601, but not forbidden either.
   523  //     There is some precedent for this as it is supported by the principle of
   524  //     duration arithmetic as specified in ISO 8601-2-2019, section 14.1.
   525  //     Of note, the JavaScript grammar for ISO 8601 permits a leading plus sign.
   526  //
   527  //   - A fractional value is only permitted for accurate units
   528  //     (i.e., hour, minute, and seconds) in the last time component,
   529  //     which is permissible by ISO 8601-1:2019, section 5.5.2.3.
   530  //
   531  //   - Both periods ('.') and commas (',') are supported as the separator
   532  //     between the integer part and fraction part of a number,
   533  //     as specified in ISO 8601-1:2019, section 3.2.6.
   534  //     While ISO 8601 recommends comma as the default separator,
   535  //     most formatters use a period.
   536  //
   537  //   - Leading zeros are ignored. This is not required by ISO 8601,
   538  //     but also not forbidden by the standard. Many parsers support this.
   539  //
   540  //   - Lowercase designators are supported. This is not required by ISO 8601,
   541  //     but also not forbidden by the standard. Many parsers support this.
   542  //
   543  // If the nominal units of year, month, week, or day are present,
   544  // this produces a best-effort value and also reports [errInaccurateDateUnits].
   545  //
   546  // The accepted grammar is identical to JavaScript's Duration:
   547  //
   548  //	https://tc39.es/proposal-temporal/#prod-Duration
   549  //
   550  // We follow JavaScript's grammar as JSON itself is derived from JavaScript.
   551  // The Temporal.Duration.toJSON method is guaranteed to produce an output
   552  // that can be parsed by this function so long as arithmetic in JavaScript
   553  // does not use a largestUnit value higher than "hours" (which is the default).
   554  // Even if it does, this will do a best-effort parsing with inaccurate units,
   555  // but report [errInaccurateDateUnits].
   556  func parseDurationISO8601(b []byte) (time.Duration, error) {
   557  	var invalid, overflow, inaccurate, sawFrac bool
   558  	var sumNanos, n, co uint64
   559  
   560  	// cutBytes is like [bytes.Cut], but uses either c0 or c1 as the separator.
   561  	cutBytes := func(b []byte, c0, c1 byte) (prefix, suffix []byte, ok bool) {
   562  		for i, c := range b {
   563  			if c == c0 || c == c1 {
   564  				return b[:i], b[i+1:], true
   565  			}
   566  		}
   567  		return b, nil, false
   568  	}
   569  
   570  	// mayParseUnit attempts to parse another date or time number
   571  	// identified by the desHi and desLo unit characters.
   572  	// If the part is absent for current unit, it returns b as is.
   573  	mayParseUnit := func(b []byte, desHi, desLo byte, unit time.Duration) []byte {
   574  		number, suffix, ok := cutBytes(b, desHi, desLo)
   575  		if !ok || sawFrac {
   576  			return b // designator is not present or already saw fraction, which can only be in the last component
   577  		}
   578  
   579  		// Parse the number.
   580  		// A fraction is allowed for the accurate units in the last part.
   581  		whole, frac, ok := cutBytes(number, '.', ',')
   582  		if ok {
   583  			sawFrac = true
   584  			invalid = invalid || len(frac) == len("") || unit > time.Hour
   585  			if unit == time.Second {
   586  				n, ok = parsePaddedBase10(frac, uint64(time.Second))
   587  				invalid = invalid || !ok
   588  			} else {
   589  				f, err := strconv.ParseFloat("0."+string(frac), 64)
   590  				invalid = invalid || err != nil || len(bytes.Trim(frac[len("."):], "0123456789")) > 0
   591  				n = uint64(math.Round(f * float64(unit))) // never overflows since f is within [0..1]
   592  			}
   593  			sumNanos, co = bits.Add64(sumNanos, n, 0) // overflow if co > 0
   594  			overflow = overflow || co > 0
   595  		}
   596  		for len(whole) > 1 && whole[0] == '0' {
   597  			whole = whole[len("0"):] // trim leading zeros
   598  		}
   599  		n, ok := jsonwire.ParseUint(whole)         // overflow if !ok && MaxUint64
   600  		hi, lo := bits.Mul64(n, uint64(unit))      // overflow if hi > 0
   601  		sumNanos, co = bits.Add64(sumNanos, lo, 0) // overflow if co > 0
   602  		invalid = invalid || (!ok && n != math.MaxUint64)
   603  		overflow = overflow || (!ok && n == math.MaxUint64) || hi > 0 || co > 0
   604  		inaccurate = inaccurate || unit > time.Hour
   605  		return suffix
   606  	}
   607  
   608  	suffix, neg := consumeSign(b, true)
   609  	prefix, suffix, okP := cutBytes(suffix, 'P', 'p')
   610  	durDate, durTime, okT := cutBytes(suffix, 'T', 't')
   611  	invalid = invalid || len(prefix) > 0 || !okP || (okT && len(durTime) == 0) || len(durDate)+len(durTime) == 0
   612  	if len(durDate) > 0 { // nominal portion of the duration
   613  		durDate = mayParseUnit(durDate, 'Y', 'y', time.Duration(daysPerYear*24*60*60*1e9))
   614  		durDate = mayParseUnit(durDate, 'M', 'm', time.Duration(daysPerYear/12*24*60*60*1e9))
   615  		durDate = mayParseUnit(durDate, 'W', 'w', time.Duration(7*24*60*60*1e9))
   616  		durDate = mayParseUnit(durDate, 'D', 'd', time.Duration(24*60*60*1e9))
   617  		invalid = invalid || len(durDate) > 0 // unknown elements
   618  	}
   619  	if len(durTime) > 0 { // accurate portion of the duration
   620  		durTime = mayParseUnit(durTime, 'H', 'h', time.Duration(60*60*1e9))
   621  		durTime = mayParseUnit(durTime, 'M', 'm', time.Duration(60*1e9))
   622  		durTime = mayParseUnit(durTime, 'S', 's', time.Duration(1e9))
   623  		invalid = invalid || len(durTime) > 0 // unknown elements
   624  	}
   625  	d := mayApplyDurationSign(sumNanos, neg)
   626  	overflow = overflow || (neg != (d < 0) && d != 0) // overflows signed duration
   627  
   628  	switch {
   629  	case invalid:
   630  		return 0, fmt.Errorf("invalid ISO 8601 duration %q: %w", b, strconv.ErrSyntax)
   631  	case overflow:
   632  		return 0, fmt.Errorf("invalid ISO 8601 duration %q: %w", b, strconv.ErrRange)
   633  	case inaccurate:
   634  		return d, fmt.Errorf("invalid ISO 8601 duration %q: %w", b, errInaccurateDateUnits)
   635  	default:
   636  		return d, nil
   637  	}
   638  }
   639  
   640  // mayAppendDurationSign appends a negative sign if n is negative.
   641  func mayAppendDurationSign(b []byte, d time.Duration) ([]byte, uint64) {
   642  	if d < 0 {
   643  		b = append(b, '-')
   644  		d *= -1
   645  	}
   646  	return b, uint64(d)
   647  }
   648  
   649  // mayApplyDurationSign inverts n if neg is specified.
   650  func mayApplyDurationSign(n uint64, neg bool) time.Duration {
   651  	if neg {
   652  		return -1 * time.Duration(n)
   653  	} else {
   654  		return +1 * time.Duration(n)
   655  	}
   656  }
   657  
   658  // appendTimeUnix appends t formatted as a decimal fractional number,
   659  // where pow10 is a power-of-10 used to scale up the number.
   660  func appendTimeUnix(b []byte, t time.Time, pow10 uint64) []byte {
   661  	sec, nsec := t.Unix(), int64(t.Nanosecond())
   662  	if sec < 0 {
   663  		b = append(b, '-')
   664  		sec, nsec = negateSecNano(sec, nsec)
   665  	}
   666  	switch {
   667  	case pow10 == 1e0: // fast case where units is in seconds
   668  		b = strconv.AppendUint(b, uint64(sec), 10)
   669  		return appendFracBase10(b, uint64(nsec), 1e9)
   670  	case uint64(sec) < 1e9: // intermediate case where units is not seconds, but no overflow
   671  		b = strconv.AppendUint(b, uint64(sec)*uint64(pow10)+uint64(uint64(nsec)/(1e9/pow10)), 10)
   672  		return appendFracBase10(b, (uint64(nsec)*pow10)%1e9, 1e9)
   673  	default: // slow case where units is not seconds and overflow would occur
   674  		b = strconv.AppendUint(b, uint64(sec), 10)
   675  		b = appendPaddedBase10(b, uint64(nsec)/(1e9/pow10), pow10)
   676  		return appendFracBase10(b, (uint64(nsec)*pow10)%1e9, 1e9)
   677  	}
   678  }
   679  
   680  // parseTimeUnix parses t formatted as a decimal fractional number,
   681  // where pow10 is a power-of-10 used to scale down the number.
   682  func parseTimeUnix(b []byte, pow10 uint64) (time.Time, error) {
   683  	suffix, neg := consumeSign(b, false)                     // consume sign
   684  	wholeBytes, fracBytes := bytesCutByte(suffix, '.', true) // consume whole and frac fields
   685  	whole, okWhole := jsonwire.ParseUint(wholeBytes)         // parse whole field; may overflow
   686  	frac, okFrac := parseFracBase10(fracBytes, 1e9/pow10)    // parse frac field
   687  	var sec, nsec int64
   688  	switch {
   689  	case pow10 == 1e0: // fast case where units is in seconds
   690  		sec = int64(whole) // check overflow later after negation
   691  		nsec = int64(frac) // cannot overflow
   692  	case okWhole: // intermediate case where units is not seconds, but no overflow
   693  		sec = int64(whole / pow10)                     // check overflow later after negation
   694  		nsec = int64((whole%pow10)*(1e9/pow10) + frac) // cannot overflow
   695  	case !okWhole && whole == math.MaxUint64: // slow case where units is not seconds and overflow occurred
   696  		width := int(math.Log10(float64(pow10)))                                // compute len(strconv.Itoa(pow10-1))
   697  		whole, okWhole = jsonwire.ParseUint(wholeBytes[:len(wholeBytes)-width]) // parse the upper whole field
   698  		mid, _ := parsePaddedBase10(wholeBytes[len(wholeBytes)-width:], pow10)  // parse the lower whole field
   699  		sec = int64(whole)                                                      // check overflow later after negation
   700  		nsec = int64(mid*(1e9/pow10) + frac)                                    // cannot overflow
   701  	}
   702  	if neg {
   703  		sec, nsec = negateSecNano(sec, nsec)
   704  	}
   705  	switch t := time.Unix(sec, nsec).UTC(); {
   706  	case (!okWhole && whole != math.MaxUint64) || !okFrac:
   707  		return time.Time{}, fmt.Errorf("invalid time %q: %w", b, strconv.ErrSyntax)
   708  	case !okWhole || neg != (t.Unix() < 0):
   709  		return time.Time{}, fmt.Errorf("invalid time %q: %w", b, strconv.ErrRange)
   710  	default:
   711  		return t, nil
   712  	}
   713  }
   714  
   715  // negateSecNano negates a Unix timestamp, where nsec must be within [0, 1e9).
   716  func negateSecNano(sec, nsec int64) (int64, int64) {
   717  	sec = ^sec               // twos-complement negation (i.e., -1*sec + 1)
   718  	nsec = -nsec + 1e9       // negate nsec and add 1e9 (which is the extra +1 from sec negation)
   719  	sec += int64(nsec / 1e9) // handle possible overflow of nsec if it started as zero
   720  	nsec %= 1e9              // ensure nsec stays within [0, 1e9)
   721  	return sec, nsec
   722  }
   723  
   724  // appendFracBase10 appends the fraction of n/max10,
   725  // where max10 is a power-of-10 that is larger than n.
   726  func appendFracBase10(b []byte, n, max10 uint64) []byte {
   727  	if n == 0 {
   728  		return b
   729  	}
   730  	return bytes.TrimRight(appendPaddedBase10(append(b, '.'), n, max10), "0")
   731  }
   732  
   733  // parseFracBase10 parses the fraction of n/max10,
   734  // where max10 is a power-of-10 that is larger than n.
   735  func parseFracBase10(b []byte, max10 uint64) (n uint64, ok bool) {
   736  	switch {
   737  	case len(b) == 0:
   738  		return 0, true
   739  	case len(b) < len(".0") || b[0] != '.':
   740  		return 0, false
   741  	}
   742  	return parsePaddedBase10(b[len("."):], max10)
   743  }
   744  
   745  // appendPaddedBase10 appends a zero-padded encoding of n,
   746  // where max10 is a power-of-10 that is larger than n.
   747  func appendPaddedBase10(b []byte, n, max10 uint64) []byte {
   748  	if n < max10/10 {
   749  		// Formatting of n is shorter than log10(max10),
   750  		// so add max10/10 to ensure the length is equal to log10(max10).
   751  		i := len(b)
   752  		b = strconv.AppendUint(b, n+max10/10, 10)
   753  		b[i]-- // subtract the addition of max10/10
   754  		return b
   755  	}
   756  	return strconv.AppendUint(b, n, 10)
   757  }
   758  
   759  // parsePaddedBase10 parses b as the zero-padded encoding of n,
   760  // where max10 is a power-of-10 that is larger than n.
   761  // Truncated suffix is treated as implicit zeros.
   762  // Extended suffix is ignored, but verified to contain only digits.
   763  func parsePaddedBase10(b []byte, max10 uint64) (n uint64, ok bool) {
   764  	pow10 := uint64(1)
   765  	for pow10 < max10 {
   766  		n *= 10
   767  		if len(b) > 0 {
   768  			if b[0] < '0' || '9' < b[0] {
   769  				return n, false
   770  			}
   771  			n += uint64(b[0] - '0')
   772  			b = b[1:]
   773  		}
   774  		pow10 *= 10
   775  	}
   776  	if len(b) > 0 && len(bytes.TrimRight(b, "0123456789")) > 0 {
   777  		return n, false // trailing characters are not digits
   778  	}
   779  	return n, true
   780  }
   781  
   782  // consumeSign consumes an optional leading negative or positive sign.
   783  func consumeSign(b []byte, allowPlus bool) ([]byte, bool) {
   784  	if len(b) > 0 {
   785  		if b[0] == '-' {
   786  			return b[len("-"):], true
   787  		} else if b[0] == '+' && allowPlus {
   788  			return b[len("+"):], false
   789  		}
   790  	}
   791  	return b, false
   792  }
   793  
   794  // bytesCutByte is similar to bytes.Cut(b, []byte{c}),
   795  // except c may optionally be included as part of the suffix.
   796  func bytesCutByte(b []byte, c byte, include bool) ([]byte, []byte) {
   797  	if i := bytes.IndexByte(b, c); i >= 0 {
   798  		if include {
   799  			return b[:i], b[i:]
   800  		}
   801  		return b[:i], b[i+1:]
   802  	}
   803  	return b, nil
   804  }
   805  
   806  // parseDec2 parses b as an unsigned, base-10, 2-digit number.
   807  // The result is undefined if digits are not base-10.
   808  func parseDec2(b []byte) byte {
   809  	if len(b) < 2 {
   810  		return 0
   811  	}
   812  	return 10*(b[0]-'0') + (b[1] - '0')
   813  }
   814  

View as plain text