gofront 1.8.0 → 1.9.1
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 +6 -5
- package/src/backend/js/expressions.js +1157 -408
- package/src/backend/js/index.js +327 -153
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +559 -2552
- package/src/backend/js/statements.js +670 -158
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +298 -19
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +91 -170
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +884 -256
- package/src/backend/wasm/emit-exprs.js +185 -77
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-shared.js +4 -1
- package/src/backend/wasm/emit-stmts.js +524 -240
- package/src/backend/wasm/emit.js +176 -38
- package/src/backend/wasm/globals.js +48 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +44 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +299 -770
- package/src/backend/wasm/monomorph.js +139 -8
- package/src/backend/wasm/natives.js +600 -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 +97 -20
- package/src/backend/wasm/variadic.js +9 -4
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +113 -130
- package/src/lexer/lexer.js +106 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +355 -132
- package/src/link/type-names.js +204 -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 +62 -5
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +929 -0
- package/src/lower/functions.js +225 -7
- package/src/lower/gen-types.js +293 -0
- package/src/lower/index.js +14 -2
- package/src/lower/json-codecs.js +334 -245
- 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 +30 -5
- package/src/parser/expressions.js +111 -57
- package/src/parser/statements.js +108 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +12 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +214 -19
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +156 -8
- package/src/typechecker/expressions.js +490 -108
- package/src/typechecker/index.js +346 -42
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +41 -0
- package/src/typechecker/resolve.js +490 -65
- package/src/typechecker/statements.js +288 -27
- 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 +287 -575
- 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,790 @@
|
|
|
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
|
+
var errLeadingInt = errors.New("time: bad [0-9]*") // never printed
|
|
14
|
+
|
|
15
|
+
var shortDayNames = []string{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}
|
|
16
|
+
|
|
17
|
+
// lookup returns the index of the entry of tab that value starts with, case
|
|
18
|
+
// insensitively, and the rest of value.
|
|
19
|
+
func lookup(tab []string, val string) (int, string, error) {
|
|
20
|
+
for i, v := range tab {
|
|
21
|
+
if len(val) >= len(v) && match(val[0:len(v)], v) {
|
|
22
|
+
return i, val[len(v):], nil
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return -1, val, errBad
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// match reports whether s1 and s2 match ignoring case.
|
|
29
|
+
// It is assumed s1 and s2 are the same length.
|
|
30
|
+
func match(s1, s2 string) bool {
|
|
31
|
+
for i := 0; i < len(s1); i++ {
|
|
32
|
+
c1 := s1[i]
|
|
33
|
+
c2 := s2[i]
|
|
34
|
+
if c1 != c2 {
|
|
35
|
+
// Switch to lower-case; 'a'-'A' is known to be a single bit.
|
|
36
|
+
c1 |= 32
|
|
37
|
+
c2 |= 32
|
|
38
|
+
if c1 != c2 || c1 < 'a' || c1 > 'z' {
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
func commaOrPeriod(b byte) bool {
|
|
47
|
+
return b == '.' || b == ','
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) {
|
|
51
|
+
if !commaOrPeriod(value[0]) {
|
|
52
|
+
err = errBad
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
if nbytes > 10 {
|
|
56
|
+
value = value[:10]
|
|
57
|
+
nbytes = 10
|
|
58
|
+
}
|
|
59
|
+
ns, err = atoi(value[1:nbytes])
|
|
60
|
+
if err != nil {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
if ns < 0 {
|
|
64
|
+
rangeErrString = "fractional second"
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
// We need nanoseconds, which means scaling by the number
|
|
68
|
+
// of digits in the format string.
|
|
69
|
+
scaleDigits := 10 - nbytes
|
|
70
|
+
for i := 0; i < scaleDigits; i++ {
|
|
71
|
+
ns *= 10
|
|
72
|
+
}
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// atoi parses a decimal integer with an optional sign.
|
|
77
|
+
func atoi(s string) (x int, err error) {
|
|
78
|
+
neg := false
|
|
79
|
+
if s != "" && (s[0] == '-' || s[0] == '+') {
|
|
80
|
+
neg = s[0] == '-'
|
|
81
|
+
s = s[1:]
|
|
82
|
+
}
|
|
83
|
+
q, rem, err := leadingInt(s)
|
|
84
|
+
x = int(q)
|
|
85
|
+
if err != nil || rem != "" {
|
|
86
|
+
return 0, errBad
|
|
87
|
+
}
|
|
88
|
+
if neg {
|
|
89
|
+
x = -x
|
|
90
|
+
}
|
|
91
|
+
return x, nil
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// leadingInt consumes the leading [0-9]* from s.
|
|
95
|
+
func leadingInt(s string) (x uint64, rem string, err error) {
|
|
96
|
+
var limit uint64 = 1 << 63
|
|
97
|
+
i := 0
|
|
98
|
+
for ; i < len(s); i++ {
|
|
99
|
+
c := s[i]
|
|
100
|
+
if c < '0' || c > '9' {
|
|
101
|
+
break
|
|
102
|
+
}
|
|
103
|
+
if x > limit/10 {
|
|
104
|
+
// overflow
|
|
105
|
+
return 0, "", errLeadingInt
|
|
106
|
+
}
|
|
107
|
+
x = x*10 + uint64(c) - '0'
|
|
108
|
+
if x > limit {
|
|
109
|
+
// overflow
|
|
110
|
+
return 0, "", errLeadingInt
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return x, s[i:], nil
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// leadingFraction consumes the leading [0-9]* from s.
|
|
117
|
+
// It is used only for fractions, so does not return an error on overflow,
|
|
118
|
+
// it just stops accumulating precision.
|
|
119
|
+
func leadingFraction(s string) (x uint64, scale float64, rem string) {
|
|
120
|
+
var limit uint64 = 1 << 63
|
|
121
|
+
i := 0
|
|
122
|
+
scale = 1
|
|
123
|
+
overflow := false
|
|
124
|
+
for ; i < len(s); i++ {
|
|
125
|
+
c := s[i]
|
|
126
|
+
if c < '0' || c > '9' {
|
|
127
|
+
break
|
|
128
|
+
}
|
|
129
|
+
if overflow {
|
|
130
|
+
continue
|
|
131
|
+
}
|
|
132
|
+
if x > (limit-1)/10 {
|
|
133
|
+
// It's possible for overflow to give a positive number, so take care.
|
|
134
|
+
overflow = true
|
|
135
|
+
continue
|
|
136
|
+
}
|
|
137
|
+
y := x*10 + uint64(c) - '0'
|
|
138
|
+
if y > limit {
|
|
139
|
+
overflow = true
|
|
140
|
+
continue
|
|
141
|
+
}
|
|
142
|
+
x = y
|
|
143
|
+
scale *= 10
|
|
144
|
+
}
|
|
145
|
+
return x, scale, s[i:]
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
func unitValue(u string) (uint64, bool) {
|
|
149
|
+
switch u {
|
|
150
|
+
case "ns":
|
|
151
|
+
return uint64(Nanosecond), true
|
|
152
|
+
case "us", "µs", "μs": // U+00B5 = micro symbol, U+03BC = Greek letter mu
|
|
153
|
+
return uint64(Microsecond), true
|
|
154
|
+
case "ms":
|
|
155
|
+
return uint64(Millisecond), true
|
|
156
|
+
case "s":
|
|
157
|
+
return uint64(Second), true
|
|
158
|
+
case "m":
|
|
159
|
+
return uint64(Minute), true
|
|
160
|
+
case "h":
|
|
161
|
+
return uint64(Hour), true
|
|
162
|
+
}
|
|
163
|
+
return 0, false
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ParseDuration parses a duration string.
|
|
167
|
+
// A duration string is a possibly signed sequence of
|
|
168
|
+
// decimal numbers, each with optional fraction and a unit suffix,
|
|
169
|
+
// such as "300ms", "-1.5h" or "2h45m".
|
|
170
|
+
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
|
171
|
+
func ParseDuration(s string) (Duration, error) {
|
|
172
|
+
// [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
|
|
173
|
+
orig := s
|
|
174
|
+
var d uint64
|
|
175
|
+
var limit uint64 = 1 << 63
|
|
176
|
+
neg := false
|
|
177
|
+
|
|
178
|
+
// Consume [-+]?
|
|
179
|
+
if s != "" {
|
|
180
|
+
c := s[0]
|
|
181
|
+
if c == '-' || c == '+' {
|
|
182
|
+
neg = c == '-'
|
|
183
|
+
s = s[1:]
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Special case: if all that is left is "0", this is zero.
|
|
187
|
+
if s == "0" {
|
|
188
|
+
return 0, nil
|
|
189
|
+
}
|
|
190
|
+
if s == "" {
|
|
191
|
+
return 0, errors.New("time: invalid duration " + quote(orig))
|
|
192
|
+
}
|
|
193
|
+
for s != "" {
|
|
194
|
+
var (
|
|
195
|
+
v, f uint64 // integers before, after decimal point
|
|
196
|
+
scale float64 = 1 // value = v + f/scale
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
var err error
|
|
200
|
+
|
|
201
|
+
// The next character must be [0-9.]
|
|
202
|
+
if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') {
|
|
203
|
+
return 0, errors.New("time: invalid duration " + quote(orig))
|
|
204
|
+
}
|
|
205
|
+
// Consume [0-9]*
|
|
206
|
+
pl := len(s)
|
|
207
|
+
v, s, err = leadingInt(s)
|
|
208
|
+
if err != nil {
|
|
209
|
+
return 0, errors.New("time: invalid duration " + quote(orig))
|
|
210
|
+
}
|
|
211
|
+
pre := pl != len(s) // whether we consumed anything before a period
|
|
212
|
+
|
|
213
|
+
// Consume (\.[0-9]*)?
|
|
214
|
+
post := false
|
|
215
|
+
if s != "" && s[0] == '.' {
|
|
216
|
+
s = s[1:]
|
|
217
|
+
pl := len(s)
|
|
218
|
+
f, scale, s = leadingFraction(s)
|
|
219
|
+
post = pl != len(s)
|
|
220
|
+
}
|
|
221
|
+
if !pre && !post {
|
|
222
|
+
// no digits (e.g. ".s" or "-.s")
|
|
223
|
+
return 0, errors.New("time: invalid duration " + quote(orig))
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Consume unit.
|
|
227
|
+
i := 0
|
|
228
|
+
for ; i < len(s); i++ {
|
|
229
|
+
c := s[i]
|
|
230
|
+
if c == '.' || '0' <= c && c <= '9' {
|
|
231
|
+
break
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if i == 0 {
|
|
235
|
+
return 0, errors.New("time: missing unit in duration " + quote(orig))
|
|
236
|
+
}
|
|
237
|
+
u := s[:i]
|
|
238
|
+
s = s[i:]
|
|
239
|
+
unit, ok := unitValue(u)
|
|
240
|
+
if !ok {
|
|
241
|
+
return 0, errors.New("time: unknown unit " + quote(u) + " in duration " + quote(orig))
|
|
242
|
+
}
|
|
243
|
+
if v > limit/unit {
|
|
244
|
+
// overflow
|
|
245
|
+
return 0, errors.New("time: invalid duration " + quote(orig))
|
|
246
|
+
}
|
|
247
|
+
v *= unit
|
|
248
|
+
if f > 0 {
|
|
249
|
+
// float64 is needed to be nanosecond accurate for fractions of hours.
|
|
250
|
+
// v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit)
|
|
251
|
+
v += uint64(float64(f) * (float64(unit) / scale))
|
|
252
|
+
if v > limit {
|
|
253
|
+
// overflow
|
|
254
|
+
return 0, errors.New("time: invalid duration " + quote(orig))
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
d += v
|
|
258
|
+
if d > limit {
|
|
259
|
+
return 0, errors.New("time: invalid duration " + quote(orig))
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if neg {
|
|
263
|
+
return -Duration(d), nil
|
|
264
|
+
}
|
|
265
|
+
if d > limit-1 {
|
|
266
|
+
return 0, errors.New("time: invalid duration " + quote(orig))
|
|
267
|
+
}
|
|
268
|
+
return Duration(d), nil
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// skip removes the given prefix from value,
|
|
272
|
+
// treating runs of space characters as equivalent.
|
|
273
|
+
func skip(value, prefix string) (string, error) {
|
|
274
|
+
for len(prefix) > 0 {
|
|
275
|
+
if prefix[0] == ' ' {
|
|
276
|
+
if len(value) > 0 && value[0] != ' ' {
|
|
277
|
+
return value, errBad
|
|
278
|
+
}
|
|
279
|
+
prefix = cutspace(prefix)
|
|
280
|
+
value = cutspace(value)
|
|
281
|
+
continue
|
|
282
|
+
}
|
|
283
|
+
if len(value) == 0 || value[0] != prefix[0] {
|
|
284
|
+
return value, errBad
|
|
285
|
+
}
|
|
286
|
+
prefix = prefix[1:]
|
|
287
|
+
value = value[1:]
|
|
288
|
+
}
|
|
289
|
+
return value, nil
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
func cutspace(s string) string {
|
|
293
|
+
for len(s) > 0 && s[0] == ' ' {
|
|
294
|
+
s = s[1:]
|
|
295
|
+
}
|
|
296
|
+
return s
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// daysIn returns the number of days in a month for a given year.
|
|
300
|
+
func daysIn(m Month, year int) int {
|
|
301
|
+
if m == February && isLeap(year) {
|
|
302
|
+
return 29
|
|
303
|
+
}
|
|
304
|
+
return daysBefore[m] - daysBefore[m-1]
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
func newParseError(layout, value, layoutElem, valueElem, message string) *ParseError {
|
|
308
|
+
return &ParseError{layout, value, layoutElem, valueElem, message}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Parse parses a formatted string and returns the time value it represents.
|
|
312
|
+
// See the documentation for the constant called Layout to see how to
|
|
313
|
+
// represent the format.
|
|
314
|
+
//
|
|
315
|
+
// In the absence of a time zone indicator, Parse returns a time in UTC.
|
|
316
|
+
//
|
|
317
|
+
// When parsing a time with a zone offset like -0700, if the offset
|
|
318
|
+
// corresponds to a time zone used by the current location (Local), then Parse
|
|
319
|
+
// uses that location and zone in the returned time. Otherwise it records the
|
|
320
|
+
// time as being in a fabricated location with time fixed at the given zone
|
|
321
|
+
// offset.
|
|
322
|
+
func Parse(layout, value string) (Time, error) {
|
|
323
|
+
return parse(layout, value, UTC, Local)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// ParseInLocation is like Parse but differs in two important ways. First, in
|
|
327
|
+
// the absence of time zone information, Parse interprets a time as UTC;
|
|
328
|
+
// ParseInLocation interprets the time as in the given location. Second, when
|
|
329
|
+
// given a zone offset or abbreviation, Parse tries to match it against the
|
|
330
|
+
// Local location; ParseInLocation uses the given location.
|
|
331
|
+
func ParseInLocation(layout, value string, loc *Location) (Time, error) {
|
|
332
|
+
return parse(layout, value, loc, loc)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
func parseTimeZone(value string) (length int, ok bool) {
|
|
336
|
+
if len(value) < 3 {
|
|
337
|
+
return 0, false
|
|
338
|
+
}
|
|
339
|
+
// Special case 1: ChST and MeST.
|
|
340
|
+
if len(value) >= 4 && (value[:4] == "ChST" || value[:4] == "MeST") {
|
|
341
|
+
return 4, true
|
|
342
|
+
}
|
|
343
|
+
// Special case 2: GMT may have an hour offset; treat it specially.
|
|
344
|
+
if value[:3] == "GMT" {
|
|
345
|
+
length = parseGMT(value)
|
|
346
|
+
return length, true
|
|
347
|
+
}
|
|
348
|
+
// Special Case 3: Some time zones are not named, but have +/-00 format
|
|
349
|
+
if value[0] == '+' || value[0] == '-' {
|
|
350
|
+
length = parseSignedOffset(value)
|
|
351
|
+
ok := length > 0 // parseSignedOffset returns 0 in case of bad input
|
|
352
|
+
return length, ok
|
|
353
|
+
}
|
|
354
|
+
// How many upper-case letters are there? Need at least three, at most five.
|
|
355
|
+
var nUpper int
|
|
356
|
+
for nUpper = 0; nUpper < 6; nUpper++ {
|
|
357
|
+
if nUpper >= len(value) {
|
|
358
|
+
break
|
|
359
|
+
}
|
|
360
|
+
if c := value[nUpper]; c < 'A' || 'Z' < c {
|
|
361
|
+
break
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
switch nUpper {
|
|
365
|
+
case 0, 1, 2, 6:
|
|
366
|
+
return 0, false
|
|
367
|
+
case 5: // Must end in T to match.
|
|
368
|
+
if value[4] == 'T' {
|
|
369
|
+
return 5, true
|
|
370
|
+
}
|
|
371
|
+
case 4:
|
|
372
|
+
// Must end in T, except one special case.
|
|
373
|
+
if value[3] == 'T' || value[:4] == "WITA" {
|
|
374
|
+
return 4, true
|
|
375
|
+
}
|
|
376
|
+
case 3:
|
|
377
|
+
return 3, true
|
|
378
|
+
}
|
|
379
|
+
return 0, false
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// parseGMT parses a GMT time zone. The input string is known to start "GMT".
|
|
383
|
+
// The function checks whether that is followed by a sign and a number in the
|
|
384
|
+
// range -23 through +23 excluding zero.
|
|
385
|
+
func parseGMT(value string) int {
|
|
386
|
+
value = value[3:]
|
|
387
|
+
if len(value) == 0 {
|
|
388
|
+
return 3
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return 3 + parseSignedOffset(value)
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// parseSignedOffset parses a signed timezone offset (e.g. "+03" or "-04").
|
|
395
|
+
// The function checks for a signed number in the range -23 through +23
|
|
396
|
+
// excluding zero. Returns length of the found offset string or 0 otherwise.
|
|
397
|
+
func parseSignedOffset(value string) int {
|
|
398
|
+
sign := value[0]
|
|
399
|
+
if sign != '-' && sign != '+' {
|
|
400
|
+
return 0
|
|
401
|
+
}
|
|
402
|
+
x, rem, err := leadingInt(value[1:])
|
|
403
|
+
|
|
404
|
+
// fail if nothing consumed by leadingInt
|
|
405
|
+
if err != nil || value[1:] == rem {
|
|
406
|
+
return 0
|
|
407
|
+
}
|
|
408
|
+
if x > 23 {
|
|
409
|
+
return 0
|
|
410
|
+
}
|
|
411
|
+
return len(value) - len(rem)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
func parse(layout, value string, defaultLocation, local *Location) (Time, error) {
|
|
415
|
+
alayout, avalue := layout, value
|
|
416
|
+
rangeErrString := "" // set if a value is out of range
|
|
417
|
+
amSet := false // do we need to subtract 12 from the hour for midnight?
|
|
418
|
+
pmSet := false // do we need to add 12 to the hour?
|
|
419
|
+
|
|
420
|
+
// Time being constructed.
|
|
421
|
+
var (
|
|
422
|
+
year int
|
|
423
|
+
month int = -1
|
|
424
|
+
day int = -1
|
|
425
|
+
yday int = -1
|
|
426
|
+
hour int
|
|
427
|
+
min int
|
|
428
|
+
sec int
|
|
429
|
+
nsec int
|
|
430
|
+
z *Location
|
|
431
|
+
zoneOffset int = -1
|
|
432
|
+
zoneName string
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
// Each iteration processes one std value.
|
|
436
|
+
for {
|
|
437
|
+
var err error
|
|
438
|
+
prefix, std, suffix := nextStdChunk(layout)
|
|
439
|
+
stdstr := layout[len(prefix) : len(layout)-len(suffix)]
|
|
440
|
+
value, err = skip(value, prefix)
|
|
441
|
+
if err != nil {
|
|
442
|
+
return Time{}, newParseError(alayout, avalue, prefix, value, "")
|
|
443
|
+
}
|
|
444
|
+
if std == 0 {
|
|
445
|
+
if len(value) != 0 {
|
|
446
|
+
return Time{}, newParseError(alayout, avalue, "", value, ": extra text: "+quote(value))
|
|
447
|
+
}
|
|
448
|
+
break
|
|
449
|
+
}
|
|
450
|
+
layout = suffix
|
|
451
|
+
var p string
|
|
452
|
+
hold := value
|
|
453
|
+
switch std & stdMask {
|
|
454
|
+
case stdYear:
|
|
455
|
+
if len(value) < 2 {
|
|
456
|
+
err = errBad
|
|
457
|
+
break
|
|
458
|
+
}
|
|
459
|
+
hold = value
|
|
460
|
+
p, value = value[0:2], value[2:]
|
|
461
|
+
year, err = atoi(p)
|
|
462
|
+
if err != nil {
|
|
463
|
+
value = hold
|
|
464
|
+
} else if year >= 69 { // Unix time starts Dec 31 1969 in some time zones
|
|
465
|
+
year += 1900
|
|
466
|
+
} else {
|
|
467
|
+
year += 2000
|
|
468
|
+
}
|
|
469
|
+
case stdLongYear:
|
|
470
|
+
if len(value) < 4 || !isDigit(value, 0) {
|
|
471
|
+
err = errBad
|
|
472
|
+
break
|
|
473
|
+
}
|
|
474
|
+
p, value = value[0:4], value[4:]
|
|
475
|
+
year, err = atoi(p)
|
|
476
|
+
case stdMonth:
|
|
477
|
+
month, value, err = lookup(shortMonthNames, value)
|
|
478
|
+
month++
|
|
479
|
+
case stdLongMonth:
|
|
480
|
+
month, value, err = lookup(longMonthNames, value)
|
|
481
|
+
month++
|
|
482
|
+
case stdNumMonth, stdZeroMonth:
|
|
483
|
+
month, value, err = getnum(value, std == stdZeroMonth)
|
|
484
|
+
if err == nil && (month <= 0 || 12 < month) {
|
|
485
|
+
rangeErrString = "month"
|
|
486
|
+
}
|
|
487
|
+
case stdWeekDay:
|
|
488
|
+
// Ignore weekday except for error checking.
|
|
489
|
+
_, value, err = lookup(shortDayNames, value)
|
|
490
|
+
case stdLongWeekDay:
|
|
491
|
+
_, value, err = lookup(longDayNames, value)
|
|
492
|
+
case stdDay, stdUnderDay, stdZeroDay:
|
|
493
|
+
if std == stdUnderDay && len(value) > 0 && value[0] == ' ' {
|
|
494
|
+
value = value[1:]
|
|
495
|
+
}
|
|
496
|
+
day, value, err = getnum(value, std == stdZeroDay)
|
|
497
|
+
// Note that we allow any one- or two-digit day here.
|
|
498
|
+
// The month, day, year combination is validated after we've completed parsing.
|
|
499
|
+
case stdUnderYearDay, stdZeroYearDay:
|
|
500
|
+
for i := 0; i < 2; i++ {
|
|
501
|
+
if std == stdUnderYearDay && len(value) > 0 && value[0] == ' ' {
|
|
502
|
+
value = value[1:]
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
yday, value, err = getnum3(value, std == stdZeroYearDay)
|
|
506
|
+
// Note that we allow any one-, two-, or three-digit year-day here.
|
|
507
|
+
// The year-day, year combination is validated after we've completed parsing.
|
|
508
|
+
case stdHour:
|
|
509
|
+
hour, value, err = getnum(value, false)
|
|
510
|
+
if hour < 0 || 24 <= hour {
|
|
511
|
+
rangeErrString = "hour"
|
|
512
|
+
}
|
|
513
|
+
case stdHour12, stdZeroHour12:
|
|
514
|
+
hour, value, err = getnum(value, std == stdZeroHour12)
|
|
515
|
+
if hour < 0 || 12 < hour {
|
|
516
|
+
rangeErrString = "hour"
|
|
517
|
+
}
|
|
518
|
+
case stdMinute, stdZeroMinute:
|
|
519
|
+
min, value, err = getnum(value, std == stdZeroMinute)
|
|
520
|
+
if min < 0 || 60 <= min {
|
|
521
|
+
rangeErrString = "minute"
|
|
522
|
+
}
|
|
523
|
+
case stdSecond, stdZeroSecond:
|
|
524
|
+
sec, value, err = getnum(value, std == stdZeroSecond)
|
|
525
|
+
if err != nil {
|
|
526
|
+
break
|
|
527
|
+
}
|
|
528
|
+
if sec < 0 || 60 <= sec {
|
|
529
|
+
rangeErrString = "second"
|
|
530
|
+
break
|
|
531
|
+
}
|
|
532
|
+
// Special case: do we have a fractional second but no
|
|
533
|
+
// fractional second in the format?
|
|
534
|
+
if len(value) >= 2 && commaOrPeriod(value[0]) && isDigit(value, 1) {
|
|
535
|
+
_, nextStd, _ := nextStdChunk(layout)
|
|
536
|
+
nextStd &= stdMask
|
|
537
|
+
if nextStd == stdFracSecond0 || nextStd == stdFracSecond9 {
|
|
538
|
+
// Fractional second in the layout; proceed normally
|
|
539
|
+
break
|
|
540
|
+
}
|
|
541
|
+
// No fractional second in the layout but we have one in the input.
|
|
542
|
+
n := 2
|
|
543
|
+
for ; n < len(value) && isDigit(value, n); n++ {
|
|
544
|
+
}
|
|
545
|
+
nsec, rangeErrString, err = parseNanoseconds(value, n)
|
|
546
|
+
value = value[n:]
|
|
547
|
+
}
|
|
548
|
+
case stdPM:
|
|
549
|
+
if len(value) < 2 {
|
|
550
|
+
err = errBad
|
|
551
|
+
break
|
|
552
|
+
}
|
|
553
|
+
p, value = value[0:2], value[2:]
|
|
554
|
+
switch p {
|
|
555
|
+
case "PM":
|
|
556
|
+
pmSet = true
|
|
557
|
+
case "AM":
|
|
558
|
+
amSet = true
|
|
559
|
+
default:
|
|
560
|
+
err = errBad
|
|
561
|
+
}
|
|
562
|
+
case stdpm:
|
|
563
|
+
if len(value) < 2 {
|
|
564
|
+
err = errBad
|
|
565
|
+
break
|
|
566
|
+
}
|
|
567
|
+
p, value = value[0:2], value[2:]
|
|
568
|
+
switch p {
|
|
569
|
+
case "pm":
|
|
570
|
+
pmSet = true
|
|
571
|
+
case "am":
|
|
572
|
+
amSet = true
|
|
573
|
+
default:
|
|
574
|
+
err = errBad
|
|
575
|
+
}
|
|
576
|
+
case stdISO8601TZ, stdISO8601ShortTZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ, stdNumSecondsTz, stdNumColonSecondsTZ:
|
|
577
|
+
if (std == stdISO8601TZ || std == stdISO8601ShortTZ || std == stdISO8601ColonTZ || std == stdISO8601SecondsTZ || std == stdISO8601ColonSecondsTZ) && len(value) >= 1 && value[0] == 'Z' {
|
|
578
|
+
value = value[1:]
|
|
579
|
+
z = UTC
|
|
580
|
+
break
|
|
581
|
+
}
|
|
582
|
+
var sign, hour, min, seconds string
|
|
583
|
+
if std == stdISO8601ColonTZ || std == stdNumColonTZ {
|
|
584
|
+
if len(value) < 6 {
|
|
585
|
+
err = errBad
|
|
586
|
+
break
|
|
587
|
+
}
|
|
588
|
+
if value[3] != ':' {
|
|
589
|
+
err = errBad
|
|
590
|
+
break
|
|
591
|
+
}
|
|
592
|
+
sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], "00", value[6:]
|
|
593
|
+
} else if std == stdNumShortTZ || std == stdISO8601ShortTZ {
|
|
594
|
+
if len(value) < 3 {
|
|
595
|
+
err = errBad
|
|
596
|
+
break
|
|
597
|
+
}
|
|
598
|
+
sign, hour, min, seconds, value = value[0:1], value[1:3], "00", "00", value[3:]
|
|
599
|
+
} else if std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ {
|
|
600
|
+
if len(value) < 9 {
|
|
601
|
+
err = errBad
|
|
602
|
+
break
|
|
603
|
+
}
|
|
604
|
+
if value[3] != ':' || value[6] != ':' {
|
|
605
|
+
err = errBad
|
|
606
|
+
break
|
|
607
|
+
}
|
|
608
|
+
sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], value[7:9], value[9:]
|
|
609
|
+
} else if std == stdISO8601SecondsTZ || std == stdNumSecondsTz {
|
|
610
|
+
if len(value) < 7 {
|
|
611
|
+
err = errBad
|
|
612
|
+
break
|
|
613
|
+
}
|
|
614
|
+
sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], value[5:7], value[7:]
|
|
615
|
+
} else {
|
|
616
|
+
if len(value) < 5 {
|
|
617
|
+
err = errBad
|
|
618
|
+
break
|
|
619
|
+
}
|
|
620
|
+
sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], "00", value[5:]
|
|
621
|
+
}
|
|
622
|
+
var hr, mm, ss int
|
|
623
|
+
hr, _, err = getnum(hour, true)
|
|
624
|
+
if err == nil {
|
|
625
|
+
mm, _, err = getnum(min, true)
|
|
626
|
+
}
|
|
627
|
+
if err == nil {
|
|
628
|
+
ss, _, err = getnum(seconds, true)
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// The range test use > rather than >=,
|
|
632
|
+
// as some people do write offsets of 24 hours
|
|
633
|
+
// or 60 minutes or 60 seconds.
|
|
634
|
+
if hr > 24 {
|
|
635
|
+
rangeErrString = "time zone offset hour"
|
|
636
|
+
}
|
|
637
|
+
if mm > 60 {
|
|
638
|
+
rangeErrString = "time zone offset minute"
|
|
639
|
+
}
|
|
640
|
+
if ss > 60 {
|
|
641
|
+
rangeErrString = "time zone offset second"
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
zoneOffset = (hr*60+mm)*60 + ss // offset is in seconds
|
|
645
|
+
switch sign[0] {
|
|
646
|
+
case '+':
|
|
647
|
+
case '-':
|
|
648
|
+
zoneOffset = -zoneOffset
|
|
649
|
+
default:
|
|
650
|
+
err = errBad
|
|
651
|
+
}
|
|
652
|
+
case stdTZ:
|
|
653
|
+
// Does it look like a time zone?
|
|
654
|
+
if len(value) >= 3 && value[0:3] == "UTC" {
|
|
655
|
+
z = UTC
|
|
656
|
+
value = value[3:]
|
|
657
|
+
break
|
|
658
|
+
}
|
|
659
|
+
n, ok := parseTimeZone(value)
|
|
660
|
+
if !ok {
|
|
661
|
+
err = errBad
|
|
662
|
+
break
|
|
663
|
+
}
|
|
664
|
+
zoneName, value = value[:n], value[n:]
|
|
665
|
+
|
|
666
|
+
case stdFracSecond0:
|
|
667
|
+
// stdFracSecond0 requires the exact number of digits as specified in
|
|
668
|
+
// the layout.
|
|
669
|
+
ndigit := 1 + digitsLen(std)
|
|
670
|
+
if len(value) < ndigit {
|
|
671
|
+
err = errBad
|
|
672
|
+
break
|
|
673
|
+
}
|
|
674
|
+
nsec, rangeErrString, err = parseNanoseconds(value, ndigit)
|
|
675
|
+
value = value[ndigit:]
|
|
676
|
+
|
|
677
|
+
case stdFracSecond9:
|
|
678
|
+
if len(value) < 2 || !commaOrPeriod(value[0]) || value[1] < '0' || '9' < value[1] {
|
|
679
|
+
// Fractional second omitted.
|
|
680
|
+
break
|
|
681
|
+
}
|
|
682
|
+
// Take any number of digits, even more than asked for,
|
|
683
|
+
// because it is what the stdSecond case would do.
|
|
684
|
+
i := 0
|
|
685
|
+
for i+1 < len(value) && '0' <= value[i+1] && value[i+1] <= '9' {
|
|
686
|
+
i++
|
|
687
|
+
}
|
|
688
|
+
nsec, rangeErrString, err = parseNanoseconds(value, 1+i)
|
|
689
|
+
value = value[1+i:]
|
|
690
|
+
}
|
|
691
|
+
if rangeErrString != "" {
|
|
692
|
+
return Time{}, newParseError(alayout, avalue, stdstr, value, ": "+rangeErrString+" out of range")
|
|
693
|
+
}
|
|
694
|
+
if err != nil {
|
|
695
|
+
return Time{}, newParseError(alayout, avalue, stdstr, hold, "")
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
if pmSet && hour < 12 {
|
|
699
|
+
hour += 12
|
|
700
|
+
} else if amSet && hour == 12 {
|
|
701
|
+
hour = 0
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// Convert yday to day, month.
|
|
705
|
+
if yday >= 0 {
|
|
706
|
+
var d int
|
|
707
|
+
var m int
|
|
708
|
+
if isLeap(year) {
|
|
709
|
+
if yday == 31+29 {
|
|
710
|
+
m = int(February)
|
|
711
|
+
d = 29
|
|
712
|
+
} else if yday > 31+29 {
|
|
713
|
+
yday--
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
if yday < 1 || yday > 365 {
|
|
717
|
+
// Argh, ParseError's Message field has to be a string.
|
|
718
|
+
return Time{}, newParseError(alayout, avalue, "", value, ": day-of-year out of range")
|
|
719
|
+
}
|
|
720
|
+
if m == 0 {
|
|
721
|
+
m = (yday-1)/31 + 1
|
|
722
|
+
if daysBefore[m] < yday {
|
|
723
|
+
m++
|
|
724
|
+
}
|
|
725
|
+
d = yday - daysBefore[m-1]
|
|
726
|
+
}
|
|
727
|
+
// If month, day already seen, yday's m, d must match.
|
|
728
|
+
// Otherwise, set them from m, d.
|
|
729
|
+
if month >= 0 && month != m {
|
|
730
|
+
return Time{}, newParseError(alayout, avalue, "", value, ": day-of-year does not match month")
|
|
731
|
+
}
|
|
732
|
+
month = m
|
|
733
|
+
if day >= 0 && day != d {
|
|
734
|
+
return Time{}, newParseError(alayout, avalue, "", value, ": day-of-year does not match day")
|
|
735
|
+
}
|
|
736
|
+
day = d
|
|
737
|
+
} else {
|
|
738
|
+
if month < 0 {
|
|
739
|
+
month = int(January)
|
|
740
|
+
}
|
|
741
|
+
if day < 0 {
|
|
742
|
+
day = 1
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// Validate the day of the month.
|
|
747
|
+
if day < 1 || day > daysIn(Month(month), year) {
|
|
748
|
+
return Time{}, newParseError(alayout, avalue, "", value, ": day out of range")
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if z != nil {
|
|
752
|
+
return Date(year, Month(month), day, hour, min, sec, nsec, z), nil
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
if zoneOffset != -1 {
|
|
756
|
+
t := Date(year, Month(month), day, hour, min, sec, nsec, UTC)
|
|
757
|
+
t.sec -= int64(zoneOffset)
|
|
758
|
+
|
|
759
|
+
// Look for local zone with the given offset.
|
|
760
|
+
// If that zone was in effect at the given time, use it.
|
|
761
|
+
name, offset := lookupZone(local, t.unixSec())
|
|
762
|
+
if offset == zoneOffset && (zoneName == "" || name == zoneName) {
|
|
763
|
+
return t.setLoc(local), nil
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// Otherwise create fake zone with unknown name.
|
|
767
|
+
return t.setLoc(FixedZone(zoneName, zoneOffset)), nil
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
if zoneName != "" {
|
|
771
|
+
t := Date(year, Month(month), day, hour, min, sec, nsec, UTC)
|
|
772
|
+
// Look for local zone with the given name.
|
|
773
|
+
name, offset := lookupZone(local, t.unixSec())
|
|
774
|
+
if name == zoneName {
|
|
775
|
+
t.sec -= int64(offset)
|
|
776
|
+
return t.setLoc(local), nil
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// Otherwise, create fake zone with unknown offset.
|
|
780
|
+
offset = 0
|
|
781
|
+
if len(zoneName) > 3 && zoneName[:3] == "GMT" {
|
|
782
|
+
offset, _ = atoi(zoneName[3:]) // Guaranteed OK by parseGMT.
|
|
783
|
+
offset *= 3600
|
|
784
|
+
}
|
|
785
|
+
return t.setLoc(FixedZone(zoneName, offset)), nil
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// Otherwise, fall back to default.
|
|
789
|
+
return Date(year, Month(month), day, hour, min, sec, nsec, defaultLocation), nil
|
|
790
|
+
}
|