Repository navigation
Expand file tree
/
Copy pathdate.go
More file actions
145 lines (115 loc) · 4.48 KB
/
Copy pathdate.go
File metadata and controls
145 lines (115 loc) · 4.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package jsonutil
import (
"encoding/json/jsontext"
"encoding/json/v2"
"errors"
"fmt"
"time"
"cloud.google.com/go/civil"
)
// DateMarshalIntUnix is a custom marshaler for civil.Date, marshaling them as integers representing unix time.
func DateMarshalIntUnix(enc *jsontext.Encoder, d civil.Date) error {
if d.IsZero() {
return enc.WriteToken(jsontext.Int(0))
}
return enc.WriteToken(jsontext.Int(int64(
time.Date(d.Year, d.Month, d.Day, 0, 0, 0, 0, time.UTC).Unix(),
)))
}
// DateUnmarshalIntUnix is a custom unmarshaler for civil.Date, unmarshaling them from integers and assuming they represent unix time.
func DateUnmarshalIntUnix(dec *jsontext.Decoder, d *civil.Date) error {
var seconds int64
if err := json.UnmarshalDecode(dec, &seconds); err != nil {
return err
}
if seconds == 0 {
*d = civil.Date{}
} else {
*d = civil.DateOf(time.Unix(seconds, 0).UTC())
}
return nil
}
// DateMarshalIntUnixFor returns a marshaler for civil.Date that writes, as [DateMarshalIntUnix] does, the dates held by
// an object member of one of the names given, or by an array such a member holds. It leaves any other date to the
// marshaler after it, or to the default, which writes a string: for an API that writes some dates as unix time and
// others as strings.
func DateMarshalIntUnixFor(names ...string) func(*jsontext.Encoder, civil.Date) error {
set := nameSet(names)
return func(enc *jsontext.Encoder, d civil.Date) error {
if !set[memberName(enc.StackPointer())] {
return errors.ErrUnsupported
}
return DateMarshalIntUnix(enc, d)
}
}
// DateUnmarshalIntUnixFor returns an unmarshaler for civil.Date that reads, as [DateUnmarshalIntUnix] does, the dates
// held by an object member of one of the names given, or by an array such a member holds; see [DateMarshalIntUnixFor].
func DateUnmarshalIntUnixFor(names ...string) func(*jsontext.Decoder, *civil.Date) error {
set := nameSet(names)
return func(dec *jsontext.Decoder, d *civil.Date) error {
if !set[memberName(dec.StackPointer())] {
return errors.ErrUnsupported
}
return DateUnmarshalIntUnix(dec, d)
}
}
// DateMarshalBasic is a custom marshaler for civil.Date, marshaling them as strings in ISO 8601's basic form, without
// separators, such as "20260320".
func DateMarshalBasic(enc *jsontext.Encoder, d civil.Date) error {
if d.Year < 0 || d.Year > 9999 {
return fmt.Errorf("year %d does not fit four digits", d.Year)
}
b := [8]byte{
byte('0' + d.Year/1000), byte('0' + d.Year/100%10), byte('0' + d.Year/10%10), byte('0' + d.Year%10),
byte('0' + d.Month/10), byte('0' + d.Month%10),
byte('0' + d.Day/10), byte('0' + d.Day%10),
}
return enc.WriteToken(jsontext.String(string(b[:])))
}
// DateUnmarshalBasic is a custom unmarshaler for civil.Date, unmarshaling them from strings in ISO 8601's basic form,
// such as "20260320".
func DateUnmarshalBasic(dec *jsontext.Decoder, d *civil.Date) error {
v, err := dec.ReadValue()
if err != nil {
return err
}
// eight digits within quotes; a digit is never escaped, so the raw value is all there is to read
if len(v) != 10 || v[0] != '"' || v[9] != '"' {
return fmt.Errorf("%s is no date such as \"20260320\"", v)
}
n := 0
for _, c := range v[1:9] {
if c < '0' || c > '9' {
return fmt.Errorf("%s is no date such as \"20260320\"", v)
}
n = n*10 + int(c-'0')
}
date := civil.Date{Year: n / 10000, Month: time.Month(n / 100 % 100), Day: n % 100}
if !date.IsValid() {
return fmt.Errorf("%s is no valid date", v)
}
*d = date
return nil
}
// DateMarshalBasicFor returns a marshaler for civil.Date that writes, as [DateMarshalBasic] does, the dates held by an
// object member of one of the names given, or by an array such a member holds; see [DateMarshalIntUnixFor].
func DateMarshalBasicFor(names ...string) func(*jsontext.Encoder, civil.Date) error {
set := nameSet(names)
return func(enc *jsontext.Encoder, d civil.Date) error {
if !set[memberName(enc.StackPointer())] {
return errors.ErrUnsupported
}
return DateMarshalBasic(enc, d)
}
}
// DateUnmarshalBasicFor returns an unmarshaler for civil.Date that reads, as [DateUnmarshalBasic] does, the dates held
// by an object member of one of the names given, or by an array such a member holds; see [DateMarshalIntUnixFor].
func DateUnmarshalBasicFor(names ...string) func(*jsontext.Decoder, *civil.Date) error {
set := nameSet(names)
return func(dec *jsontext.Decoder, d *civil.Date) error {
if !set[memberName(dec.StackPointer())] {
return errors.ErrUnsupported
}
return DateUnmarshalBasic(dec, d)
}
}