1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
package rfc3339
import (
"encoding/json"
"fmt"
"regexp"
"strconv"
)
// RFC3339 date.
type Date uint16
// Returns true if the year is a leap year, and false otherwise.
//
// Reference:
// https://www.timeanddate.com/date/leapyear.html
func isLeapYear(y uint16) bool {
// leap year rules:
// 1. year is evenly divisible by 4 and is not evenly divisible by 100
// 2. year is evenly divisible by 4 and is evenly divisible by 400
return (((y % 4) == 0) && ((y % 100) != 0)) || // rule 1
(((y % 4) == 0) && ((y % 400) == 0)) // rule 2
}
// maximum month days
var maxMonthDays = []uint16 {
31, // jan
28, // feb (incorrect for leap years!)
31, // mar
30, // apr
31, // may
30, // jun
31, // jul
31, // aug
30, // sep
31, // oct
30, // nov
31, // dec
}
// Get the maximum day for the given year and month.
func getMaxMonthDay(y, m uint16) uint16 {
if m == 2 && isLeapYear(y) {
return 29
} else {
return maxMonthDays[m - 1]
}
}
// Parse date component and check range.
func parseDateComponent(name string, s []byte, min, max uint16) (uint16, error) {
// parse value
vr, err := strconv.ParseUint(string(s), 10, 32)
if err != nil {
return 0, err
}
v := uint16(vr)
// check range
if v < min || v > max {
return 0, fmt.Errorf("%s component out of range [%d, %d]: %d", name, v, min, max)
}
return uint16(v), nil
}
var dateRe = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`)
// Create date from byte slice.
func NewDate(s []byte) (Date, error) {
var r Date
if !dateRe.Match(s) {
return r, fmt.Errorf("invalid date: \"%s\"", s)
}
// parse year
y, err := parseDateComponent("year", s[0:4], 1999, 2126)
if err != nil {
return r, err
}
// parse month
m, err := parseDateComponent("month", s[5:7], 1, 12)
if err != nil {
return r, err
}
// parse day
d, err := parseDateComponent("month", s[8:10], 1, getMaxMonthDay(y, m))
if err != nil {
return r, err
}
// encode return date
r = Date((((y - 1999) & 0x3f) << 9) |
(((m - 1) & 0xf) << 5) |
((d - 1) & 0x1f))
// return result
return r, nil
}
// Get year, month, and day components.
func (me Date) GetComponents() (uint16, uint16, uint16) {
// extract components
y := uint16((uint16(me) >> 9) + 1999)
m := uint16(((uint16(me) >> 5) & 0xf) + 1)
d := uint16((uint16(me) & 0x1f) + 1)
return y, m, d
}
// Get year component.
func (me Date) Year() uint16 {
y, _, _ := me.GetComponents()
return y
}
// Get month component.
func (me Date) Month() uint16 {
_, m, _ := me.GetComponents()
return m
}
// Get day component.
func (me Date) Day() uint16 {
_, _, d := me.GetComponents()
return d
}
// Convert to string.
func (me Date) String() string {
// extract date components
y, m, d := me.GetComponents()
// return string
return fmt.Sprintf("%04d-%02d-%02d", y, m, d)
}
// Unmarshal date from JSON string.
func (me *Date) UnmarshalJSON(b []byte) error {
// decode json string
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
// create date
d, err := NewDate([]byte(s))
if err != nil {
return err
}
// save result, return success
*me = d
return nil
}
// Marshal Date as JSON string.
func (d Date) MarshalJSON() ([]byte, error) {
return json.Marshal(d.String())
}
|