-
Notifications
You must be signed in to change notification settings - Fork 0
/
datetime.go
205 lines (176 loc) · 6.72 KB
/
datetime.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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// Copyright 2023 Adam Chalkley
//
// https://github.com/atc0005/check-rsat
//
// Licensed under the MIT License. See LICENSE file in the project root for
// full license information.
package rsat
import (
"encoding/json"
"strings"
"time"
)
// References:
//
// - https://romangaranin.net/posts/2021-02-19-json-time-and-golang/
// - https://pkg.go.dev/time#pkg-constants
// Time layouts observed "in the wild" for various versions of Red Hat
// Satellite.
const (
// StandardAPITimeLayoutWithTimezone is the time layout format as used by
// the Red Hat Satellite API for the majority of the date/time properties
// when the user has their Satellite account timezone setting configured
// as `(GMT+00:00) UTC`.
//
// Examples (from JSON API response) for Satellite 6.15:
//
// "created_at": "2024-05-09 21:14:51 UTC",
// "updated_at": "2024-05-09 21:14:51 UTC",
//
// See also https://rsat.example.com/apidoc/v2/sync_plans/index.html
StandardAPITimeLayoutWithTimezone string = "2006-01-02 15:04:05 UTC"
// StandardAPITimeLayoutWithOffset is the time layout format as used by
// the Red Hat Satellite API for the majority of the date/time properties
// when the user has their Satellite account timezone setting configured
// as `Browser timezone`.
//
// Examples (from JSON API response):
//
// "created_at": "2024-05-09 16:14:51 -0500",
// "updated_at": "2024-05-09 16:14:51 -0500",
//
// See also https://rsat.example.com/apidoc/v2/sync_plans/index.html
StandardAPITimeLayoutWithOffset string = "2006-01-02 15:04:05 -0700"
// SyncTimeLayoutWithTimezone is the time layout format as used by current
// versions of the Red Hat Satellite Sync Plans API for the next_sync
// property in current versions of Red Hat Satellite when the user has
// their Satellite account timezone setting configured as `(GMT+00:00)
// UTC`.
//
// Example: "next_sync": "2024-05-10 17:14:00 UTC",
//
// See also https://rsat.example.com/apidoc/v2/sync_plans/index.html
SyncTimeLayoutWithTimezone string = "2006-01-02 15:04:05 UTC"
// SyncTimeLayoutWithOffset is the time layout format as used by current
// versions of the Red Hat Satellite Sync Plans API for the next_sync
// property when the user has their Satellite account timezone setting
// configured as `Browser timezone`.
//
// Example: "next_sync": "2024/05/10 15:16:00 -0500",
//
// See also https://rsat.example.com/apidoc/v2/sync_plans/index.html
SyncTimeLayoutWithOffset string = "2006-01-02 15:04:05 -0700"
// LegacySyncTimeLayout is the time layout format as used by legacy
// versions of the Red Hat Satellite Sync Plans API for the next_sync
// property (e.g., Satellite 6.5).
//
// Example(account Timezone property is set to `(GMT+00:00) UTC`):
//
// "next_sync": "2024/05/10 20:16:00 +0000",
//
// Example(account Timezone property is set to `Browser timezone`):
//
// "next_sync": "2024/05/10 15:16:00 -0500",
//
// This layout works equally well for both.
//
// See also https://rsat.example.com/apidoc/v2/sync_plans/index.html
LegacySyncTimeLayout string = "2006/01/02 15:04:05 -0700"
)
// StandardAPITime is time value as represented in the Red Hat Satellite API
// for the majority of the date/time properties. It uses the
// StandardAPITimeLayout format.
type StandardAPITime time.Time
// SyncTime is time value as represented in the Red Hat Satellite Sync Plans
// API for the next_sync and sync_date properties. It uses the SyncTimeLayout
// format.
type SyncTime time.Time
// String implements the fmt.Stringer interface as a convenience method.
func (dt StandardAPITime) String() string {
return dt.Format(StandardAPITimeLayoutWithOffset)
}
// String implements the fmt.Stringer interface as a convenience method.
func (dt SyncTime) String() string {
// return dt.Format(StandardAPITimeLayout)
switch {
case time.Time(dt).IsZero():
return "Not scheduled"
default:
return time.Time(dt).Local().Format(StandardAPITimeLayoutWithOffset)
}
}
// Format calls (time.Time).Format as a convenience for the caller.
func (dt StandardAPITime) Format(layout string) string {
return time.Time(dt).Format(layout)
}
// Format calls (time.Time).Format as a convenience for the caller.
func (dt SyncTime) Format(layout string) string {
return time.Time(dt).Format(layout)
}
// MarshalJSON implements the json.Marshaler interface. This compliments the
// custom Unmarshaler implementation to handle conversion of a native Go
// time.Time format to a time value in a format that matches JSON API
// expectations.
func (dt StandardAPITime) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Time(dt).Format(StandardAPITimeLayoutWithOffset))
}
// MarshalJSON implements the json.Marshaler interface. This compliments the
// custom Unmarshaler implementation to handle conversion of a native Go
// time.Time format to a time value in a format that matches JSON API
// expectations.
func (dt SyncTime) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Time(dt).Format(SyncTimeLayoutWithOffset))
}
// UnmarshalJSON implements the json.Unmarshaler interface to handle
// converting a time string from the JSON API (most time properties) to a
// native Go time.Time value using a supported auto-detected layout.
func (dt *StandardAPITime) UnmarshalJSON(data []byte) error {
value := strings.Trim(string(data), `"`) // get rid of "
if value == "" || value == JSONNullKeyword {
// Per json.Unmarshaler convention we treat "null" value as a no-op.
return nil
}
t, err := parseDate(value)
if err != nil {
return err
}
*dt = StandardAPITime(t) // set result using the pointer
return nil
}
// UnmarshalJSON implements the json.Unmarshaler interface to handle
// converting a time string from the next_sync property in the JSON API to a
// native Go time.Time value using the SyncTimeLayout format.
func (dt *SyncTime) UnmarshalJSON(data []byte) error {
value := strings.Trim(string(data), `"`) // get rid of "
if value == "" || value == JSONNullKeyword {
// Per json.Unmarshaler convention we treat "null" value as a no-op.
return nil
}
t, err := parseDate(value)
if err != nil {
return err
}
*dt = SyncTime(t) // set result using the pointer
return nil
}
// parseDate is a helper function that attempts to handle all known datetime
// formats for legacy and current Red Hat Satellite APIs. An error is returned
// if the given datetime string does not match a known layout.
func parseDate(datetime string) (time.Time, error) {
knownLayouts := []string{
StandardAPITimeLayoutWithTimezone,
StandardAPITimeLayoutWithOffset,
SyncTimeLayoutWithTimezone,
SyncTimeLayoutWithOffset,
LegacySyncTimeLayout,
}
var result time.Time
var err error
for _, layout := range knownLayouts {
result, err = time.Parse(layout, datetime)
if err == nil {
return result, nil
}
}
return time.Time{}, err
}