package time

import (
	
	
	
	
	
)

const (
	// dateTimeFormat is a IMF-fixdate formatted RFC3339 section 5.6
	dateTimeFormatInput    = "2006-01-02T15:04:05.999999999Z"
	dateTimeFormatInputNoZ = "2006-01-02T15:04:05.999999999"
	dateTimeFormatOutput   = "2006-01-02T15:04:05.999Z"

	// httpDateFormat is a date time defined by RFC 7231#section-7.1.1.1
	// IMF-fixdate with no UTC offset.
	httpDateFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
	// Additional formats needed for compatibility.
	httpDateFormatSingleDigitDay             = "Mon, _2 Jan 2006 15:04:05 GMT"
	httpDateFormatSingleDigitDayTwoDigitYear = "Mon, _2 Jan 06 15:04:05 GMT"
)

var millisecondFloat = big.NewFloat(1e3)

// FormatDateTime formats value as a date-time, (RFC3339 section 5.6)
//
// Example: 1985-04-12T23:20:50.52Z
func ( time.Time) string {
	return .UTC().Format(dateTimeFormatOutput)
}

// ParseDateTime parses a string as a date-time, (RFC3339 section 5.6)
//
// Example: 1985-04-12T23:20:50.52Z
func ( string) (time.Time, error) {
	return tryParse(,
		dateTimeFormatInput,
		dateTimeFormatInputNoZ,
		time.RFC3339Nano,
		time.RFC3339,
	)
}

// FormatHTTPDate formats value as a http-date, (RFC 7231#section-7.1.1.1 IMF-fixdate)
//
// Example: Tue, 29 Apr 2014 18:30:38 GMT
func ( time.Time) string {
	return .UTC().Format(httpDateFormat)
}

// ParseHTTPDate parses a string as a http-date, (RFC 7231#section-7.1.1.1 IMF-fixdate)
//
// Example: Tue, 29 Apr 2014 18:30:38 GMT
func ( string) (time.Time, error) {
	return tryParse(,
		httpDateFormat,
		httpDateFormatSingleDigitDay,
		httpDateFormatSingleDigitDayTwoDigitYear,
		time.RFC850,
		time.ANSIC,
	)
}

// FormatEpochSeconds returns value as a Unix time in seconds with with decimal precision
//
// Example: 1515531081.123
func ( time.Time) float64 {
	 := .UnixNano() / int64(time.Millisecond)
	return float64() / 1e3
}

// ParseEpochSeconds returns value as a Unix time in seconds with with decimal precision
//
// Example: 1515531081.123
func ( float64) time.Time {
	 := big.NewFloat()
	 = .Mul(, millisecondFloat)
	,  := .Int64()
	// Offset to `UTC` because time.Unix returns the time value based on system
	// local setting.
	return time.Unix(0, *1e6).UTC()
}

func ( string,  ...string) (time.Time, error) {
	var  parseErrors
	for ,  := range  {
		,  := time.Parse(, )
		if  != nil {
			 = append(, parseError{
				Format: ,
				Err:    ,
			})
			continue
		}
		return , nil
	}

	return time.Time{}, fmt.Errorf("unable to parse time string, %w", )
}

type parseErrors []parseError

func ( parseErrors) () string {
	var  strings.Builder
	for ,  := range  {
		fmt.Fprintf(&, "\n * %q: %v", .Format, .Err)
	}

	return "parse errors:" + .String()
}

type parseError struct {
	Format string
	Err    error
}

// SleepWithContext will wait for the timer duration to expire, or until the context
// is canceled. Whichever happens first. If the context is canceled the
// Context's error will be returned.
func ( context.Context,  time.Duration) error {
	 := time.NewTimer()
	defer .Stop()

	select {
	case <-.C:
		break
	case <-.Done():
		return .Err()
	}

	return nil
}