Skip to content

Commit db08fef

Browse files
committed
Date ParseISO & AutoParse now accept a date-time input (time is ignored)
1 parent 9a7458e commit db08fef

File tree

2 files changed

+42
-8
lines changed

2 files changed

+42
-8
lines changed

parse.go

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package date
77
import (
88
"errors"
99
"fmt"
10+
"regexp"
1011
"strconv"
1112
"strings"
1213
"time"
@@ -92,6 +93,9 @@ func MustParseISO(value string) Date {
9293
// with possibly extra year digits beyond the prescribed four-digit minimum
9394
// and with a + or - sign prefix (e.g. , "+12345-06-07", "-0987-06-05").
9495
//
96+
// If a time field is present, it is ignored. For example, "2018-02-03T00:00:00Z" is parsed
97+
// as 3rd February 2018.
98+
//
9599
// Note that ParseISO is a little looser than the ISO 8601 standard and will
96100
// be happy to parse dates with a year longer in length than the four-digit minimum even
97101
// if they are missing the + sign prefix.
@@ -102,15 +106,23 @@ func MustParseISO(value string) Date {
102106
//
103107
// Background: https://en.wikipedia.org/wiki/ISO_8601#Dates
104108
func ParseISO(value string) (Date, error) {
105-
if len(value) < 8 {
106-
return Date{}, fmt.Errorf("Date.ParseISO: cannot parse %q: incorrect length", value)
107-
}
108-
109109
abs := value
110110
if value[0] == '+' || value[0] == '-' {
111111
abs = value[1:]
112112
}
113113

114+
if len(abs) < 8 {
115+
return Date{}, fmt.Errorf("date.ParseISO: cannot parse %q: incorrect length", value)
116+
}
117+
118+
tee := strings.IndexByte(abs, 'T')
119+
if tee == 8 || tee == 10 {
120+
if !timeRegex1.MatchString(abs[tee:]) && !timeRegex2.MatchString(abs[tee:]) {
121+
return Date{}, fmt.Errorf("date.ParseISO: date-time %q: not a time", value)
122+
}
123+
abs = abs[:tee]
124+
}
125+
114126
dash1 := strings.IndexByte(abs, '-')
115127
fm1 := dash1 + 1
116128
fm2 := dash1 + 3
@@ -125,12 +137,12 @@ func ParseISO(value string) (Date, error) {
125137
fd1 = 6
126138
fd2 = 8
127139
} else if abs[fm2] != '-' {
128-
return Date{}, fmt.Errorf("Date.ParseISO: cannot parse %q: incorrect syntax", value)
140+
return Date{}, fmt.Errorf("date.ParseISO: cannot parse %q: incorrect syntax", value)
129141
}
130142
//fmt.Printf("%s %d %d %d %d %d\n", value, dash1, fm1, fm2, fd1, fd2)
131143

132144
if len(abs) != fd2 {
133-
return Date{}, fmt.Errorf("Date.ParseISO: cannot parse %q: incorrect length", value)
145+
return Date{}, fmt.Errorf("date.ParseISO: cannot parse %q: incorrect length", value)
134146
}
135147

136148
year, err := parseField(value, abs[:dash1], "year", 4, -1)
@@ -157,13 +169,18 @@ func ParseISO(value string) (Date, error) {
157169
return Date{encode(t)}, nil
158170
}
159171

172+
var (
173+
timeRegex1 = regexp.MustCompile("^T[0-9][0-9].[0-9][0-9].[0-9][0-9]")
174+
timeRegex2 = regexp.MustCompile("^T[0-9]{2,6}")
175+
)
176+
160177
func parseField(value, field, name string, minLength, requiredLength int) (int, error) {
161178
if (minLength > 0 && len(field) < minLength) || (requiredLength > 0 && len(field) != requiredLength) {
162-
return 0, fmt.Errorf("Date.ParseISO: cannot parse %q: invalid %s", value, name)
179+
return 0, fmt.Errorf("date.ParseISO: cannot parse %q: invalid %s", value, name)
163180
}
164181
number, err := strconv.Atoi(field)
165182
if err != nil {
166-
return 0, fmt.Errorf("Date.ParseISO: cannot parse %q: invalid %s", value, name)
183+
return 0, fmt.Errorf("date.ParseISO: cannot parse %q: invalid %s", value, name)
167184
}
168185
return number, nil
169186
}

parse_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ func TestAutoParse(t *testing.T) {
2424
{"1969.12.31", 1969, time.December, 31},
2525
{"1969-12-31", 1969, time.December, 31},
2626
{"2000-02-28", 2000, time.February, 28},
27+
{"20000228", 2000, time.February, 28},
28+
{"2018-02-03T00:00:00Z", 2018, time.February, 3}, // datetime is allowed
29+
{"20180203T000000Z", 2018, time.February, 3}, // datetime is allowed
2730
{"+2000-02-29", 2000, time.February, 29},
31+
{"+20000229", 2000, time.February, 29},
2832
{"+02000-03-01", 2000, time.March, 1},
2933
{"+002004-02-28", 2004, time.February, 28},
3034
{"2004-02-29", 2004, time.February, 29},
@@ -54,7 +58,9 @@ func TestAutoParse(t *testing.T) {
5458
t.Errorf("ParseISO(%v) == %v, want (%v, %v, %v)", c.value, d, c.year, c.month, c.day)
5559
}
5660
}
61+
}
5762

63+
func TestAutoParse_errors(t *testing.T) {
5864
badCases := []string{
5965
"1234-05",
6066
"1234-5-6",
@@ -91,10 +97,15 @@ func TestParseISO(t *testing.T) {
9197
day int
9298
}{
9399
{"1969-12-31", 1969, time.December, 31},
100+
{"19691231", 1969, time.December, 31},
101+
{"2018-02-03T00:00:00Z", 2018, time.February, 3}, // datetime is allowed
102+
{"20180203T000000Z", 2018, time.February, 3}, // datetime is allowed
94103
{"+1970-01-01", 1970, time.January, 1},
95104
{"+01970-01-02", 1970, time.January, 2},
96105
{"2000-02-28", 2000, time.February, 28},
106+
{"20000228", 2000, time.February, 28},
97107
{"+2000-02-29", 2000, time.February, 29},
108+
{"+20000229", 2000, time.February, 29},
98109
{"+02000-03-01", 2000, time.March, 1},
99110
{"+002004-02-28", 2004, time.February, 28},
100111
{"2004-02-29", 2004, time.February, 29},
@@ -125,7 +136,9 @@ func TestParseISO(t *testing.T) {
125136
t.Errorf("ParseISO(%v) == %v, want (%v, %v, %v)", c.value, d, c.year, c.month, c.day)
126137
}
127138
}
139+
}
128140

141+
func TestParseISO_errors(t *testing.T) {
129142
badCases := []string{
130143
"1234-05",
131144
"1234-5-6",
@@ -143,6 +156,8 @@ func TestParseISO(t *testing.T) {
143156
"+10-11-12",
144157
"+100-02-03",
145158
"-123-05-06",
159+
"2018-02-03T0:0:0Z",
160+
"2018-02-03T0Z",
146161
}
147162
for _, c := range badCases {
148163
d, err := ParseISO(c)
@@ -205,7 +220,9 @@ func TestParse(t *testing.T) {
205220
t.Errorf("Parse(%v) == %v, want (%v, %v, %v)", c.value, d, c.year, c.month, c.day)
206221
}
207222
}
223+
}
208224

225+
func TestParse_errors(t *testing.T) {
209226
// Test inability to parse ISO 8601 expanded year format
210227
badCases := []string{
211228
"+1234-05-06",

0 commit comments

Comments
 (0)