time.go (3408B)
1 package time 2 3 import ( 4 "context" 5 "fmt" 6 "math/big" 7 "strings" 8 "time" 9 ) 10 11 const ( 12 // dateTimeFormat is a IMF-fixdate formatted RFC3339 section 5.6 13 dateTimeFormatInput = "2006-01-02T15:04:05.999999999Z" 14 dateTimeFormatInputNoZ = "2006-01-02T15:04:05.999999999" 15 dateTimeFormatOutput = "2006-01-02T15:04:05.999Z" 16 17 // httpDateFormat is a date time defined by RFC 7231#section-7.1.1.1 18 // IMF-fixdate with no UTC offset. 19 httpDateFormat = "Mon, 02 Jan 2006 15:04:05 GMT" 20 // Additional formats needed for compatibility. 21 httpDateFormatSingleDigitDay = "Mon, _2 Jan 2006 15:04:05 GMT" 22 httpDateFormatSingleDigitDayTwoDigitYear = "Mon, _2 Jan 06 15:04:05 GMT" 23 ) 24 25 var millisecondFloat = big.NewFloat(1e3) 26 27 // FormatDateTime formats value as a date-time, (RFC3339 section 5.6) 28 // 29 // Example: 1985-04-12T23:20:50.52Z 30 func FormatDateTime(value time.Time) string { 31 return value.UTC().Format(dateTimeFormatOutput) 32 } 33 34 // ParseDateTime parses a string as a date-time, (RFC3339 section 5.6) 35 // 36 // Example: 1985-04-12T23:20:50.52Z 37 func ParseDateTime(value string) (time.Time, error) { 38 return tryParse(value, 39 dateTimeFormatInput, 40 dateTimeFormatInputNoZ, 41 time.RFC3339Nano, 42 time.RFC3339, 43 ) 44 } 45 46 // FormatHTTPDate formats value as a http-date, (RFC 7231#section-7.1.1.1 IMF-fixdate) 47 // 48 // Example: Tue, 29 Apr 2014 18:30:38 GMT 49 func FormatHTTPDate(value time.Time) string { 50 return value.UTC().Format(httpDateFormat) 51 } 52 53 // ParseHTTPDate parses a string as a http-date, (RFC 7231#section-7.1.1.1 IMF-fixdate) 54 // 55 // Example: Tue, 29 Apr 2014 18:30:38 GMT 56 func ParseHTTPDate(value string) (time.Time, error) { 57 return tryParse(value, 58 httpDateFormat, 59 httpDateFormatSingleDigitDay, 60 httpDateFormatSingleDigitDayTwoDigitYear, 61 time.RFC850, 62 time.ANSIC, 63 ) 64 } 65 66 // FormatEpochSeconds returns value as a Unix time in seconds with with decimal precision 67 // 68 // Example: 1515531081.123 69 func FormatEpochSeconds(value time.Time) float64 { 70 ms := value.UnixNano() / int64(time.Millisecond) 71 return float64(ms) / 1e3 72 } 73 74 // ParseEpochSeconds returns value as a Unix time in seconds with with decimal precision 75 // 76 // Example: 1515531081.123 77 func ParseEpochSeconds(value float64) time.Time { 78 f := big.NewFloat(value) 79 f = f.Mul(f, millisecondFloat) 80 i, _ := f.Int64() 81 // Offset to `UTC` because time.Unix returns the time value based on system 82 // local setting. 83 return time.Unix(0, i*1e6).UTC() 84 } 85 86 func tryParse(v string, formats ...string) (time.Time, error) { 87 var errs parseErrors 88 for _, f := range formats { 89 t, err := time.Parse(f, v) 90 if err != nil { 91 errs = append(errs, parseError{ 92 Format: f, 93 Err: err, 94 }) 95 continue 96 } 97 return t, nil 98 } 99 100 return time.Time{}, fmt.Errorf("unable to parse time string, %w", errs) 101 } 102 103 type parseErrors []parseError 104 105 func (es parseErrors) Error() string { 106 var s strings.Builder 107 for _, e := range es { 108 fmt.Fprintf(&s, "\n * %q: %v", e.Format, e.Err) 109 } 110 111 return "parse errors:" + s.String() 112 } 113 114 type parseError struct { 115 Format string 116 Err error 117 } 118 119 // SleepWithContext will wait for the timer duration to expire, or until the context 120 // is canceled. Whichever happens first. If the context is canceled the 121 // Context's error will be returned. 122 func SleepWithContext(ctx context.Context, dur time.Duration) error { 123 t := time.NewTimer(dur) 124 defer t.Stop() 125 126 select { 127 case <-t.C: 128 break 129 case <-ctx.Done(): 130 return ctx.Err() 131 } 132 133 return nil 134 }