forked from sdcoffey/techan
-
Notifications
You must be signed in to change notification settings - Fork 0
/
timeperiod.go
170 lines (145 loc) · 5.19 KB
/
timeperiod.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package techan
import (
"fmt"
"regexp"
"time"
)
// TimePeriod is a simple struct that describes a period of time with a Start and End time
type TimePeriod struct {
Start time.Time
End time.Time
}
// Constants representing basic, human-readable and writable date formats
const (
SimpleDateTimeFormat = "01/02/2006T15:04:05"
SimpleDateFormat = "01/02/2006"
SimpleTimeFormat = "15:04:05"
SimpleDateFormatV2 = "2006-01-02"
)
// Constants representing regexes for parsing datetimes
var (
SimpleTimeFomatRegex = regexp.MustCompile(`T\d{2}:\d{2}:\d{2}`)
SimpleDateFormatV2Regex = regexp.MustCompile(`\d{4}-\d{2}-\d{2}`)
)
// ParseTimePeriod parses two datetimes as one string and returns it as a TimePeriod.
//
// Note that if you were previously using Parse, the date format has changed to something more rfc3339-like (yyyy-mm-dd)
// Will accept any combination of date and time for either side. Omitting the right hand side will result in a time
// period ending in time.Now()
func ParseTimePeriod(period string) (TimePeriod, error) {
dateMatches := SimpleDateFormatV2Regex.FindAllStringIndex(period, -1)
timeMatches := SimpleTimeFomatRegex.FindAllStringIndex(period, -1)
formats := make([]string, len(dateMatches))
timeStrings := make([]string, len(dateMatches))
for i, j := 0, 0; i < len(dateMatches); i++ {
date := period[dateMatches[i][0]:dateMatches[i][1]]
if j < len(timeMatches) && timeMatches[j][0] == dateMatches[i][1] {
t := period[timeMatches[j][0]:timeMatches[j][1]]
j++
timeStrings[i] = fmt.Sprint(date, t)
formats[i] = fmt.Sprint(SimpleDateFormatV2, "T", SimpleTimeFormat)
} else {
timeStrings[i] = date
formats[i] = SimpleDateFormatV2
}
}
times := [2]time.Time{}
for i, timeString := range timeStrings {
var err error
times[i], err = time.Parse(formats[i], timeString)
if err != nil {
return TimePeriod{}, err
}
}
timePeriod := TimePeriod{
Start: times[0],
}
if !times[1].IsZero() {
timePeriod.End = times[1]
} else {
timePeriod.End = time.Now()
}
return timePeriod, nil
}
// Parse takes a string in one of the following formats and returns a new TimePeriod, and optionally, an error
//
// Deprecated: Please use ParseTimePeriod instead
func Parse(timerange string) (tr TimePeriod, err error) {
var start, end, layout string
switch len(timerange) {
case len(SimpleDateTimeFormat)*2 + 1:
layout = SimpleDateTimeFormat
start = string(timerange[:len(SimpleDateTimeFormat)])
end = string(timerange[len(SimpleDateTimeFormat)+1:])
case len(SimpleDateTimeFormat) + 1:
layout = SimpleDateTimeFormat
start = string(timerange[:len(SimpleDateTimeFormat)])
end = ""
case len(SimpleDateFormat)*2 + 1:
layout = SimpleDateFormat
start = string(timerange[:len(SimpleDateFormat)])
end = string(timerange[len(SimpleDateFormat)+1:])
case len(SimpleDateFormat) + 1:
layout = SimpleDateFormat
start = string(timerange[:len(SimpleDateFormat)])
end = ""
default:
err = fmt.Errorf("could not parse timerange string %s", timerange)
return
}
if tr.Start, err = time.Parse(layout, start); err != nil {
err = fmt.Errorf("could not parse time string %s", start)
}
if end == "" {
tr.End = time.Now()
} else if tr.End, err = time.Parse(layout, end); err != nil {
err = fmt.Errorf("could not parse time string %s", end)
}
return
}
// In returns a copy of TimePeriod tp with both start and end times' location set to the specified location
func (tp TimePeriod) In(location *time.Location) TimePeriod {
return TimePeriod{
Start: tp.Start.In(location),
End: tp.End.In(location),
}
}
// UTC returns a copy of TimePeriod tp with both start and end times' location set to UTC
func (tp TimePeriod) UTC() TimePeriod {
return tp.In(time.UTC)
}
// Length returns the length of the period as a time.Duration value
func (tp TimePeriod) Length() time.Duration {
return tp.End.Sub(tp.Start)
}
// Since returns the amount of time elapsed since the end of another TimePeriod as a time.Duration value
func (tp TimePeriod) Since(other TimePeriod) time.Duration {
return tp.Start.Sub(other.End)
}
// Format returns the string representation of this timePeriod in the given format
func (tp TimePeriod) Format(layout string) string {
return fmt.Sprintf("%s -> %s", tp.Start.Format(layout), tp.End.Format(layout))
}
// Advance will return a new TimePeriod with the start and end periods moved forwards or backwards in time in accordance
// with the number of iterations given.
//
// Example:
// A timePeriod that is one hour long, starting at unix time 0 and ending at unix time 3600, and advanced by one,
// will return a time period starting at unix time 3600 and ending at unix time 7200
func (tp TimePeriod) Advance(iterations int) TimePeriod {
return TimePeriod{
Start: tp.Start.Add(tp.Length() * time.Duration(iterations)),
End: tp.End.Add(tp.Length() * time.Duration(iterations)),
}
}
func (tp TimePeriod) String() string {
layout := fmt.Sprint(SimpleDateFormatV2, "T", SimpleTimeFormat)
return tp.Format(layout)
}
// NewTimePeriod returns a TimePeriod starting at the given time and ending at the given time plus the given duration
func NewTimePeriod(start time.Time, period time.Duration) TimePeriod {
return TimePeriod{
Start: start,
End: start.Add(period),
}
}