gofront 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +5 -4
- package/src/backend/js/expressions.js +849 -371
- package/src/backend/js/index.js +241 -147
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +512 -2547
- package/src/backend/js/statements.js +506 -103
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +177 -13
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +63 -171
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +643 -256
- package/src/backend/wasm/emit-exprs.js +124 -57
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-stmts.js +325 -75
- package/src/backend/wasm/emit.js +104 -13
- package/src/backend/wasm/globals.js +47 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +36 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +92 -717
- package/src/backend/wasm/monomorph.js +103 -6
- package/src/backend/wasm/natives.js +596 -0
- package/src/backend/wasm/prescan.js +5 -153
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -32
- package/src/backend/wasm/types.js +68 -20
- package/src/backend/wasm/variadic.js +3 -3
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +108 -130
- package/src/lexer/lexer.js +105 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +354 -132
- package/src/link/type-names.js +148 -0
- package/src/link/wasm.js +30 -6
- package/src/lower/bounds.js +153 -0
- package/src/lower/captures.js +92 -33
- package/src/lower/embedding.js +32 -3
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +905 -0
- package/src/lower/functions.js +2 -0
- package/src/lower/gen-types.js +274 -0
- package/src/lower/index.js +7 -1
- package/src/lower/json-codecs.js +228 -247
- package/src/lower/ownership.js +25 -7
- package/src/lower/reachability.js +69 -51
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +28 -5
- package/src/parser/expressions.js +86 -16
- package/src/parser/statements.js +1 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +1 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +124 -11
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +32 -6
- package/src/typechecker/expressions.js +326 -98
- package/src/typechecker/index.js +208 -33
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +40 -0
- package/src/typechecker/resolve.js +192 -36
- package/src/typechecker/statements.js +122 -11
- package/src/typechecker/stdlib/excluded.js +9 -5
- package/src/typechecker/stdlib/index.js +25 -9
- package/src/typechecker/termination.js +10 -1
- package/src/typechecker/types.js +203 -573
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +171 -38
- package/stdlib/internal/jsonrt/enc.go +186 -36
- package/stdlib/internal/jsonrt/scanner.go +17 -8
- package/stdlib/io/io.go +157 -1
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +16 -14
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +391 -452
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +235 -50
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +141 -3
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/fmt-runtime.js +0 -573
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -119
- package/src/backend/js/stdlib/builder.js +0 -98
- package/src/backend/js/stdlib/bytes.js +0 -368
- package/src/backend/js/stdlib/errors.js +0 -45
- package/src/backend/js/stdlib/fmt.js +0 -517
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -1167
- package/src/backend/js/stdlib/maps.js +0 -37
- package/src/backend/js/stdlib/math.js +0 -85
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -34
- package/src/backend/js/stdlib/slices.js +0 -107
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -79
- package/src/backend/js/stdlib/strings.js +0 -301
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -41
- package/src/backend/js/stdlib/time.js +0 -152
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/stdlib/bytes.js +0 -6716
- package/src/backend/wasm/stdlib/core.js +0 -10278
- package/src/backend/wasm/stdlib/io.js +0 -3578
- package/src/backend/wasm/stdlib/packages.js +0 -2402
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -2229
- package/src/typechecker/stdlib/core.js +0 -1079
- package/src/typechecker/stdlib/extended.js +0 -1210
- package/stdlib/bytes/iter.go +0 -192
- package/stdlib/bytes/search.go +0 -240
- package/stdlib/bytes/split.go +0 -218
- package/stdlib/errors/overlay.go +0 -12
- package/stdlib/maps/iter.go +0 -65
- package/stdlib/slices/extra.go +0 -73
- package/stdlib/slices/iter.go +0 -113
- package/stdlib/strings/extra.go +0 -35
- package/stdlib/strings/iter.go +0 -122
- package/stdlib/sync/extra.go +0 -105
- package/stdlib/unicode/utf8/bytes.go +0 -239
|
@@ -0,0 +1,745 @@
|
|
|
1
|
+
//go:build gofront
|
|
2
|
+
|
|
3
|
+
//gofront:target both
|
|
4
|
+
|
|
5
|
+
// Copyright 2010 The Go Authors. All rights reserved.
|
|
6
|
+
// Use of this source code is governed by a BSD-style
|
|
7
|
+
// license that can be found in the LICENSE file.
|
|
8
|
+
|
|
9
|
+
package time
|
|
10
|
+
|
|
11
|
+
import "errors"
|
|
12
|
+
|
|
13
|
+
// These are predefined layouts for use in Time.Format and time.Parse. The
|
|
14
|
+
// reference time used in these layouts is the specific time stamp:
|
|
15
|
+
//
|
|
16
|
+
// 01/02 03:04:05PM '06 -0700
|
|
17
|
+
//
|
|
18
|
+
// (January 2, 15:04:05, 2006, in time zone seven hours west of GMT).
|
|
19
|
+
const (
|
|
20
|
+
Layout = "01/02 03:04:05PM '06 -0700" // The reference time, in numerical order.
|
|
21
|
+
ANSIC = "Mon Jan _2 15:04:05 2006"
|
|
22
|
+
UnixDate = "Mon Jan _2 15:04:05 MST 2006"
|
|
23
|
+
RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
|
|
24
|
+
RFC822 = "02 Jan 06 15:04 MST"
|
|
25
|
+
RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
|
|
26
|
+
RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
|
|
27
|
+
RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
|
|
28
|
+
RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
|
|
29
|
+
RFC3339 = "2006-01-02T15:04:05Z07:00"
|
|
30
|
+
RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
|
|
31
|
+
Kitchen = "3:04PM"
|
|
32
|
+
// Handy time stamps.
|
|
33
|
+
Stamp = "Jan _2 15:04:05"
|
|
34
|
+
StampMilli = "Jan _2 15:04:05.000"
|
|
35
|
+
StampMicro = "Jan _2 15:04:05.000000"
|
|
36
|
+
StampNano = "Jan _2 15:04:05.000000000"
|
|
37
|
+
DateTime = "2006-01-02 15:04:05"
|
|
38
|
+
DateOnly = "2006-01-02"
|
|
39
|
+
TimeOnly = "15:04:05"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
const (
|
|
43
|
+
_ = iota
|
|
44
|
+
stdLongMonth = iota + stdNeedDate // "January"
|
|
45
|
+
stdMonth // "Jan"
|
|
46
|
+
stdNumMonth // "1"
|
|
47
|
+
stdZeroMonth // "01"
|
|
48
|
+
stdLongWeekDay // "Monday"
|
|
49
|
+
stdWeekDay // "Mon"
|
|
50
|
+
stdDay // "2"
|
|
51
|
+
stdUnderDay // "_2"
|
|
52
|
+
stdZeroDay // "02"
|
|
53
|
+
stdUnderYearDay // "__2"
|
|
54
|
+
stdZeroYearDay // "002"
|
|
55
|
+
stdHour = iota + stdNeedClock // "15"
|
|
56
|
+
stdHour12 // "3"
|
|
57
|
+
stdZeroHour12 // "03"
|
|
58
|
+
stdMinute // "4"
|
|
59
|
+
stdZeroMinute // "04"
|
|
60
|
+
stdSecond // "5"
|
|
61
|
+
stdZeroSecond // "05"
|
|
62
|
+
stdLongYear = iota + stdNeedDate // "2006"
|
|
63
|
+
stdYear // "06"
|
|
64
|
+
stdPM = iota + stdNeedClock // "PM"
|
|
65
|
+
stdpm // "pm"
|
|
66
|
+
stdTZ = iota // "MST"
|
|
67
|
+
stdISO8601TZ // "Z0700" // prints Z for UTC
|
|
68
|
+
stdISO8601SecondsTZ // "Z070000"
|
|
69
|
+
stdISO8601ShortTZ // "Z07"
|
|
70
|
+
stdISO8601ColonTZ // "Z07:00" // prints Z for UTC
|
|
71
|
+
stdISO8601ColonSecondsTZ // "Z07:00:00"
|
|
72
|
+
stdNumTZ // "-0700" // always numeric
|
|
73
|
+
stdNumSecondsTz // "-070000"
|
|
74
|
+
stdNumShortTZ // "-07" // always numeric
|
|
75
|
+
stdNumColonTZ // "-07:00" // always numeric
|
|
76
|
+
stdNumColonSecondsTZ // "-07:00:00"
|
|
77
|
+
stdFracSecond0 // ".0", ".00", ... , trailing zeros included
|
|
78
|
+
stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted
|
|
79
|
+
|
|
80
|
+
stdNeedDate = 1 << 8 // need month, day, year
|
|
81
|
+
stdNeedClock = 2 << 8 // need hour, minute, second
|
|
82
|
+
stdArgShift = 16 // extra argument in high bits, above low stdArgShift
|
|
83
|
+
stdSeparatorShift = 28 // extra argument in high 4 bits for fractional second separators
|
|
84
|
+
stdMask = 1<<stdArgShift - 1 // mask out argument
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
// std0x records the std values for "01", "02", ..., "06".
|
|
88
|
+
var std0x = [6]int{stdZeroMonth, stdZeroDay, stdZeroHour12, stdZeroMinute, stdZeroSecond, stdYear}
|
|
89
|
+
|
|
90
|
+
// startsWithLowerCase reports whether the string has a lower-case letter at the beginning.
|
|
91
|
+
// Its purpose is to prevent matching strings like "Month" when looking for "Mon".
|
|
92
|
+
func startsWithLowerCase(str string) bool {
|
|
93
|
+
if len(str) == 0 {
|
|
94
|
+
return false
|
|
95
|
+
}
|
|
96
|
+
c := str[0]
|
|
97
|
+
return 'a' <= c && c <= 'z'
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// nextStdChunk finds the first occurrence of a std string in
|
|
101
|
+
// layout and returns the text before, the std string, and the text after.
|
|
102
|
+
func nextStdChunk(layout string) (prefix string, std int, suffix string) {
|
|
103
|
+
for i := 0; i < len(layout); i++ {
|
|
104
|
+
switch c := int(layout[i]); c {
|
|
105
|
+
case 'J': // January, Jan
|
|
106
|
+
if len(layout) >= i+3 && layout[i:i+3] == "Jan" {
|
|
107
|
+
if len(layout) >= i+7 && layout[i:i+7] == "January" {
|
|
108
|
+
return layout[0:i], stdLongMonth, layout[i+7:]
|
|
109
|
+
}
|
|
110
|
+
if !startsWithLowerCase(layout[i+3:]) {
|
|
111
|
+
return layout[0:i], stdMonth, layout[i+3:]
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
case 'M': // Monday, Mon, MST
|
|
116
|
+
if len(layout) >= i+3 {
|
|
117
|
+
if layout[i:i+3] == "Mon" {
|
|
118
|
+
if len(layout) >= i+6 && layout[i:i+6] == "Monday" {
|
|
119
|
+
return layout[0:i], stdLongWeekDay, layout[i+6:]
|
|
120
|
+
}
|
|
121
|
+
if !startsWithLowerCase(layout[i+3:]) {
|
|
122
|
+
return layout[0:i], stdWeekDay, layout[i+3:]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if layout[i:i+3] == "MST" {
|
|
126
|
+
return layout[0:i], stdTZ, layout[i+3:]
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
case '0': // 01, 02, 03, 04, 05, 06, 002
|
|
131
|
+
if len(layout) >= i+2 && '1' <= layout[i+1] && layout[i+1] <= '6' {
|
|
132
|
+
return layout[0:i], std0x[layout[i+1]-'1'], layout[i+2:]
|
|
133
|
+
}
|
|
134
|
+
if len(layout) >= i+3 && layout[i+1] == '0' && layout[i+2] == '2' {
|
|
135
|
+
return layout[0:i], stdZeroYearDay, layout[i+3:]
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
case '1': // 15, 1
|
|
139
|
+
if len(layout) >= i+2 && layout[i+1] == '5' {
|
|
140
|
+
return layout[0:i], stdHour, layout[i+2:]
|
|
141
|
+
}
|
|
142
|
+
return layout[0:i], stdNumMonth, layout[i+1:]
|
|
143
|
+
|
|
144
|
+
case '2': // 2006, 2
|
|
145
|
+
if len(layout) >= i+4 && layout[i:i+4] == "2006" {
|
|
146
|
+
return layout[0:i], stdLongYear, layout[i+4:]
|
|
147
|
+
}
|
|
148
|
+
return layout[0:i], stdDay, layout[i+1:]
|
|
149
|
+
|
|
150
|
+
case '_': // _2, _2006, __2
|
|
151
|
+
if len(layout) >= i+2 && layout[i+1] == '2' {
|
|
152
|
+
//_2006 is really a literal _, followed by stdLongYear
|
|
153
|
+
if len(layout) >= i+5 && layout[i+1:i+5] == "2006" {
|
|
154
|
+
return layout[0 : i+1], stdLongYear, layout[i+5:]
|
|
155
|
+
}
|
|
156
|
+
return layout[0:i], stdUnderDay, layout[i+2:]
|
|
157
|
+
}
|
|
158
|
+
if len(layout) >= i+3 && layout[i+1] == '_' && layout[i+2] == '2' {
|
|
159
|
+
return layout[0:i], stdUnderYearDay, layout[i+3:]
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
case '3':
|
|
163
|
+
return layout[0:i], stdHour12, layout[i+1:]
|
|
164
|
+
|
|
165
|
+
case '4':
|
|
166
|
+
return layout[0:i], stdMinute, layout[i+1:]
|
|
167
|
+
|
|
168
|
+
case '5':
|
|
169
|
+
return layout[0:i], stdSecond, layout[i+1:]
|
|
170
|
+
|
|
171
|
+
case 'P': // PM
|
|
172
|
+
if len(layout) >= i+2 && layout[i+1] == 'M' {
|
|
173
|
+
return layout[0:i], stdPM, layout[i+2:]
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
case 'p': // pm
|
|
177
|
+
if len(layout) >= i+2 && layout[i+1] == 'm' {
|
|
178
|
+
return layout[0:i], stdpm, layout[i+2:]
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
case '-': // -070000, -07:00:00, -0700, -07:00, -07
|
|
182
|
+
if len(layout) >= i+7 && layout[i:i+7] == "-070000" {
|
|
183
|
+
return layout[0:i], stdNumSecondsTz, layout[i+7:]
|
|
184
|
+
}
|
|
185
|
+
if len(layout) >= i+9 && layout[i:i+9] == "-07:00:00" {
|
|
186
|
+
return layout[0:i], stdNumColonSecondsTZ, layout[i+9:]
|
|
187
|
+
}
|
|
188
|
+
if len(layout) >= i+5 && layout[i:i+5] == "-0700" {
|
|
189
|
+
return layout[0:i], stdNumTZ, layout[i+5:]
|
|
190
|
+
}
|
|
191
|
+
if len(layout) >= i+6 && layout[i:i+6] == "-07:00" {
|
|
192
|
+
return layout[0:i], stdNumColonTZ, layout[i+6:]
|
|
193
|
+
}
|
|
194
|
+
if len(layout) >= i+3 && layout[i:i+3] == "-07" {
|
|
195
|
+
return layout[0:i], stdNumShortTZ, layout[i+3:]
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
case 'Z': // Z070000, Z07:00:00, Z0700, Z07:00,
|
|
199
|
+
if len(layout) >= i+7 && layout[i:i+7] == "Z070000" {
|
|
200
|
+
return layout[0:i], stdISO8601SecondsTZ, layout[i+7:]
|
|
201
|
+
}
|
|
202
|
+
if len(layout) >= i+9 && layout[i:i+9] == "Z07:00:00" {
|
|
203
|
+
return layout[0:i], stdISO8601ColonSecondsTZ, layout[i+9:]
|
|
204
|
+
}
|
|
205
|
+
if len(layout) >= i+5 && layout[i:i+5] == "Z0700" {
|
|
206
|
+
return layout[0:i], stdISO8601TZ, layout[i+5:]
|
|
207
|
+
}
|
|
208
|
+
if len(layout) >= i+6 && layout[i:i+6] == "Z07:00" {
|
|
209
|
+
return layout[0:i], stdISO8601ColonTZ, layout[i+6:]
|
|
210
|
+
}
|
|
211
|
+
if len(layout) >= i+3 && layout[i:i+3] == "Z07" {
|
|
212
|
+
return layout[0:i], stdISO8601ShortTZ, layout[i+3:]
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
case '.', ',': // ,000, or .000, or ,999, or .999 - repeated digits for fractional seconds.
|
|
216
|
+
if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') {
|
|
217
|
+
ch := layout[i+1]
|
|
218
|
+
j := i + 1
|
|
219
|
+
for j < len(layout) && layout[j] == ch {
|
|
220
|
+
j++
|
|
221
|
+
}
|
|
222
|
+
// String of digits must end here - only fractional second if all digits.
|
|
223
|
+
if !isDigit(layout, j) {
|
|
224
|
+
code := stdFracSecond0
|
|
225
|
+
if layout[i+1] == '9' {
|
|
226
|
+
code = stdFracSecond9
|
|
227
|
+
}
|
|
228
|
+
std := stdFracSecond(code, j-(i+1), c)
|
|
229
|
+
return layout[0:i], std, layout[j:]
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return layout, 0, ""
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
var shortMonthNames = []string{
|
|
238
|
+
"Jan",
|
|
239
|
+
"Feb",
|
|
240
|
+
"Mar",
|
|
241
|
+
"Apr",
|
|
242
|
+
"May",
|
|
243
|
+
"Jun",
|
|
244
|
+
"Jul",
|
|
245
|
+
"Aug",
|
|
246
|
+
"Sep",
|
|
247
|
+
"Oct",
|
|
248
|
+
"Nov",
|
|
249
|
+
"Dec",
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// isDigit reports whether s[i] is in range and is a decimal digit.
|
|
253
|
+
func isDigit(s string, i int) bool {
|
|
254
|
+
if len(s) <= i {
|
|
255
|
+
return false
|
|
256
|
+
}
|
|
257
|
+
c := s[i]
|
|
258
|
+
return '0' <= c && c <= '9'
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// getnum parses s[0:1] or s[0:2] (fixed forces s[0:2])
|
|
262
|
+
// as a decimal integer and returns the integer and the
|
|
263
|
+
// remainder of the string.
|
|
264
|
+
func getnum(s string, fixed bool) (int, string, error) {
|
|
265
|
+
if !isDigit(s, 0) {
|
|
266
|
+
return 0, s, errBad
|
|
267
|
+
}
|
|
268
|
+
if !isDigit(s, 1) {
|
|
269
|
+
if fixed {
|
|
270
|
+
return 0, s, errBad
|
|
271
|
+
}
|
|
272
|
+
return int(s[0] - '0'), s[1:], nil
|
|
273
|
+
}
|
|
274
|
+
return int(s[0]-'0')*10 + int(s[1]-'0'), s[2:], nil
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// getnum3 parses s[0:1], s[0:2], or s[0:3] (fixed forces s[0:3])
|
|
278
|
+
// as a decimal integer and returns the integer and the remainder
|
|
279
|
+
// of the string.
|
|
280
|
+
func getnum3(s string, fixed bool) (int, string, error) {
|
|
281
|
+
var n, i int
|
|
282
|
+
for i = 0; i < 3 && isDigit(s, i); i++ {
|
|
283
|
+
n = n*10 + int(s[i]-'0')
|
|
284
|
+
}
|
|
285
|
+
if i == 0 || fixed && i != 3 {
|
|
286
|
+
return 0, s, errBad
|
|
287
|
+
}
|
|
288
|
+
return n, s[i:], nil
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
var errBad = errors.New("bad value for field") // not passed to the user
|
|
292
|
+
|
|
293
|
+
// stdFracSecond returns the std for a fractional second with n digits and the
|
|
294
|
+
// separator sep.
|
|
295
|
+
func stdFracSecond(code, n, c int) int {
|
|
296
|
+
// Use 0xfff to make the failure case even more absurd.
|
|
297
|
+
if c == '.' {
|
|
298
|
+
return code | ((n & 0xfff) << stdArgShift)
|
|
299
|
+
}
|
|
300
|
+
return code | ((n & 0xfff) << stdArgShift) | 1<<stdSeparatorShift
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
func digitsLen(std int) int {
|
|
304
|
+
return (std >> stdArgShift) & 0xfff
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
func separator(std int) byte {
|
|
308
|
+
if (std >> stdSeparatorShift) == 0 {
|
|
309
|
+
return '.'
|
|
310
|
+
}
|
|
311
|
+
return ','
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// appendInt appends the decimal form of x to b and returns the result.
|
|
315
|
+
// If the decimal form (excluding sign) is shorter than width, the result is padded with leading 0's.
|
|
316
|
+
// If x is negative, a leading '-' is added to the sign.
|
|
317
|
+
func appendInt(b []byte, x int, width int) []byte {
|
|
318
|
+
u := uint(x)
|
|
319
|
+
if x < 0 {
|
|
320
|
+
b = append(b, '-')
|
|
321
|
+
u = uint(-x)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// 2-digit and 4-digit fields are the most common in time formats.
|
|
325
|
+
utod := func(u uint) byte { return '0' + byte(u) }
|
|
326
|
+
switch {
|
|
327
|
+
case width == 2 && u < 100:
|
|
328
|
+
return append(b, utod(u/10), utod(u%10))
|
|
329
|
+
case width == 4 && u < 10000:
|
|
330
|
+
return append(b, utod(u/1000), utod(u/100%10), utod(u/10%10), utod(u%10))
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Compute the number of decimal digits.
|
|
334
|
+
var n int
|
|
335
|
+
if u == 0 {
|
|
336
|
+
n = 1
|
|
337
|
+
}
|
|
338
|
+
for u2 := u; u2 > 0; u2 = u2 / 10 {
|
|
339
|
+
n++
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Add 0-padding.
|
|
343
|
+
for pad := width - n; pad > 0; pad-- {
|
|
344
|
+
b = append(b, '0')
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Assemble the decimal in reverse order after the padding.
|
|
348
|
+
start := len(b)
|
|
349
|
+
b = append(b, make([]byte, n)...)
|
|
350
|
+
i := len(b) - 1
|
|
351
|
+
for u >= 10 && i > start {
|
|
352
|
+
q := u / 10
|
|
353
|
+
b[i] = utod(u - q*10)
|
|
354
|
+
u = q
|
|
355
|
+
i--
|
|
356
|
+
}
|
|
357
|
+
b[i] = utod(u)
|
|
358
|
+
return b
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// formatNano appends a fractional second, as nanoseconds, to b
|
|
362
|
+
// and returns the result. The nanosec must be within [0, 999999999].
|
|
363
|
+
func formatNano(b []byte, nanosec uint, std int) []byte {
|
|
364
|
+
var (
|
|
365
|
+
n = digitsLen(std)
|
|
366
|
+
separator = separator(std)
|
|
367
|
+
trim = std&stdMask == stdFracSecond9
|
|
368
|
+
)
|
|
369
|
+
u := nanosec
|
|
370
|
+
var buf [9]byte
|
|
371
|
+
for start := len(buf); start > 0; {
|
|
372
|
+
start--
|
|
373
|
+
buf[start] = byte(u%10 + '0')
|
|
374
|
+
u /= 10
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if n > 9 {
|
|
378
|
+
n = 9
|
|
379
|
+
}
|
|
380
|
+
if trim {
|
|
381
|
+
for n > 0 && buf[n-1] == '0' {
|
|
382
|
+
n--
|
|
383
|
+
}
|
|
384
|
+
if n == 0 {
|
|
385
|
+
return b
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
b = append(b, separator)
|
|
389
|
+
return append(b, buf[:n]...)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// String returns the time formatted using the format string
|
|
393
|
+
//
|
|
394
|
+
// "2006-01-02 15:04:05.999999999 -0700 MST"
|
|
395
|
+
func (t Time) String() string {
|
|
396
|
+
return t.Format("2006-01-02 15:04:05.999999999 -0700 MST")
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// GoString implements fmt.GoStringer and formats t to be printed in Go source
|
|
400
|
+
// code.
|
|
401
|
+
func (t Time) GoString() string {
|
|
402
|
+
abs := t.abs()
|
|
403
|
+
year, month, day, _ := absDate(abs)
|
|
404
|
+
hour, min, sec := t.Clock()
|
|
405
|
+
|
|
406
|
+
buf := make([]byte, 0, len("time.Date(9999, time.September, 31, 23, 59, 59, 999999999, time.Local)"))
|
|
407
|
+
buf = append(buf, "time.Date("...)
|
|
408
|
+
buf = appendInt(buf, year, 0)
|
|
409
|
+
if January <= month && month <= December {
|
|
410
|
+
buf = append(buf, ", time."...)
|
|
411
|
+
buf = append(buf, longMonthNames[month-1]...)
|
|
412
|
+
} else {
|
|
413
|
+
// It's difficult to construct a time.Time with a date outside the
|
|
414
|
+
// standard range but we might as well try to handle the case.
|
|
415
|
+
buf = appendInt(buf, int(month), 0)
|
|
416
|
+
}
|
|
417
|
+
buf = append(buf, ", "...)
|
|
418
|
+
buf = appendInt(buf, day, 0)
|
|
419
|
+
buf = append(buf, ", "...)
|
|
420
|
+
buf = appendInt(buf, hour, 0)
|
|
421
|
+
buf = append(buf, ", "...)
|
|
422
|
+
buf = appendInt(buf, min, 0)
|
|
423
|
+
buf = append(buf, ", "...)
|
|
424
|
+
buf = appendInt(buf, sec, 0)
|
|
425
|
+
buf = append(buf, ", "...)
|
|
426
|
+
buf = appendInt(buf, t.Nanosecond(), 0)
|
|
427
|
+
buf = append(buf, ", "...)
|
|
428
|
+
switch loc := t.Location(); loc {
|
|
429
|
+
case UTC:
|
|
430
|
+
buf = append(buf, "time.UTC"...)
|
|
431
|
+
case Local:
|
|
432
|
+
buf = append(buf, "time.Local"...)
|
|
433
|
+
default:
|
|
434
|
+
// there are several options for how we could display this, none of
|
|
435
|
+
// which are great:
|
|
436
|
+
//
|
|
437
|
+
// - use Location(loc.name), which is not technically valid syntax
|
|
438
|
+
// - use LoadLocation(loc.name), which will cause a syntax error when
|
|
439
|
+
// embedded and also would require us to escape the string without
|
|
440
|
+
// importing fmt or strconv
|
|
441
|
+
// - try to use FixedZone, which would also require escaping the name
|
|
442
|
+
// and would represent e.g. "America/Los_Angeles" daylight saving time
|
|
443
|
+
// shifts inaccurately
|
|
444
|
+
// - use the pointer format, which is no worse than you'd get with the
|
|
445
|
+
// old fmt.Sprintf("%#v", t) format.
|
|
446
|
+
//
|
|
447
|
+
// Of these, Location(loc.name) is the least disruptive. This is an edge
|
|
448
|
+
// case we hope not to hit too often.
|
|
449
|
+
buf = append(buf, `time.Location(`...)
|
|
450
|
+
buf = append(buf, quote(loc.name)...)
|
|
451
|
+
buf = append(buf, ')')
|
|
452
|
+
}
|
|
453
|
+
buf = append(buf, ')')
|
|
454
|
+
return string(buf)
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Format returns a textual representation of the time value formatted according
|
|
458
|
+
// to the layout defined by the argument. See the documentation for the
|
|
459
|
+
// constant called Layout to see how to represent the layout format.
|
|
460
|
+
func (t Time) Format(layout string) string {
|
|
461
|
+
const bufSize = 64
|
|
462
|
+
var b []byte
|
|
463
|
+
max := len(layout) + 10
|
|
464
|
+
if max < bufSize {
|
|
465
|
+
b = make([]byte, 0, bufSize)
|
|
466
|
+
} else {
|
|
467
|
+
b = make([]byte, 0, max)
|
|
468
|
+
}
|
|
469
|
+
b = t.AppendFormat(b, layout)
|
|
470
|
+
return string(b)
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// AppendFormat is like Format but appends the textual
|
|
474
|
+
// representation to b and returns the extended buffer.
|
|
475
|
+
func (t Time) AppendFormat(b []byte, layout string) []byte {
|
|
476
|
+
name, offset := t.zone()
|
|
477
|
+
abs := t.abs()
|
|
478
|
+
return t.appendFormat(b, layout, name, offset, abs)
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
func (t Time) appendFormat(b []byte, layout string, name string, offset int, abs int64) []byte {
|
|
482
|
+
var (
|
|
483
|
+
year int = -1
|
|
484
|
+
month Month
|
|
485
|
+
day int
|
|
486
|
+
yday int
|
|
487
|
+
hour int = -1
|
|
488
|
+
min int
|
|
489
|
+
sec int
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
// Each iteration generates one std value.
|
|
493
|
+
for layout != "" {
|
|
494
|
+
prefix, std, suffix := nextStdChunk(layout)
|
|
495
|
+
if prefix != "" {
|
|
496
|
+
b = append(b, prefix...)
|
|
497
|
+
}
|
|
498
|
+
if std == 0 {
|
|
499
|
+
break
|
|
500
|
+
}
|
|
501
|
+
layout = suffix
|
|
502
|
+
|
|
503
|
+
// Compute year, month, day if needed.
|
|
504
|
+
if year < 0 && std&stdNeedDate != 0 {
|
|
505
|
+
year, month, day, yday = absDate(abs)
|
|
506
|
+
yday++
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Compute hour, minute, second if needed.
|
|
510
|
+
if hour < 0 && std&stdNeedClock != 0 {
|
|
511
|
+
s := int(abs - floorDiv(abs, secondsPerDay)*secondsPerDay)
|
|
512
|
+
hour = s / secondsPerHour
|
|
513
|
+
s -= hour * secondsPerHour
|
|
514
|
+
min = s / secondsPerMinute
|
|
515
|
+
sec = s - min*secondsPerMinute
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
switch std & stdMask {
|
|
519
|
+
case stdYear:
|
|
520
|
+
y := year
|
|
521
|
+
if y < 0 {
|
|
522
|
+
y = -y
|
|
523
|
+
}
|
|
524
|
+
b = appendInt(b, y%100, 2)
|
|
525
|
+
case stdLongYear:
|
|
526
|
+
b = appendInt(b, year, 4)
|
|
527
|
+
case stdMonth:
|
|
528
|
+
b = append(b, shortMonthNames[month-1]...)
|
|
529
|
+
case stdLongMonth:
|
|
530
|
+
m := month.String()
|
|
531
|
+
b = append(b, m...)
|
|
532
|
+
case stdNumMonth:
|
|
533
|
+
b = appendInt(b, int(month), 0)
|
|
534
|
+
case stdZeroMonth:
|
|
535
|
+
b = appendInt(b, int(month), 2)
|
|
536
|
+
case stdWeekDay:
|
|
537
|
+
b = append(b, shortDayNames[absWeekday(abs)]...)
|
|
538
|
+
case stdLongWeekDay:
|
|
539
|
+
s := absWeekday(abs).String()
|
|
540
|
+
b = append(b, s...)
|
|
541
|
+
case stdDay:
|
|
542
|
+
b = appendInt(b, day, 0)
|
|
543
|
+
case stdUnderDay:
|
|
544
|
+
if day < 10 {
|
|
545
|
+
b = append(b, ' ')
|
|
546
|
+
}
|
|
547
|
+
b = appendInt(b, day, 0)
|
|
548
|
+
case stdZeroDay:
|
|
549
|
+
b = appendInt(b, day, 2)
|
|
550
|
+
case stdUnderYearDay:
|
|
551
|
+
if yday < 100 {
|
|
552
|
+
b = append(b, ' ')
|
|
553
|
+
if yday < 10 {
|
|
554
|
+
b = append(b, ' ')
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
b = appendInt(b, yday, 0)
|
|
558
|
+
case stdZeroYearDay:
|
|
559
|
+
b = appendInt(b, yday, 3)
|
|
560
|
+
case stdHour:
|
|
561
|
+
b = appendInt(b, hour, 2)
|
|
562
|
+
case stdHour12:
|
|
563
|
+
// Noon is 12PM, midnight is 12AM.
|
|
564
|
+
hr := hour % 12
|
|
565
|
+
if hr == 0 {
|
|
566
|
+
hr = 12
|
|
567
|
+
}
|
|
568
|
+
b = appendInt(b, hr, 0)
|
|
569
|
+
case stdZeroHour12:
|
|
570
|
+
// Noon is 12PM, midnight is 12AM.
|
|
571
|
+
hr := hour % 12
|
|
572
|
+
if hr == 0 {
|
|
573
|
+
hr = 12
|
|
574
|
+
}
|
|
575
|
+
b = appendInt(b, hr, 2)
|
|
576
|
+
case stdMinute:
|
|
577
|
+
b = appendInt(b, min, 0)
|
|
578
|
+
case stdZeroMinute:
|
|
579
|
+
b = appendInt(b, min, 2)
|
|
580
|
+
case stdSecond:
|
|
581
|
+
b = appendInt(b, sec, 0)
|
|
582
|
+
case stdZeroSecond:
|
|
583
|
+
b = appendInt(b, sec, 2)
|
|
584
|
+
case stdPM:
|
|
585
|
+
if hour >= 12 {
|
|
586
|
+
b = append(b, "PM"...)
|
|
587
|
+
} else {
|
|
588
|
+
b = append(b, "AM"...)
|
|
589
|
+
}
|
|
590
|
+
case stdpm:
|
|
591
|
+
if hour >= 12 {
|
|
592
|
+
b = append(b, "pm"...)
|
|
593
|
+
} else {
|
|
594
|
+
b = append(b, "am"...)
|
|
595
|
+
}
|
|
596
|
+
case stdISO8601TZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ShortTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumColonTZ, stdNumSecondsTz, stdNumShortTZ, stdNumColonSecondsTZ:
|
|
597
|
+
// Ugly special case. We cheat and take the "Z" variants
|
|
598
|
+
// to mean "the time zone as formatted for ISO 8601".
|
|
599
|
+
if offset == 0 && (std == stdISO8601TZ || std == stdISO8601ColonTZ || std == stdISO8601SecondsTZ || std == stdISO8601ShortTZ || std == stdISO8601ColonSecondsTZ) {
|
|
600
|
+
b = append(b, 'Z')
|
|
601
|
+
break
|
|
602
|
+
}
|
|
603
|
+
zone := offset / 60 // convert to minutes
|
|
604
|
+
absoffset := offset
|
|
605
|
+
if zone < 0 {
|
|
606
|
+
b = append(b, '-')
|
|
607
|
+
zone = -zone
|
|
608
|
+
absoffset = -absoffset
|
|
609
|
+
} else {
|
|
610
|
+
b = append(b, '+')
|
|
611
|
+
}
|
|
612
|
+
b = appendInt(b, zone/60, 2)
|
|
613
|
+
if std == stdISO8601ColonTZ || std == stdNumColonTZ || std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ {
|
|
614
|
+
b = append(b, ':')
|
|
615
|
+
}
|
|
616
|
+
if std != stdNumShortTZ && std != stdISO8601ShortTZ {
|
|
617
|
+
b = appendInt(b, zone%60, 2)
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// append seconds if appropriate
|
|
621
|
+
if std == stdISO8601SecondsTZ || std == stdNumSecondsTz || std == stdNumColonSecondsTZ || std == stdISO8601ColonSecondsTZ {
|
|
622
|
+
if std == stdNumColonSecondsTZ || std == stdISO8601ColonSecondsTZ {
|
|
623
|
+
b = append(b, ':')
|
|
624
|
+
}
|
|
625
|
+
b = appendInt(b, absoffset%60, 2)
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
case stdTZ:
|
|
629
|
+
// Ugly special case. We cheat and take the "MST" variant
|
|
630
|
+
// to mean "the time zone abbreviation".
|
|
631
|
+
if name != "" {
|
|
632
|
+
b = append(b, name...)
|
|
633
|
+
break
|
|
634
|
+
}
|
|
635
|
+
// No time zone known for this time, but we must print one.
|
|
636
|
+
// Use the -0700 format.
|
|
637
|
+
zone := offset / 60 // convert to minutes
|
|
638
|
+
if zone < 0 {
|
|
639
|
+
b = append(b, '-')
|
|
640
|
+
zone = -zone
|
|
641
|
+
} else {
|
|
642
|
+
b = append(b, '+')
|
|
643
|
+
}
|
|
644
|
+
b = appendInt(b, zone/60, 2)
|
|
645
|
+
b = appendInt(b, zone%60, 2)
|
|
646
|
+
case stdFracSecond0, stdFracSecond9:
|
|
647
|
+
b = formatNano(b, uint(t.nsec), std)
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return b
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
var errAppendBadYear = errors.New("year outside of range [0,9999]")
|
|
654
|
+
var errAppendBadZone = errors.New("timezone hour outside of range [0,23]")
|
|
655
|
+
|
|
656
|
+
// appendStrictRFC3339 formats t as RFC 3339, rejecting what that format can
|
|
657
|
+
// not represent.
|
|
658
|
+
func (t Time) appendStrictRFC3339(b []byte) ([]byte, error) {
|
|
659
|
+
if y := t.Year(); y < 0 || y >= 10000 {
|
|
660
|
+
return b, errAppendBadYear
|
|
661
|
+
}
|
|
662
|
+
_, offset := t.zone()
|
|
663
|
+
if offset <= -24*3600 || offset >= 24*3600 {
|
|
664
|
+
return b, errAppendBadZone
|
|
665
|
+
}
|
|
666
|
+
b = t.AppendFormat(b, RFC3339Nano)
|
|
667
|
+
return b, nil
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// MarshalJSON implements the encoding/json.Marshaler interface. The time is a
|
|
671
|
+
// quoted string in the RFC 3339 format with sub-second precision. If the
|
|
672
|
+
// timestamp cannot be represented as valid RFC 3339 (e.g., the year is out of
|
|
673
|
+
// range), then an error is reported.
|
|
674
|
+
func (t Time) MarshalJSON() ([]byte, error) {
|
|
675
|
+
b := make([]byte, 0, len(RFC3339Nano)+len(`""`))
|
|
676
|
+
b = append(b, '"')
|
|
677
|
+
b, err := t.appendStrictRFC3339(b)
|
|
678
|
+
b = append(b, '"')
|
|
679
|
+
if err != nil {
|
|
680
|
+
return nil, errors.New("Time.MarshalJSON: " + err.Error())
|
|
681
|
+
}
|
|
682
|
+
return b, nil
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// UnmarshalJSON implements the encoding/json.Unmarshaler interface. The time
|
|
686
|
+
// must be a quoted string in the RFC 3339 format.
|
|
687
|
+
func (t *Time) UnmarshalJSON(data []byte) error {
|
|
688
|
+
if string(data) == "null" {
|
|
689
|
+
return nil
|
|
690
|
+
}
|
|
691
|
+
if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' {
|
|
692
|
+
return errors.New("Time.UnmarshalJSON: input is not a JSON string")
|
|
693
|
+
}
|
|
694
|
+
data = data[len(`"`) : len(data)-len(`"`)]
|
|
695
|
+
parsed, err := parseStrictRFC3339(data)
|
|
696
|
+
*t = parsed
|
|
697
|
+
return err
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// MarshalText implements the encoding.TextMarshaler interface. The time is
|
|
701
|
+
// formatted in RFC 3339 format with sub-second precision. If the timestamp
|
|
702
|
+
// cannot be represented as valid RFC 3339 (e.g., the year is out of range),
|
|
703
|
+
// then an error is returned.
|
|
704
|
+
func (t Time) MarshalText() ([]byte, error) {
|
|
705
|
+
b := make([]byte, 0, len(RFC3339Nano))
|
|
706
|
+
b, err := t.appendStrictRFC3339(b)
|
|
707
|
+
if err != nil {
|
|
708
|
+
return nil, errors.New("Time.MarshalText: " + err.Error())
|
|
709
|
+
}
|
|
710
|
+
return b, nil
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// UnmarshalText implements the encoding.TextUnmarshaler interface. The time
|
|
714
|
+
// must be in the RFC 3339 format.
|
|
715
|
+
func (t *Time) UnmarshalText(data []byte) error {
|
|
716
|
+
parsed, err := parseStrictRFC3339(data)
|
|
717
|
+
*t = parsed
|
|
718
|
+
return err
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
func parseStrictRFC3339(b []byte) (Time, error) {
|
|
722
|
+
t, err := Parse(RFC3339, string(b))
|
|
723
|
+
if err != nil {
|
|
724
|
+
return Time{}, err
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// The parse template syntax cannot correctly validate RFC 3339.
|
|
728
|
+
// Explicitly check for cases that Parse is unable to validate for.
|
|
729
|
+
// See https://go.dev/issue/54580.
|
|
730
|
+
num2 := func(b []byte) byte { return 10*(b[0]-'0') + (b[1] - '0') }
|
|
731
|
+
switch {
|
|
732
|
+
case b[len("2006-01-02T")+1] == ':': // hour must be two digits
|
|
733
|
+
return Time{}, &ParseError{RFC3339, string(b), "15", string(b[len("2006-01-02T"):][:1]), ""}
|
|
734
|
+
case b[len("2006-01-02T15:04:05")] == ',': // sub-second separator must be a period
|
|
735
|
+
return Time{}, &ParseError{RFC3339, string(b), ".", ",", ""}
|
|
736
|
+
case b[len(b)-1] != 'Z':
|
|
737
|
+
switch {
|
|
738
|
+
case num2(b[len(b)-len("07:00"):]) >= 24: // timezone hour must be in range
|
|
739
|
+
return Time{}, &ParseError{RFC3339, string(b), "Z07:00", string(b[len(b)-len("Z07:00"):]), ": timezone hour out of range"}
|
|
740
|
+
case num2(b[len(b)-len("00"):]) >= 60: // timezone minute must be in range
|
|
741
|
+
return Time{}, &ParseError{RFC3339, string(b), "minute", string(b[len(b)-len("00"):]), ""}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return t, nil
|
|
745
|
+
}
|