aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/aws/smithy-go/time/time.go
blob: b552a09f8a8bcf3669d7ad7bb60382b5e1df4210 (plain) (blame)
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
}