-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtime_parser.go
More file actions
104 lines (90 loc) · 2.78 KB
/
time_parser.go
File metadata and controls
104 lines (90 loc) · 2.78 KB
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
package qparser
import (
"fmt"
"regexp"
"strings"
"time"
)
var (
timezoneFixRegex = regexp.MustCompile(`^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?)\s([+-]?\d{2}:\d{2})$`)
timeFormats = []string{
time.RFC3339,
time.RFC3339Nano,
time.DateTime,
"2006-01-02T15:04:05",
"2006-01-02T15:04:05.000",
"2006-01-02T15:04:05.000000",
"2006-01-02T15:04:05.999999999",
"2006-01-02T15:04:05-07:00",
"2006-01-02T15:04:05.000-07:00",
"2006-01-02T15:04:05.000000-07:00",
"2006-01-02T15:04:05.999999999-07:00",
"2006-01-02 15:04:05-07:00",
"2006-01-02 15:04:05.000-07:00",
"2006-01-02 15:04:05.000000-07:00",
"2006-01-02 15:04:05.999999999-07:00",
// Formats for space-separated timezone
"2006-01-02T15:04:05 -07:00",
"2006-01-02T15:04:05.000 -07:00",
"2006-01-02T15:04:05.000000 -07:00",
"2006-01-02T15:04:05.999999999 -07:00",
time.DateOnly,
time.TimeOnly,
}
)
func parseTime(value string) (time.Time, error) {
value = strings.TrimSpace(value)
// Handle space-separated timezone offsets by converting them to standard format
if strings.Contains(value, " ") {
value = fixTimezoneOffset(value)
}
// Fast path: try most common formats first based on heuristics
if len(value) >= 19 {
// RFC3339 format detection
if value[4] == '-' && value[7] == '-' && value[10] == 'T' && value[13] == ':' && value[16] == ':' {
if strings.HasSuffix(value, "Z") || (len(value) >= 20 && (value[19] == '+' || value[19] == '-')) {
// Try RFC3339 formats first
if t, err := time.Parse(time.RFC3339, value); err == nil {
return t, nil
}
if t, err := time.Parse(time.RFC3339Nano, value); err == nil {
return t, nil
}
}
}
}
// Date only detection
if len(value) == 10 && value[4] == '-' && value[7] == '-' {
if t, err := time.Parse(time.DateOnly, value); err == nil {
return t, nil
}
}
// Time only detection
if len(value) >= 8 && value[2] == ':' && value[5] == ':' {
if t, err := time.Parse(time.TimeOnly, value); err == nil {
return t, nil
}
}
// Fallback to all formats
for _, format := range timeFormats {
if parsedTime, err := time.Parse(format, value); err == nil {
return parsedTime, nil
}
}
return time.Time{}, fmt.Errorf("%w: unable to parse with any known date format: %s", ErrInvalidValue, value)
}
func fixTimezoneOffset(value string) string {
// Handle cases like "2025-07-04T17:12:32 07:00" -> "2025-07-04T17:12:32+07:00"
// Also handle cases that already have +/- like "2025-07-04T17:12:32 -05:00"
matches := timezoneFixRegex.FindStringSubmatch(value)
if matches != nil {
timePart := matches[1]
offsetPart := matches[2]
// If offset doesn't start with + or -, assume positive
if offsetPart[0] != '+' && offsetPart[0] != '-' {
offsetPart = "+" + offsetPart
}
return timePart + offsetPart
}
return value
}