Skip to content

Commit b4113cc

Browse files
committed
Date ParseISO & AutoParse now accept a date-time input (time is ignored)
1 parent 082ed93 commit b4113cc

File tree

2 files changed

+51
-28
lines changed

2 files changed

+51
-28
lines changed

parse.go

Lines changed: 22 additions & 5 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"
@@ -142,6 +143,9 @@ func MustParseISO(value string) Date {
142143
// minimum. A leading plus '+' sign is allowed and ignored. Basic format (without '-'
143144
// separators) is allowed.
144145
//
146+
// If a time field is present, it is ignored. For example, "2018-02-03T00:00:00Z" is parsed as
147+
// 3rd February 2018.
148+
//
145149
// For ordinal dates, the extended format (including '-') is supported, but the basic format
146150
// (without '-') is not supported because it could not be distinguished from the YYYYMMDD format.
147151
//
@@ -168,6 +172,14 @@ func parseISO(input, value string) (Date, error) {
168172
}
169173
}
170174

175+
tee := strings.IndexByte(abs, 'T')
176+
if tee == 8 || tee == 10 {
177+
if !timeRegex1.MatchString(abs[tee:]) && !timeRegex2.MatchString(abs[tee:]) {
178+
return 0, fmt.Errorf("date.ParseISO: date-time %q: not a time", value)
179+
}
180+
abs = abs[:tee]
181+
}
182+
171183
dash1 := strings.IndexByte(abs, '-')
172184
dash2 := strings.LastIndexByte(abs, '-')
173185

