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
|
package timefmt
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"time"
)
var unixTimestampPattern = regexp.MustCompile(`^[+-]?\d+$`)
var localLayouts = []string{
"2006-01-02",
"2006-01-02T15:04",
"2006-01-02 15:04",
"2006-01-02T15:04:05",
"2006-01-02 15:04:05",
}
// Parse converts timestamp/date text into a time value.
func Parse(value string) (time.Time, error) {
return ParseAt(value, time.Now())
}
// ParseAt behaves like Parse but uses a provided "now" reference for relative values.
func ParseAt(value string, now time.Time) (time.Time, error) {
trimmed := strings.TrimSpace(value)
if trimmed == "" {
return time.Time{}, errors.New("time value must not be empty")
}
lower := strings.ToLower(trimmed)
switch lower {
case "today":
return startOfDay(now), nil
case "yesterday":
return startOfDay(now.AddDate(0, 0, -1)), nil
}
if unixTimestampPattern.MatchString(trimmed) {
seconds, err := strconv.ParseInt(trimmed, 10, 64)
if err != nil {
return time.Time{}, fmt.Errorf("parse unix timestamp %q: %w", value, err)
}
return time.Unix(seconds, 0), nil
}
if parsed, err := time.Parse(time.RFC3339, trimmed); err == nil {
return parsed, nil
}
for _, layout := range localLayouts {
parsed, err := time.ParseInLocation(layout, trimmed, now.Location())
if err == nil {
return parsed, nil
}
}
return time.Time{}, fmt.Errorf("unsupported time format %q", value)
}
func startOfDay(value time.Time) time.Time {
year, month, day := value.Date()
return time.Date(year, month, day, 0, 0, 0, 0, value.Location())
}
|