// 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.

//go:build goexperiment.jsonv2

package json

import (
	
	
	
	
	
	
	
	
	
	

	
	
	
	
	
)

var (
	timeDurationType = reflect.TypeFor[time.Duration]()
	timeTimeType     = reflect.TypeFor[time.Time]()
)

func makeTimeArshaler( *arshaler,  reflect.Type) *arshaler {
	// Ideally, time types would implement MarshalerTo and UnmarshalerFrom,
	// 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  {
	case timeDurationType:
		.nonDefault = true
		 := .marshal
		.marshal = func( *jsontext.Encoder,  addressableValue,  *jsonopts.Struct) error {
			 := export.Encoder()
			var  durationArshaler
			if .Format != "" && .FormatDepth == .Tokens.Depth() {
				if !.initFormat(.Format) {
					return newInvalidFormatError(, , )
				}
			} else if .Flags.Get(jsonflags.FormatTimeWithLegacySemantics) {
				return (, , )
			}

			// TODO(https://go.dev/issue/62121): Use reflect.Value.AssertTo.
			.td = *.Addr().Interface().(*time.Duration)
			 := stringOrNumberKind(!.isNumeric() || .Tokens.Last.NeedObjectName() || .Flags.Get(jsonflags.StringifyNumbers))
			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 .Format != "" && .FormatDepth == .Tokens.Depth() {
				if !.initFormat(.Format) {
					return newInvalidFormatError(, , )
				}
			} else if .Flags.Get(jsonflags.FormatTimeWithLegacySemantics) {
				return (, , )
			}

			 := !.isNumeric() || .Tokens.Last.NeedObjectName() || .Flags.Get(jsonflags.StringifyNumbers)
			var  jsonwire.ValueFlags
			 := .Addr().Interface().(*time.Duration)
			,  := .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 .Format != "" && .FormatDepth == .Tokens.Depth() {
				if !.initFormat(.Format) {
					return newInvalidFormatError(, , )
				}
			}

			// TODO(https://go.dev/issue/62121): Use reflect.Value.AssertTo.
			.tt = *.Addr().Interface().(*time.Time)
			 := stringOrNumberKind(!.isNumeric() || .Tokens.Last.NeedObjectName() || .Flags.Get(jsonflags.StringifyNumbers))
			if  := .AppendRaw(, !.hasCustomFormat(), .appendMarshal);  != nil {
				if .Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
					return internal.NewMarshalerError(.Addr().Interface(), , "MarshalJSON") // unlike unmarshal, always wrapped
				}
				if !isSyntacticError() && !export.IsIOError() {
					 = newMarshalErrorBefore(, , )
				}
				return 
			}
			return nil
		}
		.unmarshal = func( *jsontext.Decoder,  addressableValue,  *jsonopts.Struct) ( error) {
			 := export.Decoder()
			var  timeArshaler
			if .Format != "" && .FormatDepth == .Tokens.Depth() {
				if !.initFormat(.Format) {
					return newInvalidFormatError(, , )
				}
			} else if .Flags.Get(jsonflags.FormatTimeWithLegacySemantics) {
				.looseRFC3339 = true
			}

			 := !.isNumeric() || .Tokens.Last.NeedObjectName() || .Flags.Get(jsonflags.StringifyNumbers)
			var  jsonwire.ValueFlags
			 := .Addr().Interface().(*time.Time)
			,  := .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  // unlike marshal, never wrapped
					}
					return newUnmarshalErrorAfter(, , )
				}
				* = .tt
				return nil
			case '0':
				if  {
					break
				}
				if  := .unmarshal();  != nil {
					if .Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
						return  // unlike marshal, never wrapped
					}
					return newUnmarshalErrorAfter(, , )
				}
				* = .tt
				return nil
			}
			return newUnmarshalErrorAfter(, , nil)
		}
	}
	return 
}

type durationArshaler struct {
	td time.Duration

	// base records the representation where:
	//   - 0 uses time.Duration.String
	//   - 1e0, 1e3, 1e6, or 1e9 use a decimal encoding of the duration as
	//     nanoseconds, microseconds, milliseconds, or seconds.
	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
	default:
		return false
	}
	return true
}

func ( *durationArshaler) () bool {
	return .base != 0 && .base != 60
}

func ( *durationArshaler) ( []byte) ([]byte, error) {
	switch .base {
	case 0:
		return append(, .td.String()...), nil
	default:
		return appendDurationBase10(, .td, .base), nil
	}
}

