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