Unify/fix timezone offset/name states

* Merge duplicate states (fixes lots of edge cases)
* Support for +00:00 is consistent with +0000 now
* Support (timezone description) after any offset/name
* Update tests to cover positive/negative cases
* Update example with new supported formats
This commit is contained in:
Klondike Dragon 2023-12-30 01:10:43 -07:00
parent fd21b1ee3e
commit c4de5d4f6a
4 changed files with 763 additions and 564 deletions

View File

@ -127,6 +127,15 @@ var examples = []string{
"Mon Aug 10 15:44:11 UTC+0000 2015", "Mon Aug 10 15:44:11 UTC+0000 2015",
// git log default date format // git log default date format
"Thu Apr 7 15:13:13 2005 -0700", "Thu Apr 7 15:13:13 2005 -0700",
// variants of git log default date format
"Thu Apr 7 15:13:13 2005 -07:00",
"Thu Apr 7 15:13:13 2005 -07:00 PST",
"Thu Apr 7 15:13:13 2005 -07:00 PST (Pacific Standard Time)",
"Thu Apr 7 15:13:13 -0700 2005",
"Thu Apr 7 15:13:13 -07:00 2005",
"Thu Apr 7 15:13:13 -0700 PST 2005",
"Thu Apr 7 15:13:13 -07:00 PST 2005",
"Thu Apr 7 15:13:13 PST 2005",
// Variants of the above with a (full time zone description) // Variants of the above with a (full time zone description)
"Fri Jul 3 2015 06:04:07 PST-0700 (Pacific Daylight Time)", "Fri Jul 3 2015 06:04:07 PST-0700 (Pacific Daylight Time)",
"Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)", "Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)",
@ -149,11 +158,15 @@ var examples = []string{
"04/08/2014, 22:05", "04/08/2014, 22:05",
"4/8/14 22:05", "4/8/14 22:05",
"04/2/2014 03:00:51", "04/2/2014 03:00:51",
"8/8/1965 12:00:00 AM",
"8/8/1965 01:00:01 PM",
"8/8/1965 01:00 PM",
"8/8/1965 1:00 PM", "8/8/1965 1:00 PM",
"8/8/1965 01:00 PM",
"8/8/1965 12:00 AM", "8/8/1965 12:00 AM",
"8/8/1965 12:00:00AM",
"8/8/1965 01:00:01 PM",
"8/8/1965 01:00:01PM -0700",
"8/8/1965 13:00:01 -0700 PST",
"8/8/1965 01:00:01 PM -0700 PST",
"8/8/1965 01:00:01 PM -07:00 PST (Pacific Standard Time)",
"4/02/2014 03:00:51", "4/02/2014 03:00:51",
"03/19/2012 10:11:59", "03/19/2012 10:11:59",
"03/19/2012 10:11:59.3186369", "03/19/2012 10:11:59.3186369",
@ -308,9 +321,9 @@ func main() {
} }
/* /*
+----------------------------------------------------------+-----------------------------------------+ +------------------------------------------------------------+-----------------------------------------+
| Input | Parsed, and Output as %v | | Input | Parsed, and Output as %v |
+----------------------------------------------------------+-----------------------------------------+ +------------------------------------------------------------+-----------------------------------------+
| May 8, 2009 5:57:51 PM | 2009-05-08 17:57:51 +0000 UTC | | May 8, 2009 5:57:51 PM | 2009-05-08 17:57:51 +0000 UTC |
| oct 7, 1970 | 1970-10-07 00:00:00 +0000 UTC | | oct 7, 1970 | 1970-10-07 00:00:00 +0000 UTC |
| oct 7, '70 | 1970-10-07 00:00:00 +0000 UTC | | oct 7, '70 | 1970-10-07 00:00:00 +0000 UTC |
@ -352,6 +365,14 @@ func main() {
| Monday 02 Jan 2006 03:04:05 PM MST | 2006-01-02 15:04:05 +0000 MST | | Monday 02 Jan 2006 03:04:05 PM MST | 2006-01-02 15:04:05 +0000 MST |
| Mon Aug 10 15:44:11 UTC+0000 2015 | 2015-08-10 15:44:11 +0000 UTC | | Mon Aug 10 15:44:11 UTC+0000 2015 | 2015-08-10 15:44:11 +0000 UTC |
| Thu Apr 7 15:13:13 2005 -0700 | 2005-04-07 15:13:13 -0700 -0700 | | Thu Apr 7 15:13:13 2005 -0700 | 2005-04-07 15:13:13 -0700 -0700 |
| Thu Apr 7 15:13:13 2005 -07:00 | 2005-04-07 15:13:13 -0700 -0700 |
| Thu Apr 7 15:13:13 2005 -07:00 PST | 2005-04-07 15:13:13 -0700 PST |
| Thu Apr 7 15:13:13 2005 -07:00 PST (Pacific Standard Time) | 2005-04-07 15:13:13 -0700 PST |
| Thu Apr 7 15:13:13 -0700 2005 | 2005-04-07 15:13:13 -0700 -0700 |
| Thu Apr 7 15:13:13 -07:00 2005 | 2005-04-07 15:13:13 -0700 -0700 |
| Thu Apr 7 15:13:13 -0700 PST 2005 | 2005-04-07 15:13:13 -0700 PST |
| Thu Apr 7 15:13:13 -07:00 PST 2005 | 2005-04-07 15:13:13 -0700 PST |
| Thu Apr 7 15:13:13 PST 2005 | 2005-04-07 15:13:13 +0000 PST |
| Fri Jul 3 2015 06:04:07 PST-0700 (Pacific Daylight Time) | 2015-07-03 06:04:07 -0700 PST | | Fri Jul 3 2015 06:04:07 PST-0700 (Pacific Daylight Time) | 2015-07-03 06:04:07 -0700 PST |
| Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) | 2015-07-03 18:04:07 +0100 +0100 | | Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) | 2015-07-03 18:04:07 +0100 +0100 |
| Sun, 3 Jan 2021 00:12:23 +0800 (GMT+08:00) | 2021-01-03 00:12:23 +0800 +0800 | | Sun, 3 Jan 2021 00:12:23 +0800 (GMT+08:00) | 2021-01-03 00:12:23 +0800 +0800 |
@ -370,11 +391,15 @@ func main() {
| 04/08/2014, 22:05 | 2014-04-08 22:05:00 +0000 UTC | | 04/08/2014, 22:05 | 2014-04-08 22:05:00 +0000 UTC |
| 4/8/14 22:05 | 2014-04-08 22:05:00 +0000 UTC | | 4/8/14 22:05 | 2014-04-08 22:05:00 +0000 UTC |
| 04/2/2014 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | | 04/2/2014 03:00:51 | 2014-04-02 03:00:51 +0000 UTC |
| 8/8/1965 12:00:00 AM | 1965-08-08 00:00:00 +0000 UTC |
| 8/8/1965 01:00:01 PM | 1965-08-08 13:00:01 +0000 UTC |
| 8/8/1965 01:00 PM | 1965-08-08 13:00:00 +0000 UTC |
| 8/8/1965 1:00 PM | 1965-08-08 13:00:00 +0000 UTC | | 8/8/1965 1:00 PM | 1965-08-08 13:00:00 +0000 UTC |
| 8/8/1965 01:00 PM | 1965-08-08 13:00:00 +0000 UTC |
| 8/8/1965 12:00 AM | 1965-08-08 00:00:00 +0000 UTC | | 8/8/1965 12:00 AM | 1965-08-08 00:00:00 +0000 UTC |
| 8/8/1965 12:00:00AM | 1965-08-08 00:00:00 +0000 UTC |
| 8/8/1965 01:00:01 PM | 1965-08-08 13:00:01 +0000 UTC |
| 8/8/1965 01:00:01PM -0700 | 1965-08-08 13:00:01 -0700 -0700 |
| 8/8/1965 13:00:01 -0700 PST | 1965-08-08 13:00:01 -0700 PST |
| 8/8/1965 01:00:01 PM -0700 PST | 1965-08-08 13:00:01 -0700 PST |
| 8/8/1965 01:00:01 PM -07:00 PST (Pacific Standard Time) | 1965-08-08 13:00:01 -0700 PST |
| 4/02/2014 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | | 4/02/2014 03:00:51 | 2014-04-02 03:00:51 +0000 UTC |
| 03/19/2012 10:11:59 | 2012-03-19 10:11:59 +0000 UTC | | 03/19/2012 10:11:59 | 2012-03-19 10:11:59 +0000 UTC |
| 03/19/2012 10:11:59.3186369 | 2012-03-19 10:11:59.3186369 +0000 UTC | | 03/19/2012 10:11:59.3186369 | 2012-03-19 10:11:59.3186369 +0000 UTC |
@ -476,7 +501,7 @@ func main() {
| 1384216367189 | 2013-11-12 00:32:47.189 +0000 UTC | | 1384216367189 | 2013-11-12 00:32:47.189 +0000 UTC |
| 1384216367111222 | 2013-11-12 00:32:47.111222 +0000 UTC | | 1384216367111222 | 2013-11-12 00:32:47.111222 +0000 UTC |
| 1384216367111222333 | 2013-11-12 00:32:47.111222333 +0000 UTC | | 1384216367111222333 | 2013-11-12 00:32:47.111222333 +0000 UTC |
+----------------------------------------------------------+-----------------------------------------+ +------------------------------------------------------------+-----------------------------------------+
*/ */
``` ```

View File

@ -60,6 +60,15 @@ var examples = []string{
"Mon Aug 10 15:44:11 UTC+0000 2015", "Mon Aug 10 15:44:11 UTC+0000 2015",
// git log default date format // git log default date format
"Thu Apr 7 15:13:13 2005 -0700", "Thu Apr 7 15:13:13 2005 -0700",
// variants of git log default date format
"Thu Apr 7 15:13:13 2005 -07:00",
"Thu Apr 7 15:13:13 2005 -07:00 PST",
"Thu Apr 7 15:13:13 2005 -07:00 PST (Pacific Standard Time)",
"Thu Apr 7 15:13:13 -0700 2005",
"Thu Apr 7 15:13:13 -07:00 2005",
"Thu Apr 7 15:13:13 -0700 PST 2005",
"Thu Apr 7 15:13:13 -07:00 PST 2005",
"Thu Apr 7 15:13:13 PST 2005",
// Variants of the above with a (full time zone description) // Variants of the above with a (full time zone description)
"Fri Jul 3 2015 06:04:07 PST-0700 (Pacific Daylight Time)", "Fri Jul 3 2015 06:04:07 PST-0700 (Pacific Daylight Time)",
"Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)", "Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)",
@ -82,11 +91,15 @@ var examples = []string{
"04/08/2014, 22:05", "04/08/2014, 22:05",
"4/8/14 22:05", "4/8/14 22:05",
"04/2/2014 03:00:51", "04/2/2014 03:00:51",
"8/8/1965 12:00:00 AM",
"8/8/1965 01:00:01 PM",
"8/8/1965 01:00 PM",
"8/8/1965 1:00 PM", "8/8/1965 1:00 PM",
"8/8/1965 01:00 PM",
"8/8/1965 12:00 AM", "8/8/1965 12:00 AM",
"8/8/1965 12:00:00AM",
"8/8/1965 01:00:01 PM",
"8/8/1965 01:00:01PM -0700",
"8/8/1965 13:00:01 -0700 PST",
"8/8/1965 01:00:01 PM -0700 PST",
"8/8/1965 01:00:01 PM -07:00 PST (Pacific Standard Time)",
"4/02/2014 03:00:51", "4/02/2014 03:00:51",
"03/19/2012 10:11:59", "03/19/2012 10:11:59",
"03/19/2012 10:11:59.3186369", "03/19/2012 10:11:59.3186369",
@ -241,9 +254,9 @@ func main() {
} }
/* /*
+----------------------------------------------------------+-----------------------------------------+ +------------------------------------------------------------+-----------------------------------------+
| Input | Parsed, and Output as %v | | Input | Parsed, and Output as %v |
+----------------------------------------------------------+-----------------------------------------+ +------------------------------------------------------------+-----------------------------------------+
| May 8, 2009 5:57:51 PM | 2009-05-08 17:57:51 +0000 UTC | | May 8, 2009 5:57:51 PM | 2009-05-08 17:57:51 +0000 UTC |
| oct 7, 1970 | 1970-10-07 00:00:00 +0000 UTC | | oct 7, 1970 | 1970-10-07 00:00:00 +0000 UTC |
| oct 7, '70 | 1970-10-07 00:00:00 +0000 UTC | | oct 7, '70 | 1970-10-07 00:00:00 +0000 UTC |
@ -285,6 +298,14 @@ func main() {
| Monday 02 Jan 2006 03:04:05 PM MST | 2006-01-02 15:04:05 +0000 MST | | Monday 02 Jan 2006 03:04:05 PM MST | 2006-01-02 15:04:05 +0000 MST |
| Mon Aug 10 15:44:11 UTC+0000 2015 | 2015-08-10 15:44:11 +0000 UTC | | Mon Aug 10 15:44:11 UTC+0000 2015 | 2015-08-10 15:44:11 +0000 UTC |
| Thu Apr 7 15:13:13 2005 -0700 | 2005-04-07 15:13:13 -0700 -0700 | | Thu Apr 7 15:13:13 2005 -0700 | 2005-04-07 15:13:13 -0700 -0700 |
| Thu Apr 7 15:13:13 2005 -07:00 | 2005-04-07 15:13:13 -0700 -0700 |
| Thu Apr 7 15:13:13 2005 -07:00 PST | 2005-04-07 15:13:13 -0700 PST |
| Thu Apr 7 15:13:13 2005 -07:00 PST (Pacific Standard Time) | 2005-04-07 15:13:13 -0700 PST |
| Thu Apr 7 15:13:13 -0700 2005 | 2005-04-07 15:13:13 -0700 -0700 |
| Thu Apr 7 15:13:13 -07:00 2005 | 2005-04-07 15:13:13 -0700 -0700 |
| Thu Apr 7 15:13:13 -0700 PST 2005 | 2005-04-07 15:13:13 -0700 PST |
| Thu Apr 7 15:13:13 -07:00 PST 2005 | 2005-04-07 15:13:13 -0700 PST |
| Thu Apr 7 15:13:13 PST 2005 | 2005-04-07 15:13:13 +0000 PST |
| Fri Jul 3 2015 06:04:07 PST-0700 (Pacific Daylight Time) | 2015-07-03 06:04:07 -0700 PST | | Fri Jul 3 2015 06:04:07 PST-0700 (Pacific Daylight Time) | 2015-07-03 06:04:07 -0700 PST |
| Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) | 2015-07-03 18:04:07 +0100 +0100 | | Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) | 2015-07-03 18:04:07 +0100 +0100 |
| Sun, 3 Jan 2021 00:12:23 +0800 (GMT+08:00) | 2021-01-03 00:12:23 +0800 +0800 | | Sun, 3 Jan 2021 00:12:23 +0800 (GMT+08:00) | 2021-01-03 00:12:23 +0800 +0800 |
@ -303,11 +324,15 @@ func main() {
| 04/08/2014, 22:05 | 2014-04-08 22:05:00 +0000 UTC | | 04/08/2014, 22:05 | 2014-04-08 22:05:00 +0000 UTC |
| 4/8/14 22:05 | 2014-04-08 22:05:00 +0000 UTC | | 4/8/14 22:05 | 2014-04-08 22:05:00 +0000 UTC |
| 04/2/2014 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | | 04/2/2014 03:00:51 | 2014-04-02 03:00:51 +0000 UTC |
| 8/8/1965 12:00:00 AM | 1965-08-08 00:00:00 +0000 UTC |
| 8/8/1965 01:00:01 PM | 1965-08-08 13:00:01 +0000 UTC |
| 8/8/1965 01:00 PM | 1965-08-08 13:00:00 +0000 UTC |
| 8/8/1965 1:00 PM | 1965-08-08 13:00:00 +0000 UTC | | 8/8/1965 1:00 PM | 1965-08-08 13:00:00 +0000 UTC |
| 8/8/1965 01:00 PM | 1965-08-08 13:00:00 +0000 UTC |
| 8/8/1965 12:00 AM | 1965-08-08 00:00:00 +0000 UTC | | 8/8/1965 12:00 AM | 1965-08-08 00:00:00 +0000 UTC |
| 8/8/1965 12:00:00AM | 1965-08-08 00:00:00 +0000 UTC |
| 8/8/1965 01:00:01 PM | 1965-08-08 13:00:01 +0000 UTC |
| 8/8/1965 01:00:01PM -0700 | 1965-08-08 13:00:01 -0700 -0700 |
| 8/8/1965 13:00:01 -0700 PST | 1965-08-08 13:00:01 -0700 PST |
| 8/8/1965 01:00:01 PM -0700 PST | 1965-08-08 13:00:01 -0700 PST |
| 8/8/1965 01:00:01 PM -07:00 PST (Pacific Standard Time) | 1965-08-08 13:00:01 -0700 PST |
| 4/02/2014 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | | 4/02/2014 03:00:51 | 2014-04-02 03:00:51 +0000 UTC |
| 03/19/2012 10:11:59 | 2012-03-19 10:11:59 +0000 UTC | | 03/19/2012 10:11:59 | 2012-03-19 10:11:59 +0000 UTC |
| 03/19/2012 10:11:59.3186369 | 2012-03-19 10:11:59.3186369 +0000 UTC | | 03/19/2012 10:11:59.3186369 | 2012-03-19 10:11:59.3186369 +0000 UTC |
@ -409,5 +434,5 @@ func main() {
| 1384216367189 | 2013-11-12 00:32:47.189 +0000 UTC | | 1384216367189 | 2013-11-12 00:32:47.189 +0000 UTC |
| 1384216367111222 | 2013-11-12 00:32:47.111222 +0000 UTC | | 1384216367111222 | 2013-11-12 00:32:47.111222 +0000 UTC |
| 1384216367111222333 | 2013-11-12 00:32:47.111222333 +0000 UTC | | 1384216367111222333 | 2013-11-12 00:32:47.111222333 +0000 UTC |
+----------------------------------------------------------+-----------------------------------------+ +------------------------------------------------------------+-----------------------------------------+
*/ */

View File

@ -123,21 +123,19 @@ const (
timeWsAlpha timeWsAlpha
timeWsAlphaRParen timeWsAlphaRParen
timeWsAlphaWs timeWsAlphaWs
timeWsAlphaZoneOffset // 6 timeWsAlphaWsYear
timeWsAlphaZoneOffset // 7
timeWsAlphaZoneOffsetWs timeWsAlphaZoneOffsetWs
timeWsAlphaZoneOffsetWsYear timeWsAlphaZoneOffsetWsYear
timeWsAlphaZoneOffsetWsExtra timeWsOffsetWsTZDescInParen // overloaded, can come from timeWsAlphaWs, timeWsAlphaZoneOffsetWs, timeWsOffsetWs, timeWsOffsetWsAlphaZoneWs
timeWsAMPMMaybe timeWsAMPMMaybe
timeWsAMPM // 11 timeWsAMPM // 12
timeWsOffset timeWsOffset // overloaded, can come from timeWs or timeWsYear
timeWsOffsetWs // 13 timeWsOffsetWs // 14
timeWsOffsetColonAlpha timeWsOffsetWsYear // overloaded, can come from timeWsOffsetWs or timeWsOffsetWsAlphaZoneWs (ensures year is only set once)
timeWsOffsetColon timeWsOffsetWsAlphaZone
timeWsYear // 16 timeWsOffsetWsAlphaZoneWs
timeWsYearOffset timeWsYear
timeOffset
timeOffsetColon
timeOffsetColonAlpha
timePeriod timePeriod
timePeriodAMPM timePeriodAMPM
timeZ timeZ
@ -613,14 +611,9 @@ iterRunes:
case dateYearDashDashOffset: case dateYearDashDashOffset:
// 2020-07-20+00:00 // 2020-07-20+00:00
switch r { if r != ':' && !unicode.IsDigit(r) {
case ':':
p.set(p.offseti, "-07:00")
default:
if !unicode.IsDigit(r) {
return p, p.unknownErr(datestr) return p, p.unknownErr(datestr)
} }
}
case dateYearDashAlpha: case dateYearDashAlpha:
// dateYearDashAlpha // dateYearDashAlpha
@ -1234,14 +1227,9 @@ iterRunes:
case dateDigitDotDotOffset: case dateDigitDotDotOffset:
// 2020-07-20+00:00 // 2020-07-20+00:00
switch r { if r != ':' && !unicode.IsDigit(r) {
case ':':
p.set(p.offseti, "-07:00")
default:
if !unicode.IsDigit(r) {
return p, p.unknownErr(datestr) return p, p.unknownErr(datestr)
} }
}
case dateAlpha: case dateAlpha:
// dateAlphaWs // dateAlphaWs
@ -1251,6 +1239,7 @@ iterRunes:
// Mon Jan 02 15:04:05 2006 -0700 // Mon Jan 02 15:04:05 2006 -0700
// Mon Aug 10 15:44:11 UTC+0100 2015 // Mon Aug 10 15:44:11 UTC+0100 2015
// Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)
// Fri Jul 03 2015 18:04:07 GMT+01:00 (GMT Daylight Time)
// dateAlphaWsDigit // dateAlphaWsDigit
// May 8, 2009 5:57:51 PM // May 8, 2009 5:57:51 PM
// oct 1, 1970 // oct 1, 1970
@ -1374,6 +1363,7 @@ iterRunes:
// Mon Jan 02 15:04:05 -0700 2006 // Mon Jan 02 15:04:05 -0700 2006
// Mon Jan 02 15:04:05 2006 -0700 // Mon Jan 02 15:04:05 2006 -0700
// Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)
// Fri Jul 03 2015 18:04:07 GMT+01:00 (GMT Daylight Time)
// Mon Aug 10 15:44:11 UTC+0100 2015 // Mon Aug 10 15:44:11 UTC+0100 2015
// dateAlphaWsDigit // dateAlphaWsDigit
// May 8, 2009 5:57:51 PM // May 8, 2009 5:57:51 PM
@ -1445,6 +1435,7 @@ iterRunes:
// May 8 17:57:51 2009 // May 8 17:57:51 2009
// May 08 17:57:51 2009 // May 08 17:57:51 2009
// Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) // Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)
// Jul 03 2015 18:04:07 GMT+01:00 (GMT Daylight Time)
if r == ':' { if r == ':' {
// Guessed wrong; was not a year // Guessed wrong; was not a year
p.yeari = 0 p.yeari = 0
@ -1702,7 +1693,6 @@ iterRunes:
} }
} }
iterTimeRunes:
for ; i < len(p.datestr); i++ { for ; i < len(p.datestr); i++ {
r := rune(p.datestr[i]) r := rune(p.datestr[i])
@ -1718,14 +1708,15 @@ iterRunes:
// 05:24:37 PM // 05:24:37 PM
// 06:20:00 UTC // 06:20:00 UTC
// 06:20:00 UTC-05 // 06:20:00 UTC-05
// timeWsYear
// 15:04:05 2008
// timeWsOffset
// 00:12:00 +0000 UTC // 00:12:00 +0000 UTC
// 22:18:00 +0000 UTC m=+0.000000001 // 22:18:00 +0000 UTC m=+0.000000001
// 15:04:05 -0700
// 15:04:05 -07:00
// 15:04:05 2008
// timeOffset
// 03:21:51+00:00 // 03:21:51+00:00
// 19:55:00+0100 // 19:55:00+0100
// 15:04:05 -0700
// 15:04:05 -07:00
// timePeriod // timePeriod
// 17:24:37.3186369 // 17:24:37.3186369
// 00:07:31.945167 // 00:07:31.945167
@ -1738,7 +1729,8 @@ iterRunes:
switch r { switch r {
case '-', '+': case '-', '+':
// 03:21:51+00:00 // 03:21:51+00:00
p.stateTime = timeOffset p.offseti = i
p.stateTime = timeWsOffset
if p.seci == 0 { if p.seci == 0 {
// 22:18+0530 // 22:18+0530
p.minlen = i - p.mini p.minlen = i - p.mini
@ -1746,10 +1738,11 @@ iterRunes:
p.seclen = i - p.seci p.seclen = i - p.seci
} else if p.msi > 0 && p.mslen == 0 { } else if p.msi > 0 && p.mslen == 0 {
p.mslen = i - p.msi p.mslen = i - p.msi
} else if p.parsedAMPM {
// time fully parsed, plus AM/PM indicator, this is OK
} else { } else {
return p, p.unknownErr(datestr) return p, p.unknownErr(datestr)
} }
p.offseti = i
case '.', ',': case '.', ',':
// NOTE: go 1.20 can now parse a string that has a comma delimiter properly // NOTE: go 1.20 can now parse a string that has a comma delimiter properly
p.stateTime = timePeriod p.stateTime = timePeriod
@ -1782,9 +1775,9 @@ iterRunes:
return p, p.unknownErr(datestr) return p, p.unknownErr(datestr)
} }
} else { } else {
// Could be AM/PM // Could be AM/PM (followed by whitespace or an offset)
isLower := r == 'a' || r == 'p' isLower := r == 'a' || r == 'p'
isTwoLetterWord := ((i+2) == len(p.datestr) || p.nextIs(i+1, ' ')) isTwoLetterWord := ((i+2) == len(p.datestr) || (len(p.datestr) > i+2 && (p.datestr[i+2] == ' ' || p.datestr[i+2] == '+' || p.datestr[i+2] == '-')))
switch { switch {
case isLower && p.nextIs(i, 'm') && isTwoLetterWord && !p.parsedAMPM: case isLower && p.nextIs(i, 'm') && isTwoLetterWord && !p.parsedAMPM:
if !p.coalesceTime(i) { if !p.coalesceTime(i) {
@ -1838,37 +1831,27 @@ iterRunes:
p.stateTime = timePeriod p.stateTime = timePeriod
} }
} }
case timeOffset:
// 19:55:00+0100
// timeOffsetColon
// 15:04:05+07:00
// 15:04:05-07:00
if r == ':' {
p.stateTime = timeOffsetColon
} else if !unicode.IsDigit(r) {
return p, p.unknownErr(datestr)
}
case timeWs: case timeWs:
// timeWsAlpha // timeWsAlpha
// 06:20:00 UTC // 06:20:00 UTC
// 06:20:00 UTC-05 // 06:20:00 UTC-05
// 15:44:11 UTC+0100 2015 // 15:44:11 UTC+0100 2015
// 18:04:07 GMT+0100 (GMT Daylight Time) // 18:04:07 GMT+0100 (GMT Daylight Time)
// 18:04:07 GMT+01:00 (GMT Daylight Time)
// 17:57:51 MST 2009 // 17:57:51 MST 2009
// timeWsAMPMMaybe // timeWsAMPMMaybe
// 05:24:37 PM // 05:24:37 PM
// timeWsOffset // timeWsOffset
// 15:04:05 -0700 // 15:04:05 -0700
// 00:12:00 +0000 UTC // 00:12:00 +0000 UTC
// timeWsOffsetColon
// 15:04:05 -07:00 // 15:04:05 -07:00
// 17:57:51 -0700 2009 // 17:57:51 -0700 2009
// timeWsOffsetColonAlpha
// 00:12:00 +00:00 UTC // 00:12:00 +00:00 UTC
// timeWsYear // timeWsYear
// 00:12:00 2008 // 00:12:00 2008
// timeWsYearOffset // merge to state timeWsOffset
// 00:12:00 2008 -0700 // 00:12:00 2008 -0700
// 00:12:00 2008 -07:00
// timeZ // timeZ
// 15:04:05.99Z // 15:04:05.99Z
switch r { switch r {
@ -1889,8 +1872,12 @@ iterRunes:
p.stateTime = timeWsAlpha p.stateTime = timeWsAlpha
} else if unicode.IsDigit(r) { } else if unicode.IsDigit(r) {
// 00:12:00 2008 // 00:12:00 2008
if p.yeari == 0 {
p.stateTime = timeWsYear p.stateTime = timeWsYear
p.yeari = i p.yeari = i
} else {
return p, p.unknownErr(datestr)
}
} else if r == '(' { } else if r == '(' {
// (start of time zone description, ignore) // (start of time zone description, ignore)
} else { } else {
@ -1898,19 +1885,33 @@ iterRunes:
} }
} }
case timeWsYear: case timeWsYear:
// timeWsYearOffset // merge to state timeWsOffset
// 00:12:00 2008 -0700 // 00:12:00 2008 -0700
// 00:12:00 2008 -07:00
switch r { switch r {
case ' ': case ' ':
if p.yearlen == 0 {
p.yearlen = i - p.yeari p.yearlen = i - p.yeari
if !p.setYear() { if !p.setYear() {
return p, p.unknownErr(datestr) return p, p.unknownErr(datestr)
} }
} else {
// allow multiple trailing whitespace
}
case '+', '-': case '+', '-':
// The year must be followed by a space before an offset!
if p.yearlen > 0 {
p.offseti = i p.offseti = i
p.stateTime = timeWsYearOffset p.stateTime = timeWsOffset
} else {
return p, p.unknownErr(datestr)
}
default: default:
if !unicode.IsDigit(r) { if unicode.IsDigit(r) {
if p.yearlen > 0 {
return p, p.unknownErr(datestr)
}
} else {
return p, p.unknownErr(datestr) return p, p.unknownErr(datestr)
} }
} }
@ -1919,11 +1920,13 @@ iterRunes:
// 06:20:00 UTC-05 // 06:20:00 UTC-05
// 06:20:00 (EST) // 06:20:00 (EST)
// timeWsAlphaWs // timeWsAlphaWs
// timeWsAlphaWsYear
// 17:57:51 MST 2009 // 17:57:51 MST 2009
// timeWsAlphaZoneOffset // timeWsAlphaZoneOffset
// timeWsAlphaZoneOffsetWs // timeWsAlphaZoneOffsetWs
// timeWsAlphaZoneOffsetWsExtra // timeWsAlphaZoneOffsetWsExtra
// 18:04:07 GMT+0100 (GMT Daylight Time) // 18:04:07 GMT+0100 (GMT Daylight Time)
// 18:04:07 GMT+01:00 (GMT Daylight Time)
// timeWsAlphaZoneOffsetWsYear // timeWsAlphaZoneOffsetWsYear
// 15:44:11 UTC+0100 2015 // 15:44:11 UTC+0100 2015
switch r { switch r {
@ -1962,7 +1965,6 @@ iterRunes:
} }
if r == ' ' { if r == ' ' {
p.stateTime = timeWsAlphaWs p.stateTime = timeWsAlphaWs
p.yeari = i + 1
} else { } else {
// 06:20:00 (EST) // 06:20:00 (EST)
// This must be the end of the datetime or the format is unknown // This must be the end of the datetime or the format is unknown
@ -1974,7 +1976,23 @@ iterRunes:
} }
} }
case timeWsAlphaWs: case timeWsAlphaWs:
// timeWsAlphaWsYear
// 17:57:51 MST 2009 // 17:57:51 MST 2009
if unicode.IsDigit(r) {
if p.yeari == 0 {
p.yeari = i
} else {
return p, p.unknownErr(datestr)
}
p.stateTime = timeWsAlphaWsYear
} else if r == '(' {
p.extra = i - 1
p.stateTime = timeWsOffsetWsTZDescInParen
}
case timeWsAlphaWsYear:
if !unicode.IsDigit(r) {
return p, p.unknownErr(datestr)
}
case timeWsAlphaZoneOffset: case timeWsAlphaZoneOffset:
// 06:20:00 UTC-05 // 06:20:00 UTC-05
@ -1982,13 +2000,13 @@ iterRunes:
// timeWsAlphaZoneOffsetWs // timeWsAlphaZoneOffsetWs
// timeWsAlphaZoneOffsetWsExtra // timeWsAlphaZoneOffsetWsExtra
// 18:04:07 GMT+0100 (GMT Daylight Time) // 18:04:07 GMT+0100 (GMT Daylight Time)
// 18:04:07 GMT+01:00 (GMT Daylight Time)
// timeWsAlphaZoneOffsetWsYear // timeWsAlphaZoneOffsetWsYear
// 15:44:11 UTC+0100 2015 // 15:44:11 UTC+0100 2015
switch r { switch r {
case ' ': case ' ':
p.set(p.offseti, "-0700") if err := p.setTZOffset(i, datestr); err != nil {
if p.yeari == 0 { return p, err
p.yeari = i + 1
} }
p.stateTime = timeWsAlphaZoneOffsetWs p.stateTime = timeWsAlphaZoneOffsetWs
default: default:
@ -2000,14 +2018,36 @@ iterRunes:
// timeWsAlphaZoneOffsetWs // timeWsAlphaZoneOffsetWs
// timeWsAlphaZoneOffsetWsExtra // timeWsAlphaZoneOffsetWsExtra
// 18:04:07 GMT+0100 (GMT Daylight Time) // 18:04:07 GMT+0100 (GMT Daylight Time)
// 18:04:07 GMT+01:00 (GMT Daylight Time)
// timeWsAlphaZoneOffsetWsYear // timeWsAlphaZoneOffsetWsYear
// 15:44:11 UTC+0100 2015 // 15:44:11 UTC+0100 2015
if unicode.IsDigit(r) { if unicode.IsDigit(r) {
if p.yeari == 0 {
p.yeari = i
p.stateTime = timeWsAlphaZoneOffsetWsYear p.stateTime = timeWsAlphaZoneOffsetWsYear
} else { } else {
p.extra = i - 1 return p, p.unknownErr(datestr)
p.stateTime = timeWsAlphaZoneOffsetWsExtra
} }
} else if r == '(' {
p.extra = i - 1
p.stateTime = timeWsOffsetWsTZDescInParen
} else {
return p, p.unknownErr(datestr)
}
case timeWsOffsetWsTZDescInParen:
// timeWsAlphaZoneOffsetWs
// timeWsAlphaZoneOffsetWsExtra
// 18:04:07 GMT+0100 (GMT Daylight Time)
// 18:04:07 GMT+01:00 (GMT Daylight Time)
if r == '(' {
return p, p.unknownErr(datestr)
} else if r == ')' {
// must be the end
if i != len(p.datestr)-1 {
return p, p.unknownErr(datestr)
}
}
// any other char is OK
case timeWsAlphaZoneOffsetWsYear: case timeWsAlphaZoneOffsetWsYear:
// 15:44:11 UTC+0100 2015 // 15:44:11 UTC+0100 2015
if unicode.IsDigit(r) { if unicode.IsDigit(r) {
@ -2069,18 +2109,24 @@ iterRunes:
// timeWsOffsetWsOffset // timeWsOffsetWsOffset
// 17:57:51 -0700 -07 // 17:57:51 -0700 -07
// timeWsOffsetWs // timeWsOffsetWs
// 17:57:51 -0700 2009
// 00:12:00 +0000 UTC
// timeWsOffsetColon
// 15:04:05 -07:00 // 15:04:05 -07:00
// timeWsOffsetColonAlpha // timeWsOffsetWsYear
// 17:57:51 -0700 2009
// timeWsOffsetWsAlphaZone
// 00:12:00 +0000 UTC
// 00:12:00 +00:00 UTC // 00:12:00 +00:00 UTC
// timeWsOffsetWsAlphaZoneWs --> timeWsOffsetWsYear (overloaded)
// 00:12:00 +00:00 UTC 2009
// timeWsOffsetWsTZDescInParen
// 00:12:00 +00:00 UTC (Universal Coordinated Time)
switch r { switch r {
case ':': case ':':
p.stateTime = timeWsOffsetColon // Parse the case where an offset has a colon the same as timeWsOffset!
// continue
case ' ': case ' ':
p.set(p.offseti, "-0700") if err := p.setTZOffset(i, datestr); err != nil {
p.yeari = i + 1 return p, err
}
p.stateTime = timeWsOffsetWs p.stateTime = timeWsOffsetWs
default: default:
if !unicode.IsDigit(r) { if !unicode.IsDigit(r) {
@ -2088,73 +2134,139 @@ iterRunes:
} }
} }
case timeWsOffsetWs: case timeWsOffsetWs:
// timeWsOffsetWs
// timeWsOffsetWsYear
// 17:57:51 -0700 2009 // 17:57:51 -0700 2009
// 17:57:51 -07:00 2009
// timeWsOffsetWsAlphaZone
// 00:12:00 +0000 UTC // 00:12:00 +0000 UTC
// 00:12:00 +00:00 UTC
// 22:18:00.001 +0000 UTC m=+0.000000001 // 22:18:00.001 +0000 UTC m=+0.000000001
// 22:18:00.001 +00:00 UTC m=+0.000000001
// w Extra // w Extra
// 17:57:51 -0700 -07 // 17:57:51 -0700 -07
// 17:57:51 -07:00 -07
// 22:18:00.001 +0000 m=+0.000000001
// 00:00:00 +0300 (European Daylight Time)
// 00:00:00 +03:00 (European Daylight Time)
switch r { switch r {
case '=': case '+', '-':
// eff you golang
if p.datestr[i-1] == 'm' {
p.extra = i - 2
p.trimExtra(false)
} else {
return p, p.unknownErr(datestr)
}
case '+', '-', '(':
// This really doesn't seem valid, but for some reason when round-tripping a go date // This really doesn't seem valid, but for some reason when round-tripping a go date
// their is an extra +03 printed out. seems like go bug to me, but, parsing anyway. // their is an extra +03 printed out. seems like go bug to me, but, parsing anyway.
// 00:00:00 +0300 +03 // 00:00:00 +0300 +03
// 00:00:00 +0300 +0300 // 00:00:00 +0300 +0300
// 00:00:00 +03:00 +03
// 00:00:00 +03:00 +0300
p.extra = i - 1 p.extra = i - 1
p.stateTime = timeWsOffset
p.trimExtra(false) p.trimExtra(false)
p.stateTime = timeWsOffset
case '(':
// 00:00:00 +0300 (European Daylight Time)
// 00:00:00 +03:00 (European Daylight Time)
p.extra = i - 1
p.stateTime = timeWsOffsetWsTZDescInParen
case ' ': case ' ':
// continue // continue
default: default:
switch { switch {
case unicode.IsDigit(r): case unicode.IsDigit(r):
p.yearlen = i - p.yeari + 1 if p.yeari == 0 {
if p.yearlen == 4 { p.yeari = i
if !p.setYear() { } else {
return p, p.unknownErr(datestr)
}
} else if p.yearlen > 4 {
return p, p.unknownErr(datestr) return p, p.unknownErr(datestr)
} }
p.stateTime = timeWsOffsetWsYear
case unicode.IsLetter(r): case unicode.IsLetter(r):
if r == 'm' && p.nextIs(i, '=') {
// 22:18:00.001 +0000 UTC m=+0.000000001
// 22:18:00.001 +00:00 UTC m=+0.000000001
// very strange syntax!
p.extra = i - 1
p.trimExtra(false)
} else {
// 15:04:05 -0700 MST // 15:04:05 -0700 MST
// 15:04:05 -07:00 MST
// 15:04:05 -07:00 MST (Mountain Standard Time)
// 15:04:05 -07:00 MST 2006
if p.tzi == 0 { if p.tzi == 0 {
p.tzi = i p.tzi = i
} else {
return p, p.unknownErr(datestr)
}
p.stateTime = timeWsOffsetWsAlphaZone
} }
default: default:
return p, p.unknownErr(datestr) return p, p.unknownErr(datestr)
} }
} }
case timeOffsetColon, timeWsOffsetColon: case timeWsOffsetWsAlphaZone:
// timeOffsetColon switch {
// 15:04:05-07:00 case r == ' ':
// timeOffsetColonAlpha if p.tzi > 0 {
// 2015-02-18 00:12:00+00:00 UTC p.tzlen = i - p.tzi
// timeWsOffsetColon switch p.tzlen {
// 15:04:05 -07:00 case 3:
// timeWsOffsetColonAlpha // 13:31:51.999 +01:00 CET
// 2015-02-18 00:12:00 +00:00 UTC p.set(p.tzi, "MST")
if unicode.IsLetter(r) { case 4:
// TODO: do we need to handle the m=+0.000000001 case? // 13:31:51.999 +01:00 CEST
// 2015-02-18 00:12:00 +00:00 UTC p.set(p.tzi, "MST ")
if p.stateTime == timeWsOffsetColon { default:
p.stateTime = timeWsOffsetColonAlpha if p.simpleErrorMessages {
return p, ErrUnknownTimeZone
} else { } else {
p.stateTime = timeOffsetColonAlpha return p, fmt.Errorf("%w %q near %q (must be 3 or 4 characters)", ErrUnknownTimeZone, datestr, p.datestr[p.tzi:p.tzi+p.tzlen])
} }
p.tzi = i }
break iterTimeRunes } else {
} else if r != ' ' && !unicode.IsDigit(r) {
return p, p.unknownErr(datestr) return p, p.unknownErr(datestr)
} }
p.stateTime = timeWsOffsetWsAlphaZoneWs
case unicode.IsLetter(r):
// continue
}
case timeWsOffsetWsAlphaZoneWs:
switch r {
case '=':
// 22:18:00.001 +0000 UTC m=+0.000000001
// very strange syntax!
if p.datestr[i-1] == 'm' {
p.extra = i - 2
p.trimExtra(false)
} else {
return p, p.unknownErr(datestr)
}
case '(':
// 00:00:00 -0600 MDT (Mountain Daylight Time)
// 00:00:00 -06:00 MDT (Mountain Daylight Time)
p.extra = i - 1
p.stateTime = timeWsOffsetWsTZDescInParen
case ' ':
// continue (extra whitespace)
case 'm':
if !p.nextIs(i, '=') {
return p, p.unknownErr(datestr)
}
default:
if unicode.IsDigit(r) {
if p.yeari == 0 {
p.yeari = i
} else {
return p, p.unknownErr(datestr)
}
p.stateTime = timeWsOffsetWsYear
} else {
return p, p.unknownErr(datestr)
}
}
case timeWsOffsetWsYear:
if !unicode.IsDigit(r) {
return p, p.unknownErr(datestr)
}
case timePeriod: case timePeriod:
// 15:04:05.999999999 // 15:04:05.999999999
// 15:04:05.999999999 // 15:04:05.999999999
@ -2167,7 +2279,7 @@ iterRunes:
// 00:07:31.945167 // 00:07:31.945167
// 18:31:59.257000000 // 18:31:59.257000000
// 00:00:00.000 // 00:00:00.000
// (note: if we have an offset (+/-) or whitespace (Ws) after this state, re-enter the timeWs or timeOffset // (note: if we have an offset (+/-) or whitespace (Ws) after this state, re-enter the timeWs or timeWsOffset
// state above so that we do not have to duplicate all of the logic again for this parsing just because we // state above so that we do not have to duplicate all of the logic again for this parsing just because we
// have parsed a fractional second...) // have parsed a fractional second...)
switch r { switch r {
@ -2180,7 +2292,7 @@ iterRunes:
case '+', '-': case '+', '-':
p.mslen = i - p.msi p.mslen = i - p.msi
p.offseti = i p.offseti = i
p.stateTime = timeOffset p.stateTime = timeWsOffset
case 'Z': case 'Z':
p.stateTime = timeZ p.stateTime = timeZ
p.mslen = i - p.msi p.mslen = i - p.msi
@ -2229,7 +2341,7 @@ iterRunes:
p.stateTime = timeWs p.stateTime = timeWs
case '+', '-': case '+', '-':
p.offseti = i p.offseti = i
p.stateTime = timeOffset p.stateTime = timeWsOffset
default: default:
return p, p.unexpectedTail(i) return p, p.unexpectedTail(i)
} }
@ -2240,43 +2352,6 @@ iterRunes:
} }
switch p.stateTime { switch p.stateTime {
case timeOffsetColonAlpha, timeWsOffsetColonAlpha:
// process offset
offsetLen := i - p.offseti
switch offsetLen {
case 6, 7:
// may or may not have a space on the end
if offsetLen == 7 {
if p.datestr[p.offseti+6] != ' ' {
if p.simpleErrorMessages {
return p, ErrUnknownTZOffset
} else {
return p, fmt.Errorf("%w %q near %q (expected offset like -07:00)", ErrUnknownTZOffset, datestr, p.datestr[p.offseti:p.offseti+offsetLen])
}
}
}
p.set(p.offseti, "-07:00")
default:
if p.simpleErrorMessages {
return p, ErrUnknownTZOffset
} else {
return p, fmt.Errorf("%w %q near %q (expected offset like -07:00)", ErrUnknownTZOffset, datestr, p.datestr[p.offseti:p.offseti+offsetLen])
}
}
// process timezone
switch len(p.datestr) - p.tzi {
case 3:
// 13:31:51.999 +01:00 CET
p.set(p.tzi, "MST")
case 4:
p.set(p.tzi, "MST ")
default:
if p.simpleErrorMessages {
return p, ErrUnknownTimeZone
} else {
return p, fmt.Errorf("%w %q near %q (must be 3 or 4 characters)", ErrUnknownTimeZone, datestr, p.datestr[p.tzi:])
}
}
case timeWsAlpha: case timeWsAlpha:
switch len(p.datestr) - p.tzi { switch len(p.datestr) - p.tzi {
case 3: case 3:
@ -2295,33 +2370,21 @@ iterRunes:
case timeWsAlphaRParen: case timeWsAlphaRParen:
// nothing extra to do // nothing extra to do
case timeWsAlphaWs: case timeWsYear, timeWsAlphaWsYear:
p.yearlen = i - p.yeari p.yearlen = i - p.yeari
if !p.setYear() { if !p.setYear() {
return p, p.unknownErr(datestr) return p, p.unknownErr(datestr)
} }
case timeWsYear: case timeWsOffsetWsTZDescInParen:
p.yearlen = i - p.yeari // The last character must be a closing ')'
if !p.setYear() { if len(p.datestr) <= 0 || p.datestr[i-1] != ')' {
return p, p.unknownErr(datestr) return p, p.unknownErr(datestr)
} }
case timeWsAlphaZoneOffsetWsExtra:
p.trimExtra(false) p.trimExtra(false)
case timeWsAlphaZoneOffset: case timeWsAlphaZoneOffset:
// 06:20:00 UTC-05 // 06:20:00 UTC-05
switch i - p.offseti { if err := p.setTZOffset(i, datestr); err != nil {
case 2, 3, 4: return p, err
p.set(p.offseti, "-07")
case 5:
p.set(p.offseti, "-0700")
case 6:
p.set(p.offseti, "-07:00")
default:
if p.simpleErrorMessages {
return p, ErrUnknownTZOffset
} else {
return p, fmt.Errorf("%w %q near %q (must be 2 or 4 digits optional colon)", ErrUnknownTZOffset, datestr, p.datestr[p.offseti:i])
}
} }
case timePeriod: case timePeriod:
@ -2333,24 +2396,26 @@ iterRunes:
return p, fmt.Errorf("%w in %q near %q", ErrFracSecTooLong, datestr, p.datestr[p.msi:p.mslen]) return p, fmt.Errorf("%w in %q near %q", ErrFracSecTooLong, datestr, p.datestr[p.msi:p.mslen])
} }
} }
case timeOffset, timeWsOffset, timeWsYearOffset: case timeWsOffset:
switch len(p.datestr) - p.offseti { // 17:57:51 -07:00 (or 19:55:00.799 +01:00)
case 3: // 15:04:05+07:00 (or 19:55:00.799+01:00)
// 19:55:00+01 (or 19:55:00 +01) // 17:57:51 2006 -07:00 (or 19:55:00.799 +01:00)
p.set(p.offseti, "-07") if err := p.setTZOffset(len(p.datestr), datestr); err != nil {
case 5: return p, err
// 19:55:00+0100 (or 19:55:00 +0100)
p.set(p.offseti, "-0700")
default:
if p.simpleErrorMessages {
return p, ErrUnknownTZOffset
} else {
return p, fmt.Errorf("%w %q near %q (must be 2 or 4 digits optional colon)", ErrUnknownTZOffset, datestr, p.datestr[p.offseti:])
}
} }
case timeWsOffsetWs: case timeWsOffsetWsYear:
// 17:57:51 -0700 2009 // 17:57:51 -0700 2009
p.yearlen = len(p.datestr) - p.yeari
if p.yearlen == 4 {
if !p.setYear() {
return p, p.unknownErr(datestr)
}
} else if p.yearlen > 4 {
return p, p.unknownErr(datestr)
}
case timeWsOffsetWsAlphaZone:
// 00:12:00 +0000 UTC // 00:12:00 +0000 UTC
if p.tzi > 0 { if p.tzi > 0 {
switch len(p.datestr) - p.tzi { switch len(p.datestr) - p.tzi {
@ -2367,19 +2432,8 @@ iterRunes:
return p, fmt.Errorf("%w %q near %q (must be 3 or 4 characters)", ErrUnknownTimeZone, datestr, p.datestr[p.tzi:]) return p, fmt.Errorf("%w %q near %q (must be 3 or 4 characters)", ErrUnknownTimeZone, datestr, p.datestr[p.tzi:])
} }
} }
}
case timeOffsetColon, timeWsOffsetColon:
// 17:57:51 -07:00 (or 19:55:00.799 +01:00)
// 15:04:05+07:00 (or 19:55:00.799+01:00)
switch len(p.datestr) - p.offseti {
case 6:
p.set(p.offseti, "-07:00")
default:
if p.simpleErrorMessages {
return p, ErrUnknownTZOffset
} else { } else {
return p, fmt.Errorf("%w %q near %q (expected offset like -07:00)", ErrUnknownTZOffset, datestr, p.datestr[p.offseti:]) return p, p.unknownErr(datestr)
}
} }
} }
if !p.coalesceTime(i) { if !p.coalesceTime(i) {
@ -2458,13 +2512,8 @@ iterRunes:
case dateYearDashDashOffset: case dateYearDashDashOffset:
/// 2020-07-20+00:00 /// 2020-07-20+00:00
switch len(p.datestr) - p.offseti { if err := p.setTZOffset(len(p.datestr), datestr); err != nil {
case 5: return p, err
p.set(p.offseti, "-0700")
case 6:
p.set(p.offseti, "-07:00")
default:
return p, p.unknownErr(datestr)
} }
return p, nil return p, nil
@ -2555,13 +2604,8 @@ iterRunes:
case dateDigitDotDotOffset: case dateDigitDotDotOffset:
// 2020.07.20+00:00 // 2020.07.20+00:00
switch len(p.datestr) - p.offseti { if err := p.setTZOffset(len(p.datestr), datestr); err != nil {
case 5: return p, err
p.set(p.offseti, "-0700")
case 6:
p.set(p.offseti, "-07:00")
default:
return p, p.unknownErr(datestr)
} }
return p, nil return p, nil
@ -2840,6 +2884,7 @@ func (p *parser) set(start int, val string) {
p.formatSetLen = endingPos p.formatSetLen = endingPos
} }
} }
func (p *parser) setMonth() bool { func (p *parser) setMonth() bool {
if p.molen == 2 { if p.molen == 2 {
p.set(p.moi, "01") p.set(p.moi, "01")
@ -2863,6 +2908,7 @@ func (p *parser) setDay() bool {
return false return false
} }
} }
func (p *parser) setYear() bool { func (p *parser) setYear() bool {
if p.yearlen == 2 { if p.yearlen == 2 {
p.set(p.yeari, "06") p.set(p.yeari, "06")
@ -2875,6 +2921,25 @@ func (p *parser) setYear() bool {
} }
} }
func (p *parser) setTZOffset(i int, datestr string) error {
offsetlen := i - p.offseti
switch offsetlen {
case 3:
p.set(p.offseti, "-07")
case 5:
p.set(p.offseti, "-0700")
case 6:
p.set(p.offseti, "-07:00")
default:
if p.simpleErrorMessages {
return ErrUnknownTZOffset
} else {
return fmt.Errorf("%w %q near %q (must be 2 or 4 digits optional colon)", ErrUnknownTZOffset, datestr, p.datestr[p.offseti:i])
}
}
return nil
}
// Find the proper end of the current component (scanning chars starting from start and going // Find the proper end of the current component (scanning chars starting from start and going
// up until the end, and either returning at end or returning the first character that is // up until the end, and either returning at end or returning the first character that is
// not allowed, as determined by allowNumeric, allowAlpha, and allowOther) // not allowed, as determined by allowNumeric, allowAlpha, and allowOther)

View File

@ -105,11 +105,19 @@ var testInputs = []dateTest{
{in: "Mon Aug 1 5:44:11 CEST+0200 2015", out: "2015-08-01 03:44:11 +0000 UTC", zname: "CEST"}, {in: "Mon Aug 1 5:44:11 CEST+0200 2015", out: "2015-08-01 03:44:11 +0000 UTC", zname: "CEST"},
// ?? // ??
{in: "Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)", out: "2015-07-03 17:04:07 +0000 UTC"}, {in: "Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)", out: "2015-07-03 17:04:07 +0000 UTC"},
{in: "Fri Jul 03 2015 18:04:07 GMT+01:00 (GMT Daylight Time)", out: "2015-07-03 17:04:07 +0000 UTC"},
{in: "Fri Jul 3 2015 06:04:07 GMT+0100 (GMT Daylight Time)", out: "2015-07-03 05:04:07 +0000 UTC"}, {in: "Fri Jul 3 2015 06:04:07 GMT+0100 (GMT Daylight Time)", out: "2015-07-03 05:04:07 +0000 UTC"},
{in: "Fri Jul 03 2015 18:04:07 UTC+0100 (GMT Daylight Time)", out: "2015-07-03 17:04:07 +0000 UTC"}, {in: "Fri Jul 03 2015 18:04:07 UTC+0100 (GMT Daylight Time)", out: "2015-07-03 17:04:07 +0000 UTC"},
{in: "Fri Jul 3 2015 06:04:07 UTC+0100 (GMT Daylight Time)", out: "2015-07-03 05:04:07 +0000 UTC"}, {in: "Fri Jul 3 2015 06:04:07 UTC+0100 (GMT Daylight Time)", out: "2015-07-03 05:04:07 +0000 UTC"},
{in: "Fri Jul 3 2015 06:04:07 PST-0700 (Pacific Daylight Time)", out: "2015-07-03 13:04:07 +0000 UTC", zname: "PST"}, {in: "Fri Jul 3 2015 06:04:07 PST-0700 (Pacific Daylight Time)", out: "2015-07-03 13:04:07 +0000 UTC", zname: "PST"},
{in: "Fri Jul 3 2015 06:04:07 PST-07:00 (Pacific Daylight Time)", out: "2015-07-03 13:04:07 +0000 UTC", zname: "PST"},
{in: "Fri Jul 3 2015 06:04:07 CEST-0700 (Central European Summer Time)", out: "2015-07-03 13:04:07 +0000 UTC", zname: "CEST"}, {in: "Fri Jul 3 2015 06:04:07 CEST-0700 (Central European Summer Time)", out: "2015-07-03 13:04:07 +0000 UTC", zname: "CEST"},
{in: "Fri Jul 03 2015 18:04:07 GMT (GMT Daylight Time)", out: "2015-07-03 18:04:07 +0000 UTC", zname: "GMT"},
{in: "Fri Jul 3 2015 06:04:07 +0100 (GMT Daylight Time)", out: "2015-07-03 05:04:07 +0000 UTC"},
{in: "Fri Jul 03 2015 18:04:07 UTC (GMT Daylight Time)", out: "2015-07-03 18:04:07 +0000 UTC"},
{in: "Fri Jul 3 2015 06:04:07 +01:00 (GMT Daylight Time)", out: "2015-07-03 05:04:07 +0000 UTC"},
{in: "Fri Jul 3 2015 06:04:07 PST (Pacific Daylight Time)", out: "2015-07-03 06:04:07 +0000 UTC", zname: "PST"},
{in: "Fri Jul 3 2015 06:04:07 -07:00 (Pacific Daylight Time)", out: "2015-07-03 13:04:07 +0000 UTC"},
{in: "Fri Jul 3 2015", out: "2015-07-03 00:00:00 +0000 UTC"}, {in: "Fri Jul 3 2015", out: "2015-07-03 00:00:00 +0000 UTC"},
{in: "Fri Jul 3 2015 11:15:26pm", out: "2015-07-03 23:15:26 +0000 UTC"}, {in: "Fri Jul 3 2015 11:15:26pm", out: "2015-07-03 23:15:26 +0000 UTC"},
// Month dd, yyyy at time // Month dd, yyyy at time
@ -342,6 +350,14 @@ var testInputs = []dateTest{
{in: "04/02/2014 04:08:09.123 AM", out: "2014-04-02 04:08:09.123 +0000 UTC"}, {in: "04/02/2014 04:08:09.123 AM", out: "2014-04-02 04:08:09.123 +0000 UTC"},
{in: "04/02/2014 04:08:09.123PM", out: "2014-04-02 16:08:09.123 +0000 UTC"}, {in: "04/02/2014 04:08:09.123PM", out: "2014-04-02 16:08:09.123 +0000 UTC"},
{in: "04/02/2014 04:08:09.123 PM", out: "2014-04-02 16:08:09.123 +0000 UTC"}, {in: "04/02/2014 04:08:09.123 PM", out: "2014-04-02 16:08:09.123 +0000 UTC"},
{in: "04/02/2014 04:08:09pm-0700", out: "2014-04-02 23:08:09 +0000 UTC"},
{in: "04/02/2014 04:08:09PM-0700 PST", out: "2014-04-02 23:08:09 +0000 UTC", zname: "PST"},
{in: "04/02/2014 04:08:09pm-0700 PST (Pacific Standard Time)", out: "2014-04-02 23:08:09 +0000 UTC", zname: "PST"},
{in: "04/02/2014 04:08:09pm-0700 (Pacific Standard Time)", out: "2014-04-02 23:08:09 +0000 UTC"},
{in: "04/02/2014 04:08:09am+02:00", out: "2014-04-02 02:08:09 +0000 UTC"},
{in: "04/02/2014 04:08:09AM+02:00 CET", out: "2014-04-02 02:08:09 +0000 UTC", zname: "CET"},
{in: "04/02/2014 04:08:09am+02:00 CET (Central European Time)", out: "2014-04-02 02:08:09 +0000 UTC", zname: "CET"},
{in: "04/02/2014 04:08:09am+02:00 (Central European Time)", out: "2014-04-02 02:08:09 +0000 UTC"},
// yyyy/mm/dd // yyyy/mm/dd
{in: "2014/04/02", out: "2014-04-02 00:00:00 +0000 UTC"}, {in: "2014/04/02", out: "2014-04-02 00:00:00 +0000 UTC"},
{in: "2014/03/31", out: "2014-03-31 00:00:00 +0000 UTC"}, {in: "2014/03/31", out: "2014-03-31 00:00:00 +0000 UTC"},
@ -513,6 +529,15 @@ var testInputs = []dateTest{
// Git log default date format - https://github.com/araddon/dateparse/pull/92 // Git log default date format - https://github.com/araddon/dateparse/pull/92
{in: "Thu Apr 7 15:13:13 2005 -0700", out: "2005-04-07 22:13:13 +0000 UTC"}, {in: "Thu Apr 7 15:13:13 2005 -0700", out: "2005-04-07 22:13:13 +0000 UTC"},
{in: "Tue Dec 12 23:07:11 2023 -0700", out: "2023-12-13 06:07:11 +0000 UTC"}, {in: "Tue Dec 12 23:07:11 2023 -0700", out: "2023-12-13 06:07:11 +0000 UTC"},
// Variants with different offset formats, or that place the year after the offset and/or timezone
{in: "Thu Apr 7 15:13:13 2005 -07:00", out: "2005-04-07 22:13:13 +0000 UTC"},
{in: "Thu Apr 7 15:13:13 2005 -07:00 PST", out: "2005-04-07 22:13:13 +0000 UTC", zname: "PST"},
{in: "Thu Apr 7 15:13:13 2005 -07:00 PST (Pacific Standard Time)", out: "2005-04-07 22:13:13 +0000 UTC", zname: "PST"},
{in: "Thu Apr 7 15:13:13 -0700 2005", out: "2005-04-07 22:13:13 +0000 UTC"},
{in: "Thu Apr 7 15:13:13 -07:00 2005", out: "2005-04-07 22:13:13 +0000 UTC"},
{in: "Thu Apr 7 15:13:13 -0700 PST 2005", out: "2005-04-07 22:13:13 +0000 UTC", zname: "PST"},
{in: "Thu Apr 7 15:13:13 -07:00 PST 2005", out: "2005-04-07 22:13:13 +0000 UTC", zname: "PST"},
{in: "Thu Apr 7 15:13:13 PST 2005", out: "2005-04-07 15:13:13 +0000 UTC", zname: "PST"},
// RabbitMQ log format - https://github.com/araddon/dateparse/pull/122 // RabbitMQ log format - https://github.com/araddon/dateparse/pull/122
{in: "8-Mar-2018::14:09:27", out: "2018-03-08 14:09:27 +0000 UTC"}, {in: "8-Mar-2018::14:09:27", out: "2018-03-08 14:09:27 +0000 UTC"},
{in: "08-03-2018::02:09:29 PM", out: "2018-03-08 14:09:29 +0000 UTC"}, {in: "08-03-2018::02:09:29 PM", out: "2018-03-08 14:09:29 +0000 UTC"},
@ -531,6 +556,8 @@ var testInputs = []dateTest{
{in: "2014-04-26 17:24:37.1 +0000", out: "2014-04-26 17:24:37.1 +0000 UTC"}, {in: "2014-04-26 17:24:37.1 +0000", out: "2014-04-26 17:24:37.1 +0000 UTC"},
{in: "2014-05-11 08:20:13 +0000", out: "2014-05-11 08:20:13 +0000 UTC"}, {in: "2014-05-11 08:20:13 +0000", out: "2014-05-11 08:20:13 +0000 UTC"},
{in: "2014-05-11 08:20:13 +0530", out: "2014-05-11 02:50:13 +0000 UTC"}, {in: "2014-05-11 08:20:13 +0530", out: "2014-05-11 02:50:13 +0000 UTC"},
{in: "2014-05-11 08:20:13 +0530 m=+0.000000001", out: "2014-05-11 02:50:13 +0000 UTC"},
{in: "2014-05-11 08:20:13.123456 +0530 m=+0.000000001", out: "2014-05-11 02:50:13.123456 +0000 UTC"},
// yyyy-mm-dd hh:mm:ss +0300 +03 ?? issue author said this is from golang? // yyyy-mm-dd hh:mm:ss +0300 +03 ?? issue author said this is from golang?
{in: "2018-06-29 19:09:57.77297118 +0300 +03", out: "2018-06-29 16:09:57.77297118 +0000 UTC"}, {in: "2018-06-29 19:09:57.77297118 +0300 +03", out: "2018-06-29 16:09:57.77297118 +0000 UTC"},
{in: "2018-06-29 19:09:57.77297118 +0300 +0300", out: "2018-06-29 16:09:57.77297118 +0000 UTC"}, {in: "2018-06-29 19:09:57.77297118 +0300 +0300", out: "2018-06-29 16:09:57.77297118 +0000 UTC"},
@ -549,6 +576,8 @@ var testInputs = []dateTest{
{in: "2014-04-26 17:24:37.123456 +00:00", out: "2014-04-26 17:24:37.123456 +0000 UTC"}, {in: "2014-04-26 17:24:37.123456 +00:00", out: "2014-04-26 17:24:37.123456 +0000 UTC"},
{in: "2014-04-26 17:24:37.12 +00:00", out: "2014-04-26 17:24:37.12 +0000 UTC"}, {in: "2014-04-26 17:24:37.12 +00:00", out: "2014-04-26 17:24:37.12 +0000 UTC"},
{in: "2014-04-26 17:24:37.1 +00:00", out: "2014-04-26 17:24:37.1 +0000 UTC"}, {in: "2014-04-26 17:24:37.1 +00:00", out: "2014-04-26 17:24:37.1 +0000 UTC"},
{in: "2014-04-26 17:24:37 +00:00 m=+0.000000001", out: "2014-04-26 17:24:37 +0000 UTC"},
{in: "2014-04-26 17:24:37.123456 +00:00 m=+0.000000001", out: "2014-04-26 17:24:37.123456 +0000 UTC"},
// yyyy-mm-dd hh:mm:ss +0000 TZ // yyyy-mm-dd hh:mm:ss +0000 TZ
// Golang Native Format // Golang Native Format
{in: "2012-08-03 18:31:59 +0000 UTC", out: "2012-08-03 18:31:59 +0000 UTC", zname: "UTC"}, {in: "2012-08-03 18:31:59 +0000 UTC", out: "2012-08-03 18:31:59 +0000 UTC", zname: "UTC"},
@ -570,6 +599,9 @@ var testInputs = []dateTest{
{in: "2015-02-08 03:02:00 +0200 CEST m=+0.000000001", out: "2015-02-08 01:02:00 +0000 UTC", loc: "Europe/Berlin", zname: "CEST"}, {in: "2015-02-08 03:02:00 +0200 CEST m=+0.000000001", out: "2015-02-08 01:02:00 +0000 UTC", loc: "Europe/Berlin", zname: "CEST"},
{in: "2015-02-08 03:02:00 +0300 MSK m=+0.000000001", out: "2015-02-08 00:02:00 +0000 UTC", zname: "MSK"}, {in: "2015-02-08 03:02:00 +0300 MSK m=+0.000000001", out: "2015-02-08 00:02:00 +0000 UTC", zname: "MSK"},
{in: "2015-02-08 03:02:00.001 +0300 MSK m=+0.000000001", out: "2015-02-08 00:02:00.001 +0000 UTC", zname: "MSK"}, {in: "2015-02-08 03:02:00.001 +0300 MSK m=+0.000000001", out: "2015-02-08 00:02:00.001 +0000 UTC", zname: "MSK"},
// Variant with colon in offset
{in: "2015-02-08 03:02:00 +02:00 CEST", out: "2015-02-08 01:02:00 +0000 UTC", loc: "Europe/Berlin", zname: "CEST"},
{in: "2015-02-08 03:02:00 +02:00 CEST (Central European Standard Time)", out: "2015-02-08 01:02:00 +0000 UTC", loc: "Europe/Berlin", zname: "CEST"},
// yyyy-mm-dd hh:mm:ss TZ // yyyy-mm-dd hh:mm:ss TZ
{in: "2012-08-03 18:31:59 UTC", out: "2012-08-03 18:31:59 +0000 UTC", zname: "UTC"}, {in: "2012-08-03 18:31:59 UTC", out: "2012-08-03 18:31:59 +0000 UTC", zname: "UTC"},
{in: "2012-08-03 18:31:59 CEST", out: "2012-08-03 18:31:59 +0000 UTC", zname: "CEST"}, {in: "2012-08-03 18:31:59 CEST", out: "2012-08-03 18:31:59 +0000 UTC", zname: "CEST"},
@ -663,6 +695,8 @@ var testInputs = []dateTest{
// yyyy-mm-ddThh:mm:ss:000+0000 - weird format with additional colon in front of milliseconds // yyyy-mm-ddThh:mm:ss:000+0000 - weird format with additional colon in front of milliseconds
{in: "2012-08-17T18:31:59:257", out: "2012-08-17 18:31:59.257 +0000 UTC"}, // https://github.com/araddon/dateparse/issues/137 {in: "2012-08-17T18:31:59:257", out: "2012-08-17 18:31:59.257 +0000 UTC"}, // https://github.com/araddon/dateparse/issues/137
{in: "2012-08-17T18:31:59:257+0100", out: "2012-08-17 17:31:59.257 +0000 UTC"}, // https://github.com/araddon/dateparse/issues/117 {in: "2012-08-17T18:31:59:257+0100", out: "2012-08-17 17:31:59.257 +0000 UTC"}, // https://github.com/araddon/dateparse/issues/117
{in: "2012-08-17T18:31:59:257+0200 CET", out: "2012-08-17 16:31:59.257 +0000 UTC", zname: "CET"},
{in: "2012-08-17T18:31:59:257+0200 CET (Central European Time)", out: "2012-08-17 16:31:59.257 +0000 UTC", zname: "CET"},
// yyyy-mm-ddThh:mm:ssZ // yyyy-mm-ddThh:mm:ssZ
{in: "2009-08-12T22:15Z", out: "2009-08-12 22:15:00 +0000 UTC"}, {in: "2009-08-12T22:15Z", out: "2009-08-12 22:15:00 +0000 UTC"},
{in: "2009-08-12T22:15:09Z", out: "2009-08-12 22:15:09 +0000 UTC"}, {in: "2009-08-12T22:15:09Z", out: "2009-08-12 22:15:09 +0000 UTC"},
@ -788,7 +822,7 @@ func TestParse(t *testing.T) {
} }
fullInput := prefix + th.in fullInput := prefix + th.in
t.Run(fmt.Sprintf("simpleerr-%v-%s", simpleErrorMessage, fullInput), func(t *testing.T) { t.Run(fmt.Sprintf("simpleerr-%v-addweekday-%v-%s", simpleErrorMessage, addWeekday, fullInput), func(t *testing.T) {
var ts time.Time var ts time.Time
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -935,6 +969,56 @@ var testParseErrors = []dateTest{
{in: "8-Mar-2018:14:09:27", err: true}, {in: "8-Mar-2018:14:09:27", err: true},
{in: "8-Mar-2018: 14:09:27", err: true}, {in: "8-Mar-2018: 14:09:27", err: true},
{in: "8-Mar-2018:::14:09:27", err: true}, {in: "8-Mar-2018:::14:09:27", err: true},
// Invalid repeated year
{in: "Thu Apr 7 15:13:13 2005 2004", err: true},
{in: "Thu Apr 7 15:13:13 2005 2004 ", err: true},
{in: "Thu Apr 7 15:13:13 2005-0700", err: true},
{in: "Thu Apr 7 15:13:13 2005-07:00", err: true},
{in: "Thu Apr 7 15:13:13 2005 -0700 2005", err: true},
{in: "Thu Apr 7 15:13:13 2005 -0700 PST 2005", err: true},
{in: "Thu Apr 7 15:13:13 2005 -07:00 2005", err: true},
{in: "Thu Apr 7 15:13:13 2005 -07:00 PST 2005", err: true},
// Invalid offsets
{in: "Fri Jul 03 2015 18:04:07 GMT+0", err: true},
{in: "Fri Jul 03 2015 18:04:07 GMT+000", err: true},
{in: "Fri Jul 03 2015 18:04:07 GMT+0:100", err: true},
{in: "Fri Jul 03 2015 18:04:07 GMT+010:0", err: true},
{in: "Fri Jul 03 2015 18:04:07 GMT+01000", err: true},
{in: "Fri Jul 03 2015 18:04:07 GMT+01:000", err: true},
{in: "Fri Jul 03 2015 18:04:07 +0", err: true},
{in: "Fri Jul 03 2015 18:04:07 +000", err: true},
{in: "Fri Jul 03 2015 18:04:07 +0:100", err: true},
{in: "Fri Jul 03 2015 18:04:07 +010:0", err: true},
{in: "Fri Jul 03 2015 18:04:07 +01000", err: true},
{in: "Fri Jul 03 2015 18:04:07 +01:000", err: true},
// Invalid extra words on the end (or invalid time zone description)
{in: "2018-09-30 21:09:13 (Universal Coordinated Time)", err: true},
{in: "2018-09-30 21:09:13pm (Universal Coordinated Time)", err: true},
{in: "Fri Jul 3 2015 06:04:07 GMT+0100 blah", err: true},
{in: "Fri Jul 3 2015 06:04:07 GMT+0100 hello world", err: true},
{in: "Fri Jul 03 2015 18:04:07 UTC+0100 GMT Daylight Time", err: true},
{in: "Fri Jul 3 2015 06:04:07 UTC+0100 (GMT", err: true},
{in: "Fri Jul 3 2015 06:04:07 PST-0700 (Pacific (Daylight) Time)", err: true},
{in: "Fri Jul 3 2015 06:04:07 CEST-0700 (Central European Summer Time) extra", err: true},
{in: "Fri Jul 3 2015 06:04:07 +0100 blah", err: true},
{in: "Fri Jul 3 2015 06:04:07 +0100 hello world", err: true},
{in: "Fri Jul 03 2015 18:04:07 +0100 GMT Daylight Time", err: true},
{in: "Fri Jul 3 2015 06:04:07 +0100 (GMT", err: true},
{in: "Fri Jul 3 2015 06:04:07 -0700 (Pacific (Daylight) Time)", err: true},
{in: "Fri Jul 3 2015 06:04:07 -0700 (Central European Summer Time) extra", err: true},
{in: "Fri Jul 3 2015 06:04:07 +01:00 blah", err: true},
{in: "Fri Jul 3 2015 06:04:07 +01:00 hello world", err: true},
{in: "Fri Jul 03 2015 18:04:07 +01:00 GMT Daylight Time", err: true},
{in: "Fri Jul 3 2015 06:04:07 +01:00 (GMT", err: true},
{in: "Fri Jul 3 2015 06:04:07 -07:00 (Pacific (Daylight) Time)", err: true},
{in: "Fri Jul 3 2015 06:04:07 -07:00 (Central European Summer Time) extra", err: true},
{in: "Fri Jul 03 2015 18:04:07 GMT GMT", err: true},
{in: "Fri Jul 3 2015 06:04:07 PMT blah", err: true},
{in: "Fri Jul 3 2015 06:04:07 PMT hello world", err: true},
{in: "Fri Jul 03 2015 18:04:07 AMT GMT Daylight Time", err: true},
{in: "Fri Jul 3 2015 06:04:07 UTC (GMT", err: true},
{in: "Fri Jul 3 2015 06:04:07 PST (Pacific (Daylight) Time)", err: true},
{in: "Fri Jul 3 2015 06:04:07 CEST (Central European Summer Time) extra", err: true},
} }
func TestParseErrors(t *testing.T) { func TestParseErrors(t *testing.T) {