@@ -177,7 +189,7 @@ func parseISO(input, value string) (Date, error) {
177189
fm := ln - 4
178190
fd := ln - 2
179191
if fm < 0 || fd < 0 {
180-
return 0, fmt.Errorf("Date.ParseISO: cannot parse %q: too short", input)
192+
return 0, fmt.Errorf("date.ParseISO: cannot parse %q: too short", input)
181193
}
182194

183195
return parseYYYYMMDD(input, abs[:fm], abs[fm:fd], abs[fd:], sign)
@@ -191,7 +203,7 @@ func parseISO(input, value string) (Date, error) {
191203
fd1 := dash2 + 1
192204

193205
if abs[fm2] != '-' {
194-
return 0, fmt.Errorf("Date.ParseISO: cannot parse %q: incorrect syntax for date yyyy-mm-dd", input)
206+
return 0, fmt.Errorf("date.ParseISO: cannot parse %q: incorrect syntax for date yyyy-mm-dd", input)
195207
}
196208

197209
return parseYYYYMMDD(input, abs[:fy1], abs[fm1:fm2], abs[fd1:], sign)
@@ -202,7 +214,7 @@ func parseISO(input, value string) (Date, error) {
202214
fo1 := dash1 + 1
203215

204216
if len(abs) != fo1+3 {
205-
return 0, fmt.Errorf("Date.ParseISO: cannot parse %q: incorrect length for ordinal date yyyy-ooo", input)
217+
return 0, fmt.Errorf("date.ParseISO: cannot parse %q: incorrect length for ordinal date yyyy-ooo", input)
206218
}
207219

208220
return parseYYYYOOO(input, abs[:fy1], abs[fo1:], sign)
@@ -215,7 +227,7 @@ func parseYYYYMMDD(input, yyyy, mm, dd string, sign int) (Date, error) {
215227

216228
err := errors.Join(e1, e2, e3)
217229
if err != nil {
218-
return 0, fmt.Errorf("Date.ParseISO: cannot parse %q: %w", input, err)
230+
return 0, fmt.Errorf("date.ParseISO: cannot parse %q: %w", input, err)
219231
}
220232

221233
t := time.Date(sign*year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
@@ -229,14 +241,19 @@ func parseYYYYOOO(input, yyyy, ooo string, sign int) (Date, error) {
229241

230242
err := errors.Join(e1, e2)
231243
if err != nil {
232-
return 0, fmt.Errorf("Date.ParseISO: cannot parse ordinal date %q: %w", input, err)
244+
return 0, fmt.Errorf("date.ParseISO: cannot parse ordinal date %q: %w", input, err)
233245
}
234246

235247
t := time.Date(sign*year, time.January, ordinal, 0, 0, 0, 0, time.UTC)
236248

237249
return encode(t), nil
238250
}
239251

252+
var (
253+
timeRegex1 = regexp.MustCompile("^T[0-9][0-9].[0-9][0-9].[0-9][0-9]")
254+
timeRegex2 = regexp.MustCompile("^T[0-9]{2,6}")
255+
)
256+
240257
func parseField(field, name string, minLength, requiredLength int) (int, error) {
241258
if (minLength > 0 && len(field) < minLength) || (requiredLength > 0 && len(field) != requiredLength) {
242259
return 0, fmt.Errorf("%s has wrong length", name)

parse_test.go

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ func TestAutoParse_errors(t *testing.T) {
133133
"+10-11-12",
134134
"+100-02-03",
135135
"-123-05-06",
136+
"20210506T0Z",
137+
"2021-05-06T0:0:0Z",
136138
"--",
137139
"",
138140
" ",
@@ -161,6 +163,7 @@ func TestParseISO(t *testing.T) {
161163
{value: "+1970-01-01", year: 1970, month: time.January, day: 1},
162164
{value: "+01970-01-02", year: 1970, month: time.January, day: 2},
163165
{value: "2000-02-28", year: 2000, month: time.February, day: 28},
166+
{value: "2018-02-03T00:00:00Z", year: 2018, month: time.February, day: 3},
164167
{value: "+2000-02-29", year: 2000, month: time.February, day: 29},
165168
{value: "+02000-03-01", year: 2000, month: time.March, day: 1},
166169
{value: "+002004-02-28", year: 2004, month: time.February, day: 28},
@@ -190,6 +193,7 @@ func TestParseISO(t *testing.T) {
190193
{value: "12340506", year: 1234, month: time.May, day: 6},
191194
{value: "+12340506", year: 1234, month: time.May, day: 6},
192195
{value: "-00191012", year: -19, month: time.October, day: 12},
196+
{value: "20210506T010203Z", year: 2021, month: time.May, day: 6},
193197
}
194198
for i, c := range cases {
195199
t.Run(fmt.Sprintf("%d %s", i, c.value), func(t *testing.T) {
@@ -207,29 +211,31 @@ func TestParseISO_errors(t *testing.T) {
207211
value string
208212
want string
209213
}{
210-
{value: ``, want: `Date.ParseISO: cannot parse "": ` + "too short"},
211-
{value: `-`, want: `Date.ParseISO: cannot parse "-": ` + "too short"},
212-
{value: `z`, want: `Date.ParseISO: cannot parse "z": ` + "too short"},
213-
{value: `z--`, want: `Date.ParseISO: cannot parse "z--": ` + "year has wrong length\nmonth has wrong length\nday has wrong length"},
214-
{value: `not-a-date`, want: `Date.ParseISO: cannot parse "not-a-date": ` + "year has wrong length\nmonth has wrong length\nday has wrong length"},
215-
{value: `foot-of-og`, want: `Date.ParseISO: cannot parse "foot-of-og": ` + "invalid year\ninvalid month\ninvalid day"},
216-
{value: `215-08-15`, want: `Date.ParseISO: cannot parse "215-08-15": year has wrong length`},
217-
{value: "1234-05", want: `Date.ParseISO: cannot parse "1234-05": incorrect length for ordinal date yyyy-ooo`},
218-
{value: "1234-5-6", want: `Date.ParseISO: cannot parse "1234-5-6": ` + "month has wrong length\nday has wrong length"},
219-
{value: "1234-05-6", want: `Date.ParseISO: cannot parse "1234-05-6": day has wrong length`},
220-
{value: "1234-5-06", want: `Date.ParseISO: cannot parse "1234-5-06": month has wrong length`},
221-
{value: "1234/05/06", want: `Date.ParseISO: cannot parse "1234/05/06": ` + "invalid year\ninvalid month"},
222-
{value: "1234-0A-06", want: `Date.ParseISO: cannot parse "1234-0A-06": invalid month`},
223-
{value: "1234-05-0B", want: `Date.ParseISO: cannot parse "1234-05-0B": invalid day`},
224-
{value: "1234-05-06trailing", want: `Date.ParseISO: cannot parse "1234-05-06trailing": day has wrong length`},
225-
{value: "padding1234-05-06", want: `Date.ParseISO: cannot parse "padding1234-05-06": invalid year`},
226-
{value: "1-02-03", want: `Date.ParseISO: cannot parse "1-02-03": year has wrong length`},
227-
{value: "10-11-12", want: `Date.ParseISO: cannot parse "10-11-12": year has wrong length`},
228-
{value: "100-02-03", want: `Date.ParseISO: cannot parse "100-02-03": year has wrong length`},
229-
{value: "+1-02-03", want: `Date.ParseISO: cannot parse "+1-02-03": year has wrong length`},
230-
{value: "+10-11-12", want: `Date.ParseISO: cannot parse "+10-11-12": year has wrong length`},
231-
{value: "+100-02-03", want: `Date.ParseISO: cannot parse "+100-02-03": year has wrong length`},
232-
{value: "-123-05-06", want: `Date.ParseISO: cannot parse "-123-05-06": year has wrong length`},
214+
{value: ``, want: `date.ParseISO: cannot parse "": ` + "too short"},
215+
{value: `-`, want: `date.ParseISO: cannot parse "-": ` + "too short"},
216+
{value: `z`, want: `date.ParseISO: cannot parse "z": ` + "too short"},
217+
{value: `z--`, want: `date.ParseISO: cannot parse "z--": ` + "year has wrong length\nmonth has wrong length\nday has wrong length"},
218+
{value: `not-a-date`, want: `date.ParseISO: cannot parse "not-a-date": ` + "year has wrong length\nmonth has wrong length\nday has wrong length"},
219+
{value: `foot-of-og`, want: `date.ParseISO: cannot parse "foot-of-og": ` + "invalid year\ninvalid month\ninvalid day"},
220+
{value: `215-08-15`, want: `date.ParseISO: cannot parse "215-08-15": year has wrong length`},
221+
{value: "1234-05", want: `date.ParseISO: cannot parse "1234-05": incorrect length for ordinal date yyyy-ooo`},
222+
{value: "1234-5-6", want: `date.ParseISO: cannot parse "1234-5-6": ` + "month has wrong length\nday has wrong length"},
223+
{value: "1234-05-6", want: `date.ParseISO: cannot parse "1234-05-6": day has wrong length`},
224+
{value: "1234-5-06", want: `date.ParseISO: cannot parse "1234-5-06": month has wrong length`},
225+
{value: "1234/05/06", want: `date.ParseISO: cannot parse "1234/05/06": ` + "invalid year\ninvalid month"},
226+
{value: "1234-0A-06", want: `date.ParseISO: cannot parse "1234-0A-06": invalid month`},
227+
{value: "1234-05-0B", want: `date.ParseISO: cannot parse "1234-05-0B": invalid day`},
228+
{value: "1234-05-06trailing", want: `date.ParseISO: cannot parse "1234-05-06trailing": day has wrong length`},
229+
{value: "padding1234-05-06", want: `date.ParseISO: cannot parse "padding1234-05-06": invalid year`},
230+
{value: "1-02-03", want: `date.ParseISO: cannot parse "1-02-03": year has wrong length`},
231+
{value: "10-11-12", want: `date.ParseISO: cannot parse "10-11-12": year has wrong length`},
232+
{value: "100-02-03", want: `date.ParseISO: cannot parse "100-02-03": year has wrong length`},
233+
{value: "+1-02-03", want: `date.ParseISO: cannot parse "+1-02-03": year has wrong length`},
234+
{value: "+10-11-12", want: `date.ParseISO: cannot parse "+10-11-12": year has wrong length`},
235+
{value: "+100-02-03", want: `date.ParseISO: cannot parse "+100-02-03": year has wrong length`},
236+
{value: "-123-05-06", want: `date.ParseISO: cannot parse "-123-05-06": year has wrong length`},
237+
{value: "2018-02-03T0:0:0Z", want: `date.ParseISO: date-time "2018-02-03T0:0:0Z": not a time`},
238+
{value: "2018-02-03T0Z", want: `date.ParseISO: date-time "2018-02-03T0Z": not a time`},
233239
}
234240
for i, c := range cases {
235241
t.Run(fmt.Sprintf("%d %s", i, c.value), func(t *testing.T) {

0 commit comments

Comments
 (0)