-
Notifications
You must be signed in to change notification settings - Fork 441
/
timeduration.go
146 lines (123 loc) · 3.66 KB
/
timeduration.go
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
135
136
137
138
139
140
141
142
143
144
145
146
package provisioner
import (
"encoding/json"
"time"
"github.com/pkg/errors"
)
var now = func() time.Time {
return time.Now().UTC()
}
// TimeDuration is a type that represents a time but the JSON unmarshaling can
// use a time using the RFC 3339 format or a time.Duration string. If a duration
// is used, the time will be set on the first call to TimeDuration.Time.
type TimeDuration struct {
t time.Time
d time.Duration
}
// NewTimeDuration returns a TimeDuration with the defined time.
func NewTimeDuration(t time.Time) TimeDuration {
return TimeDuration{t: t}
}
// ParseTimeDuration returns a new TimeDuration parsing the RFC 3339 time or
// time.Duration string.
func ParseTimeDuration(s string) (TimeDuration, error) {
if s == "" {
return TimeDuration{}, nil
}
// Try to use the unquoted RFC 3339 format
var t time.Time
if err := t.UnmarshalText([]byte(s)); err == nil {
return TimeDuration{t: t.UTC()}, nil
}
// Try to use the time.Duration string format
if d, err := time.ParseDuration(s); err == nil {
return TimeDuration{d: d}, nil
}
return TimeDuration{}, errors.Errorf("failed to parse %s", s)
}
// SetDuration initializes the TimeDuration with the given duration string. If
// the time was set it will re-set to zero.
func (t *TimeDuration) SetDuration(d time.Duration) {
t.t, t.d = time.Time{}, d
}
// SetTime initializes the TimeDuration with the given time. If the duration is
// set it will be re-set to zero.
func (t *TimeDuration) SetTime(tt time.Time) {
t.t, t.d = tt, 0
}
// IsZero returns true the TimeDuration represents the zero value, false
// otherwise.
func (t *TimeDuration) IsZero() bool {
return t.t.IsZero() && t.d == 0
}
// Equal returns if t and other are equal.
func (t *TimeDuration) Equal(other *TimeDuration) bool {
return t.t.Equal(other.t) && t.d == other.d
}
// MarshalJSON implements the json.Marshaler interface. If the time is set it
// will return the time in RFC 3339 format if not it will return the duration
// string.
func (t TimeDuration) MarshalJSON() ([]byte, error) {
switch {
case t.t.IsZero():
if t.d == 0 {
return []byte(`""`), nil
}
return json.Marshal(t.d.String())
default:
return t.t.MarshalJSON()
}
}
// UnmarshalJSON implements the json.Unmarshaler interface. The time is expected
// to be a quoted string in RFC 3339 format or a quoted time.Duration string.
func (t *TimeDuration) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return errors.Wrapf(err, "error unmarshaling %s", data)
}
// Empty TimeDuration
if s == "" {
*t = TimeDuration{}
return nil
}
// Try to use the unquoted RFC 3339 format
var tt time.Time
if err := tt.UnmarshalText([]byte(s)); err == nil {
*t = TimeDuration{t: tt}
return nil
}
// Try to use the time.Duration string format
if d, err := time.ParseDuration(s); err == nil {
*t = TimeDuration{d: d}
return nil
}
return errors.Errorf("failed to parse %s", data)
}
// Time calculates the time if needed and returns it.
func (t *TimeDuration) Time() time.Time {
return t.RelativeTime(now())
}
// Unix calculates the time if needed it and returns the Unix time in seconds.
func (t *TimeDuration) Unix() int64 {
return t.RelativeTime(now()).Unix()
}
// RelativeTime returns the embedded time.Time or the base time plus the
// duration if this is not zero.
func (t *TimeDuration) RelativeTime(base time.Time) time.Time {
switch {
case t == nil:
return time.Time{}
case t.t.IsZero():
if t.d == 0 {
return time.Time{}
}
t.t = base.Add(t.d)
return t.t.UTC()
default:
return t.t.UTC()
}
}
// String implements the fmt.Stringer interface.
func (t *TimeDuration) String() string {
return t.Time().String()
}