Skip to content

Commit 9159cd4

Browse files
dsnetgopherbot
authored andcommitted
encoding/json: decompose legacy options
WARNING: This commit contains breaking changes for those already using GOEXPERIMENT=jsonv2. This decomposes FormatBytesWithLegacySemantics as: * FormatBytesWithLegacySemantics * FormatByteArrayAsArray * ParseBytesWithLooseRFC4648 This decomposes FormatTimeWithLegacySemantics as: * FormatDurationAsNano * ParseTimeWithLooseRFC3339 In particular, it splits out specific behaviors from the option that may need to be specified on a finer-grain level. FormatByteArrayAsArray and FormatDurationAsNano are targeted to just the default representation of a [N]byte or time.Duration type. Both of these are not necessary if the `format` tag is explicitly specified. However, we want to isolate their behavior from other behaviors that used to be part of FormatBytesWithLegacySemantics and FormatTimeWithLegacySemantics. ParseBytesWithLooseRFC4648 and ParseTimeWithLooseRFC3339 are targeted to just historically buggy parsing according to the relevant RFCs, which may need to be enabled by some services for backwards compatibility. While FormatTimeWithLegacySemantics is deleted, we still need FormatBytesWithLegacySemantics to configure highly esoteric aspects of how v1 used to handle byte slices. We rename OmitEmptyWithLegacyDefinition as OmitEmptyWithLegacySemantics to be consistent with other options with the WithLegacySemantics suffix. Updates #71497 Change-Id: Ic660515fb086fe3af237135f195736de99c2bd33 Reviewed-on: https://go-review.googlesource.com/c/go/+/685395 Auto-Submit: Joseph Tsai <joetsai@digital-static.net> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
1 parent c6556b8 commit 9159cd4

File tree

5 files changed

+112
-62
lines changed

5 files changed

+112
-62
lines changed