func ( *durationArshaler) ( []byte) ( error) {
	switch .base {
	case 0:
		.td,  = time.ParseDuration(string())
	default:
		.td,  = parseDurationBase10(, .base)
	}
	return 
}

type timeArshaler struct {
	tt time.Time

	// base records the representation where:
	//   - 0 uses RFC 3339 encoding of the timestamp
	//   - 1e0, 1e3, 1e6, or 1e9 use a decimal encoding of the timestamp as
	//     seconds, milliseconds, microseconds, or nanoseconds since Unix epoch.
	//   - math.MaxUint uses time.Time.Format to encode the timestamp
	base   uint64
	format string // time format passed to time.Parse

	looseRFC3339 bool
}

func ( *timeArshaler) ( string) bool {
	// We assume that an exported constant in the time package will
	// always start with an uppercase ASCII letter.
	if len() == 0 {
		return false
	}
	.base = math.MaxUint // implies custom format
	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:
		// Reject any Go identifier in case new constants are supported.
		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(, )
		// 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.
		switch  := [:]; {
		case [len("9999")] != '-': // year must be exactly 4 digits wide
			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:
		// Use time.Time.UnmarshalText to avoid possible string allocation.
		if  := .tt.UnmarshalText();  != nil {
			return 
		}
		// TODO(https://go.dev/issue/57912):
		// RFC 3339 specifies the grammar for 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.
		 := func(, , , ,  string) error {
			return &time.ParseError{Layout: , Value: , LayoutElem: , ValueElem: , Message: }
		}
		switch {
		case .looseRFC3339:
			return nil
		case [len("2006-01-02T")+1] == ':': // hour must be two digits
			return (time.RFC3339, string(), "15", string([len("2006-01-02T"):][:1]), "")
		case [len("2006-01-02T15:04:05")] == ',': // sub-second separator must be a period
			return (time.RFC3339, string(), ".", ",", "")
		case [len()-1] != 'Z':
			switch {
			case parseDec2([len()-len("07:00"):]) >= 24: // timezone hour must be in range
				return (time.RFC3339, string(), "Z07:00", string([len()-len("Z07:00"):]), ": timezone hour out of range")
			case parseDec2([len()-len("00"):]) >= 60: // timezone minute must be in range
				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 
	}
}

// appendDurationBase10 appends d formatted as a decimal fractional number,
// where pow10 is a power-of-10 used to scale down the number.
func appendDurationBase10( []byte,  time.Duration,  uint64) []byte {
	,  := mayAppendDurationSign(, )            // append sign
	,  := bits.Div64(0, , uint64()) // compute whole and frac fields
	 = strconv.AppendUint(, , 10)           // append whole field
	return appendFracBase10(, , )        // append frac field
}

// parseDurationBase10 parses d from a decimal fractional number,
// where pow10 is a power-of-10 used to scale up the number.
func parseDurationBase10( []byte,  uint64) (time.Duration, error) {
	,  := consumeSign()                            // consume sign
	,  := bytesCutByte(, '.', true) // consume whole and frac fields
	,  := jsonwire.ParseUint()         // parse whole field; may overflow
	,  := parseFracBase10(, )        // parse frac field
	,  := bits.Mul64(, uint64())               // overflow if hi > 0
	,  := bits.Add64(, uint64(), 0)               // overflow if co > 0
	switch  := mayApplyDurationSign(, ); {            // overflow if neg != (d < 0)
	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
	}
}

// mayAppendDurationSign appends a negative sign if n is negative.
func mayAppendDurationSign( []byte,  time.Duration) ([]byte, uint64) {
	if  < 0 {
		 = append(, '-')
		 *= -1
	}
	return , uint64()
}

// mayApplyDurationSign inverts n if neg is specified.
func mayApplyDurationSign( uint64,  bool) time.Duration {
	if  {
		return -1 * time.Duration()
	} else {
		return +1 * time.Duration()
	}
}

// appendTimeUnix appends t formatted as a decimal fractional number,
// where pow10 is a power-of-10 used to scale up the number.
func appendTimeUnix( []byte,  time.Time,  uint64) []byte {
	,  := .Unix(), int64(.Nanosecond())
	if  < 0 {
		 = append(, '-')
		,  = negateSecNano(, )
	}
	switch {
	case  == 1e0: // fast case where units is in seconds
		 = strconv.AppendUint(, uint64(), 10)
		return appendFracBase10(, uint64(), 1e9)
	case uint64() < 1e9: // intermediate case where units is not seconds, but no overflow
		 = strconv.AppendUint(, uint64()*uint64()+uint64(uint64()/(1e9/)), 10)
		return appendFracBase10(, (uint64()*)%1e9, 1e9)
	default: // slow case where units is not seconds and overflow would occur
		 = strconv.AppendUint(, uint64(), 10)
		 = appendPaddedBase10(, uint64()/(1e9/), )
		return appendFracBase10(, (uint64()*)%1e9, 1e9)
	}
}

// parseTimeUnix parses t formatted as a decimal fractional number,
// where pow10 is a power-of-10 used to scale down the number.
func parseTimeUnix( []byte,  uint64) (time.Time, error) {
	,  := consumeSign()                            // consume sign
	,  := bytesCutByte(, '.', true) // consume whole and frac fields
	,  := jsonwire.ParseUint()         // parse whole field; may overflow
	,  := parseFracBase10(, 1e9/)    // parse frac field
	var ,  int64
	switch {
	case  == 1e0: // fast case where units is in seconds
		 = int64() // check overflow later after negation
		 = int64() // cannot overflow
	case : // intermediate case where units is not seconds, but no overflow
		 = int64( / )                     // check overflow later after negation
		 = int64((%)*(1e9/) + ) // cannot overflow
	case ! &&  == math.MaxUint64: // slow case where units is not seconds and overflow occurred
		 := int(math.Log10(float64()))                                // compute len(strconv.Itoa(pow10-1))
		,  = jsonwire.ParseUint([:len()-]) // parse the upper whole field
		,  := parsePaddedBase10([len()-:], )  // parse the lower whole field
		 = int64()                                                      // check overflow later after negation
		 = int64(*(1e9/) + )                                    // cannot overflow
	}
	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
	}
}

// negateSecNano negates a Unix timestamp, where nsec must be within [0, 1e9).
func negateSecNano(,  int64) (int64, int64) {
	 = ^               // twos-complement negation (i.e., -1*sec + 1)
	 = - + 1e9       // negate nsec and add 1e9 (which is the extra +1 from sec negation)
	 += int64( / 1e9) // handle possible overflow of nsec if it started as zero
	 %= 1e9              // ensure nsec stays within [0, 1e9)
	return , 
}

// appendFracBase10 appends the fraction of n/max10,
// where max10 is a power-of-10 that is larger than n.
func appendFracBase10( []byte, ,  uint64) []byte {
	if  == 0 {
		return 
	}
	return bytes.TrimRight(appendPaddedBase10(append(, '.'), , ), "0")
}

// parseFracBase10 parses the fraction of n/max10,
// where max10 is a power-of-10 that is larger than n.
func parseFracBase10( []byte,  uint64) ( uint64,  bool) {
	switch {
	case len() == 0:
		return 0, true
	case len() < len(".0") || [0] != '.':
		return 0, false
	}
	return parsePaddedBase10([len("."):], )
}

// appendPaddedBase10 appends a zero-padded encoding of n,
// where max10 is a power-of-10 that is larger than n.
func appendPaddedBase10( []byte, ,  uint64) []byte {
	if  < /10 {
		// Formatting of n is shorter than log10(max10),
		// so add max10/10 to ensure the length is equal to log10(max10).
		 := len()
		 = strconv.AppendUint(, +/10, 10)
		[]-- // subtract the addition of max10/10
		return 
	}
	return strconv.AppendUint(, , 10)
}

// parsePaddedBase10 parses b as the zero-padded encoding of n,
// where max10 is a power-of-10 that is larger than n.
// Truncated suffix is treated as implicit zeros.
// Extended suffix is ignored, but verified to contain only digits.
func parsePaddedBase10( []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 // trailing characters are not digits
	}
	return , true
}

// consumeSign consumes an optional leading negative sign.
func consumeSign( []byte) ([]byte, bool) {
	if len() > 0 && [0] == '-' {
		return [len("-"):], true
	}
	return , false
}

// bytesCutByte is similar to bytes.Cut(b, []byte{c}),
// except c may optionally be included as part of the suffix.
func bytesCutByte( []byte,  byte,  bool) ([]byte, []byte) {
	if  := bytes.IndexByte(, );  >= 0 {
		if  {
			return [:], [:]
		}
		return [:], [+1:]
	}
	return , nil
}

// parseDec2 parses b as an unsigned, base-10, 2-digit number.
// The result is undefined if digits are not base-10.
func parseDec2( []byte) byte {
	if len() < 2 {
		return 0
	}
	return 10*([0]-'0') + ([1] - '0')
}