src/encoding/json/internal/jsonflags/flags.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,14 @@ const (
5858
FormatNilSliceAsNull |
5959
MatchCaseInsensitiveNames |
6060
CallMethodsWithLegacySemantics |
61+
FormatByteArrayAsArray |
6162
FormatBytesWithLegacySemantics |
62-
FormatTimeWithLegacySemantics |
63+
FormatDurationAsNano |
6364
MatchCaseSensitiveDelimiter |
6465
MergeWithLegacySemantics |
65-
OmitEmptyWithLegacyDefinition |
66+
OmitEmptyWithLegacySemantics |
67+
ParseBytesWithLooseRFC4648 |
68+
ParseTimeWithLooseRFC3339 |
6669
ReportErrorsWithLegacySemantics |
6770
StringifyWithLegacySemantics |
6871
UnmarshalArrayFromAnyLength
@@ -130,11 +133,14 @@ const (
130133
_ Bools = (maxArshalV2Flag >> 1) << iota
131134

132135
CallMethodsWithLegacySemantics // marshal or unmarshal
136+
FormatByteArrayAsArray // marshal or unmarshal
133137
FormatBytesWithLegacySemantics // marshal or unmarshal
134-
FormatTimeWithLegacySemantics // marshal or unmarshal
138+
FormatDurationAsNano // marshal or unmarshal
135139
MatchCaseSensitiveDelimiter // marshal or unmarshal
136140
MergeWithLegacySemantics // unmarshal
137-
OmitEmptyWithLegacyDefinition // marshal
141+
OmitEmptyWithLegacySemantics // marshal
142+
ParseBytesWithLooseRFC4648 // unmarshal
143+
ParseTimeWithLooseRFC3339 // unmarshal
138144
ReportErrorsWithLegacySemantics // marshal or unmarshal
139145
StringifyWithLegacySemantics // marshal or unmarshal
140146
StringifyBoolsAndStrings // marshal or unmarshal; for internal use by jsonv2.makeStructArshaler
@@ -144,6 +150,12 @@ const (
144150
maxArshalV1Flag
145151
)
146152

153+
// bitsUsed is the number of bits used in the 64-bit boolean flags
154+
const bitsUsed = 42
155+
156+
// Static compile check that bitsUsed and maxArshalV1Flag are in sync.
157+
const _ = uint64((1<<bitsUsed)-maxArshalV1Flag) + uint64(maxArshalV1Flag-(1<<bitsUsed))
158+
147159
// Flags is a set of boolean flags.
148160
// If the presence bit is zero, then the value bit must also be zero.
149161
// The least-significant bit of both fields is always zero.

src/encoding/json/v2/arshal_default.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,9 @@ func makeBytesArshaler(t reflect.Type, fncs *arshaler) *arshaler {
329329
default:
330330
return newInvalidFormatError(enc, t, mo)
331331
}
332-
} else if mo.Flags.Get(jsonflags.FormatBytesWithLegacySemantics) &&
333-
(va.Kind() == reflect.Array || hasMarshaler) {
332+
} else if mo.Flags.Get(jsonflags.FormatByteArrayAsArray) && va.Kind() == reflect.Array {
333+
return marshalArray(enc, va, mo)
334+
} else if mo.Flags.Get(jsonflags.FormatBytesWithLegacySemantics) && hasMarshaler {
334335
return marshalArray(enc, va, mo)
335336
}
336337
if mo.Flags.Get(jsonflags.FormatNilSliceAsNull) && va.Kind() == reflect.Slice && va.IsNil() {
@@ -366,8 +367,9 @@ func makeBytesArshaler(t reflect.Type, fncs *arshaler) *arshaler {
366367
default:
367368
return newInvalidFormatError(dec, t, uo)
368369
}
369-
} else if uo.Flags.Get(jsonflags.FormatBytesWithLegacySemantics) &&
370-
(va.Kind() == reflect.Array || dec.PeekKind() == '[') {
370+
} else if uo.Flags.Get(jsonflags.FormatByteArrayAsArray) && va.Kind() == reflect.Array {
371+
return unmarshalArray(dec, va, uo)
372+
} else if uo.Flags.Get(jsonflags.FormatBytesWithLegacySemantics) && dec.PeekKind() == '[' {
371373
return unmarshalArray(dec, va, uo)
372374
}
373375
var flags jsonwire.ValueFlags
@@ -395,7 +397,7 @@ func makeBytesArshaler(t reflect.Type, fncs *arshaler) *arshaler {
395397
if err != nil {
396398
return newUnmarshalErrorAfter(dec, t, err)
397399
}
398-
if len(val) != encodedLen(len(b)) && !uo.Flags.Get(jsonflags.FormatBytesWithLegacySemantics) {
400+
if len(val) != encodedLen(len(b)) && !uo.Flags.Get(jsonflags.ParseBytesWithLooseRFC4648) {
399401
// TODO(https://go.dev/issue/53845): RFC 4648, section 3.3,
400402
// specifies that non-alphabet characters must be rejected.
401403
// Unfortunately, the "base32" and "base64" packages allow
@@ -1065,7 +1067,7 @@ func makeStructArshaler(t reflect.Type) *arshaler {
10651067
}
10661068

10671069
// Check for the legacy definition of omitempty.
1068-
if f.omitempty && mo.Flags.Get(jsonflags.OmitEmptyWithLegacyDefinition) && isLegacyEmpty(v) {
1070+
if f.omitempty && mo.Flags.Get(jsonflags.OmitEmptyWithLegacySemantics) && isLegacyEmpty(v) {
10691071
continue
10701072
}
10711073

@@ -1080,7 +1082,7 @@ func makeStructArshaler(t reflect.Type) *arshaler {
10801082
// OmitEmpty skips the field if the marshaled JSON value is empty,
10811083
// which we can know up front if there are no custom marshalers,
10821084
// otherwise we must marshal the value and unwrite it if empty.
1083-
if f.omitempty && !mo.Flags.Get(jsonflags.OmitEmptyWithLegacyDefinition) &&
1085+
if f.omitempty && !mo.Flags.Get(jsonflags.OmitEmptyWithLegacySemantics) &&
10841086
!nonDefault && f.isEmpty != nil && f.isEmpty(v) {
10851087
continue // fast path for omitempty
10861088
}
@@ -1145,7 +1147,7 @@ func makeStructArshaler(t reflect.Type) *arshaler {
11451147
}
11461148

11471149
// Try unwriting the member if empty (slow path for omitempty).
1148-
if f.omitempty && !mo.Flags.Get(jsonflags.OmitEmptyWithLegacyDefinition) {
1150+
if f.omitempty && !mo.Flags.Get(jsonflags.OmitEmptyWithLegacySemantics) {
11491151
var prevName *string
11501152
if prevIdx >= 0 {
11511153
prevName = &fields.flattened[prevIdx].name

src/encoding/json/v2/arshal_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1924,12 +1924,12 @@ func TestMarshal(t *testing.T) {
19241924
}`,
19251925
}, {
19261926
name: jsontest.Name("Structs/OmitEmpty/Legacy/Zero"),
1927-
opts: []Options{jsonflags.OmitEmptyWithLegacyDefinition | 1},
1927+
opts: []Options{jsonflags.OmitEmptyWithLegacySemantics | 1},
19281928
in: structOmitEmptyAll{},
19291929
want: `{}`,
19301930
}, {
19311931
name: jsontest.Name("Structs/OmitEmpty/Legacy/NonEmpty"),
1932-
opts: []Options{jsontext.Multiline(true), jsonflags.OmitEmptyWithLegacyDefinition | 1},
1932+
opts: []Options{jsontext.Multiline(true), jsonflags.OmitEmptyWithLegacySemantics | 1},
19331933
in: structOmitEmptyAll{
19341934
Bool: true,
19351935
PointerBool: addr(true),
@@ -2144,7 +2144,7 @@ func TestMarshal(t *testing.T) {
21442144
"Default": "AQIDBA=="
21452145
}`}, {
21462146
name: jsontest.Name("Structs/Format/ArrayBytes/Legacy"),
2147-
opts: []Options{jsontext.Multiline(true), jsonflags.FormatBytesWithLegacySemantics | 1},
2147+
opts: []Options{jsontext.Multiline(true), jsonflags.FormatByteArrayAsArray | jsonflags.FormatBytesWithLegacySemantics | 1},
21482148
in: structFormatArrayBytes{
21492149
Base16: [4]byte{1, 2, 3, 4},
21502150
Base32: [4]byte{1, 2, 3, 4},
@@ -4394,7 +4394,7 @@ func TestMarshal(t *testing.T) {
43944394
}, {
43954395
/* TODO(https://go.dev/issue/71631): Re-enable this test case.
43964396
name: jsontest.Name("Duration/Format/Legacy"),
4397-
opts: []Options{jsonflags.FormatTimeWithLegacySemantics | 1},
4397+
opts: []Options{jsonflags.FormatDurationAsNano | 1},
43984398
in: structDurationFormat{
43994399
D1: 12*time.Hour + 34*time.Minute + 56*time.Second + 78*time.Millisecond + 90*time.Microsecond + 12*time.Nanosecond,
44004400
D2: 12*time.Hour + 34*time.Minute + 56*time.Second + 78*time.Millisecond + 90*time.Microsecond + 12*time.Nanosecond,
@@ -4407,7 +4407,7 @@ func TestMarshal(t *testing.T) {
44074407
want: `{"1s":""}`,
44084408
}, { */
44094409
name: jsontest.Name("Duration/MapKey/Legacy"),
4410-
opts: []Options{jsonflags.FormatTimeWithLegacySemantics | 1},
4410+
opts: []Options{jsonflags.FormatDurationAsNano | 1},
44114411
in: map[time.Duration]string{time.Second: ""},
44124412
want: `{"1000000000":""}`,
44134413
}, {
@@ -6399,7 +6399,7 @@ func TestUnmarshal(t *testing.T) {
63996399
wantErr: EU(errors.New("illegal character '\\r' at offset 3")).withPos(`{"Base64": `, "/Base64").withType('"', T[[]byte]()),
64006400
}, {
64016401
name: jsontest.Name("Structs/Format/Bytes/Base64/NonAlphabet/Ignored"),
6402-
opts: []Options{jsonflags.FormatBytesWithLegacySemantics | 1},
6402+
opts: []Options{jsonflags.ParseBytesWithLooseRFC4648 | 1},
64036403
inBuf: `{"Base64": "aa=\r\n="}`,
64046404
inVal: new(structFormatBytes),
64056405
want: &structFormatBytes{Base64: []byte{105}},
@@ -8885,7 +8885,7 @@ func TestUnmarshal(t *testing.T) {
88858885
/* TODO(https://go.dev/issue/71631): Re-enable this test case.
88868886
name: jsontest.Name("Duration/Format/Legacy"),
88878887
inBuf: `{"D1":45296078090012,"D2":"12h34m56.078090012s"}`,
8888-
opts: []Options{jsonflags.FormatTimeWithLegacySemantics | 1},
8888+
opts: []Options{jsonflags.FormatDurationAsNano | 1},
88898889
inVal: new(structDurationFormat),
88908890
want: addr(structDurationFormat{
88918891
D1: 12*time.Hour + 34*time.Minute + 56*time.Second + 78*time.Millisecond + 90*time.Microsecond + 12*time.Nanosecond,
@@ -8899,7 +8899,7 @@ func TestUnmarshal(t *testing.T) {
88998899
want: addr(map[time.Duration]string{time.Second: ""}),
89008900
}, { */
89018901
name: jsontest.Name("Duration/MapKey/Legacy"),
8902-
opts: []Options{jsonflags.FormatTimeWithLegacySemantics | 1},
8902+
opts: []Options{jsonflags.FormatDurationAsNano | 1},
89038903
inBuf: `{"1000000000":""}`,
89048904
inVal: new(map[time.Duration]string),
89058905
want: addr(map[time.Duration]string{time.Second: ""}),

src/encoding/json/v2/arshal_time.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler {
5050
if !m.initFormat(mo.Format) {
5151
return newInvalidFormatError(enc, t, mo)
5252
}
53-
} else if mo.Flags.Get(jsonflags.FormatTimeWithLegacySemantics) {
53+
} else if mo.Flags.Get(jsonflags.FormatDurationAsNano) {
5454
return marshalNano(enc, va, mo)
5555
} else {
5656
// TODO(https://go.dev/issue/71631): Decide on default duration representation.
@@ -76,7 +76,7 @@ func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler {
7676
if !u.initFormat(uo.Format) {
7777
return newInvalidFormatError(dec, t, uo)
7878
}
79-
} else if uo.Flags.Get(jsonflags.FormatTimeWithLegacySemantics) {
79+
} else if uo.Flags.Get(jsonflags.FormatDurationAsNano) {
8080
return unmarshalNano(dec, va, uo)
8181
} else {
8282
// TODO(https://go.dev/issue/71631): Decide on default duration representation.
@@ -150,7 +150,7 @@ func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler {
150150
if !u.initFormat(uo.Format) {
151151
return newInvalidFormatError(dec, t, uo)
152152
}
153-
} else if uo.Flags.Get(jsonflags.FormatTimeWithLegacySemantics) {
153+
} else if uo.Flags.Get(jsonflags.ParseTimeWithLooseRFC3339) {
154154
u.looseRFC3339 = true
155155
}
156156

src/encoding/json/v2_options.go

Lines changed: 75 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
// any empty array, slice, map, or string. In contrast, v2 redefines
3737
// `omitempty` to omit a field if it encodes as an "empty" JSON value,
3838
// which is defined as a JSON null, or an empty JSON string, object, or array.
39-
// The [OmitEmptyWithLegacyDefinition] option controls this behavior difference.
39+
// The [OmitEmptyWithLegacySemantics] option controls this behavior difference.
4040
// Note that `omitempty` behaves identically in both v1 and v2 for a
4141
// Go array, slice, map, or string (assuming no user-defined MarshalJSON method
4242
// overrides the default representation). Existing usages of `omitempty` on a
@@ -66,7 +66,7 @@
6666
//
6767
// - In v1, a Go byte array is represented as a JSON array of JSON numbers.
6868
// In contrast, in v2 a Go byte array is represented as a Base64-encoded JSON string.
69-
// The [FormatBytesWithLegacySemantics] option controls this behavior difference.
69+
// The [FormatByteArrayAsArray] option controls this behavior difference.
7070
// To explicitly specify a Go struct field to use a particular representation,
7171
// either the `format:array` or `format:base64` field option can be specified.
7272
// Field-specified options take precedence over caller-specified options.
@@ -118,9 +118,8 @@
118118
//
119119
// - In v1, a [time.Duration] is represented as a JSON number containing
120120
// the decimal number of nanoseconds. In contrast, in v2 a [time.Duration]
121-
// is represented as a JSON string containing the formatted duration
122-
// (e.g., "1h2m3.456s") according to [time.Duration.String].
123-
// The [FormatTimeWithLegacySemantics] option controls this behavior difference.
121+
// has no default representation and results in a runtime error.
122+
// The [FormatDurationAsNano] option controls this behavior difference.
124123
// To explicitly specify a Go struct field to use a particular representation,
125124
// either the `format:nano` or `format:units` field option can be specified.
126125
// Field-specified options take precedence over caller-specified options.
@@ -172,6 +171,9 @@
172171
// but the v1 package will forever remain supported.
173172
package json
174173

174+
// TODO(https://go.dev/issue/71631): Update the "Migrating to v2" documentation
175+
// with default v2 behavior for [time.Duration].
176+
175177
import (
176178
"encoding"
177179

@@ -204,11 +206,14 @@ type Options = jsonopts.Options
204206
// It is equivalent to the following boolean options being set to true:
205207
//
206208
// - [CallMethodsWithLegacySemantics]
209+
// - [FormatByteArrayAsArray]
207210
// - [FormatBytesWithLegacySemantics]
208-
// - [FormatTimeWithLegacySemantics]
211+
// - [FormatDurationAsNano]
209212
// - [MatchCaseSensitiveDelimiter]
210213
// - [MergeWithLegacySemantics]
211-
// - [OmitEmptyWithLegacyDefinition]
214+
// - [OmitEmptyWithLegacySemantics]
215+
// - [ParseBytesWithLooseRFC4648]
216+
// - [ParseTimeWithLooseRFC3339]
212217
// - [ReportErrorsWithLegacySemantics]
213218
// - [StringifyWithLegacySemantics]
214219
// - [UnmarshalArrayFromAnyLength]
@@ -278,13 +283,25 @@ func CallMethodsWithLegacySemantics(v bool) Options {
278283
}
279284
}
280285

286+
// FormatByteArrayAsArray specifies that a Go [N]byte is
287+
// formatted as as a normal Go array in contrast to the v2 default of
288+
// formatting [N]byte as using binary data encoding (RFC 4648).
289+
// If a struct field has a `format` tag option,
290+
// then the specified formatting takes precedence.
291+
//
292+
// This affects either marshaling or unmarshaling.
293+
// The v1 default is true.
294+
func FormatByteArrayAsArray(v bool) Options {
295+
if v {
296+
return jsonflags.FormatByteArrayAsArray | 1
297+
} else {
298+
return jsonflags.FormatByteArrayAsArray | 0
299+
}
300+
}
301+
281302
// FormatBytesWithLegacySemantics specifies that handling of
282303
// []~byte and [N]~byte types follow legacy semantics:
283304
//
284-
// - A Go [N]~byte is always treated as as a normal Go array
285-
// in contrast to the v2 default of treating [N]byte as
286-
// using some form of binary data encoding (RFC 4648).
287-
//
288305
// - A Go []~byte is to be treated as using some form of
289306
// binary data encoding (RFC 4648) in contrast to the v2 default
290307
// of only treating []byte as such. In particular, v2 does not
@@ -299,12 +316,6 @@ func CallMethodsWithLegacySemantics(v bool) Options {
299316
// In contrast, the v2 default is to report an error unmarshaling
300317
// a JSON array when expecting some form of binary data encoding.
301318
//
302-
// - When unmarshaling, '\r' and '\n' characters are ignored
303-
// within the encoded "base32" and "base64" data.
304-
// In contrast, the v2 default is to report an error in order to be
305-
// strictly compliant with RFC 4648, section 3.3,
306-
// which specifies that non-alphabet characters must be rejected.
307-
//
308319
// This affects either marshaling or unmarshaling.
309320
// The v1 default is true.
310321
func FormatBytesWithLegacySemantics(v bool) Options {
@@ -315,29 +326,20 @@ func FormatBytesWithLegacySemantics(v bool) Options {
315326
}
316327
}
317328

318-
// FormatTimeWithLegacySemantics specifies that [time] types are formatted
319-
// with legacy semantics:
320-
//
321-
// - When marshaling or unmarshaling, a [time.Duration] is formatted as
322-
// a JSON number representing the number of nanoseconds.
323-
// In contrast, the default v2 behavior uses a JSON string
324-
// with the duration formatted with [time.Duration.String].
325-
// If a duration field has a `format` tag option,
326-
// then the specified formatting takes precedence.
327-
//
328-
// - When unmarshaling, a [time.Time] follows loose adherence to RFC 3339.
329-
// In particular, it permits historically incorrect representations,
330-
// allowing for deviations in hour format, sub-second separator,
331-
// and timezone representation. In contrast, the default v2 behavior
332-
// is to strictly comply with the grammar specified in RFC 3339.
329+
// FormatDurationAsNano specifies that a [time.Duration] is
330+
// formatted as a JSON number representing the number of nanoseconds
331+
// in contrast to the v2 default of reporting an error.
332+
// If a duration field has a `format` tag option,
333+
// then the specified formatting takes precedence.
333334
//
334335
// This affects either marshaling or unmarshaling.
335336
// The v1 default is true.
336-
func FormatTimeWithLegacySemantics(v bool) Options {
337+
func FormatDurationAsNano(v bool) Options {
338+
// TODO(https://go.dev/issue/71631): Update documentation with v2 behavior.
337339
if v {
338-
return jsonflags.FormatTimeWithLegacySemantics | 1
340+
return jsonflags.FormatDurationAsNano | 1
339341
} else {
340-
return jsonflags.FormatTimeWithLegacySemantics | 0
342+
return jsonflags.FormatDurationAsNano | 0
341343
}
342344
}
343345

@@ -386,7 +388,7 @@ func MergeWithLegacySemantics(v bool) Options {
386388
}
387389
}
388390

389-
// OmitEmptyWithLegacyDefinition specifies that the `omitempty` tag option
391+
// OmitEmptyWithLegacySemantics specifies that the `omitempty` tag option
390392
// follows a definition of empty where a field is omitted if the Go value is
391393
// false, 0, a nil pointer, a nil interface value,
392394
// or any empty array, slice, map, or string.
@@ -400,11 +402,45 @@ func MergeWithLegacySemantics(v bool) Options {
400402
//
401403
// This only affects marshaling and is ignored when unmarshaling.
402404
// The v1 default is true.
403-
func OmitEmptyWithLegacyDefinition(v bool) Options {
405+
func OmitEmptyWithLegacySemantics(v bool) Options {
406+
if v {
407+
return jsonflags.OmitEmptyWithLegacySemantics | 1
408+
} else {
409+
return jsonflags.OmitEmptyWithLegacySemantics | 0
410+
}
411+
}
412+
413+
// ParseBytesWithLooseRFC4648 specifies that when parsing
414+
// binary data encoded as "base32" or "base64",
415+
// to ignore the presence of '\r' and '\n' characters.
416+
// In contrast, the v2 default is to report an error in order to be
417+
// strictly compliant with RFC 4648, section 3.3,
418+
// which specifies that non-alphabet characters must be rejected.
419+
//
420+
// This only affects unmarshaling and is ignored when marshaling.
421+
// The v1 default is true.
422+
func ParseBytesWithLooseRFC4648(v bool) Options {
423+
if v {
424+
return jsonflags.ParseBytesWithLooseRFC4648 | 1
425+
} else {
426+
return jsonflags.ParseBytesWithLooseRFC4648 | 0
427+
}
428+
}
429+
430+
// ParseTimeWithLooseRFC3339 specifies that a [time.Time]
431+
// parses according to loose adherence to RFC 3339.
432+
// In particular, it permits historically incorrect representations,
433+
// allowing for deviations in hour format, sub-second separator,
434+
// and timezone representation. In contrast, the default v2 behavior
435+
// is to strictly comply with the grammar specified in RFC 3339.
436+
//
437+
// This only affects unmarshaling and is ignored when marshaling.
438+
// The v1 default is true.
439+
func ParseTimeWithLooseRFC3339(v bool) Options {
404440
if v {
405-
return jsonflags.OmitEmptyWithLegacyDefinition | 1
441+
return jsonflags.ParseTimeWithLooseRFC3339 | 1
406442
} else {
407-
return jsonflags.OmitEmptyWithLegacyDefinition | 0
443+
return jsonflags.ParseTimeWithLooseRFC3339 | 0
408444
}
409445
}
410446

0 commit comments

Comments
 (0)