From 13b1994d70c0682cf2468e2c607d8adddb9ac1c1 Mon Sep 17 00:00:00 2001 From: Aaron Raddon Date: Sun, 25 Feb 2018 16:35:33 -0800 Subject: [PATCH 01/10] split into date, time partial parsing --- bench_test.go | 4 + parseany.go | 1852 ++++++++++++++++++++++++---------------------- parseany_test.go | 10 + 3 files changed, 995 insertions(+), 871 deletions(-) diff --git a/bench_test.go b/bench_test.go index 5b8e1b8..f30eb98 100644 --- a/bench_test.go +++ b/bench_test.go @@ -13,6 +13,10 @@ go test -bench Parse BenchmarkShotgunParse 50000 37588 ns/op 13258 B/op 167 allocs/op BenchmarkDateparseParseAny 500000 5752 ns/op 0 B/op 0 allocs/op +// Aarons Laptop Lenovo 900 Feb 2018 +BenchmarkShotgunParse-4 50000 30045 ns/op 13136 B/op 169 allocs/op +BenchmarkParseAny-4 200000 8627 ns/op 144 B/op 3 allocs/op + */ func BenchmarkShotgunParse(b *testing.B) { b.ReportAllocs() diff --git a/parseany.go b/parseany.go index f33719b..65198f8 100644 --- a/parseany.go +++ b/parseany.go @@ -9,64 +9,81 @@ import ( "time" "unicode" "unicode/utf8" + + u "github.com/araddon/gou" ) -type dateState int +func init() { + u.SetupLogging("debug") + u.SetColorOutput() +} + +type dateState uint8 +type timeState uint8 const ( - stateStart dateState = iota - stateDigit - stateDigitDash - stateDigitDashAlpha - stateDigitDashWs - stateDigitDashWsWs - stateDigitDashWsWsAMPMMaybe - stateDigitDashWsWsOffset - stateDigitDashWsWsOffsetAlpha - stateDigitDashWsWsOffsetColonAlpha - stateDigitDashWsWsOffsetColon - stateDigitDashWsOffset - stateDigitDashWsWsAlpha - stateDigitDashWsPeriod - stateDigitDashWsPeriodAlpha - stateDigitDashWsPeriodOffset - stateDigitDashWsPeriodOffsetAlpha - stateDigitDashT - stateDigitDashTZ - stateDigitDashTZDigit - stateDigitDashTOffset - stateDigitDashTOffsetColon - stateDigitDot - stateDigitDotDot - stateDigitSlash - stateDigitSlashWS - stateDigitSlashWSColon - stateDigitSlashWSColonAMPM - stateDigitSlashWSColonColon - stateDigitSlashWSColonColonAMPM - stateDigitChineseYear - stateDigitChineseYearWs - stateDigitWs - stateDigitWsMoShort - stateDigitWsMoShortColon - stateDigitWsMoShortColonColon - stateDigitWsMoShortComma - stateAlpha - stateAlphaWS - stateAlphaWSDigitComma - stateAlphaWSAlpha - stateAlphaWSAlphaColon - stateAlphaWSAlphaColonOffset - stateAlphaWSAlphaColonAlpha - stateAlphaWSAlphaColonAlphaOffset - stateAlphaWSAlphaColonAlphaOffsetAlpha - stateWeekdayComma - stateWeekdayCommaDash - stateWeekdayCommaOffset - stateWeekdayAbbrevComma - stateWeekdayAbbrevCommaDash - stateWeekdayAbbrevCommaOffset - stateWeekdayAbbrevCommaOffsetZone + dateStart dateState = iota + dateDigit + dateDigitDash + dateDigitDashWs + dateDigitDashT + dateDigitDashTZ + dateDigitDashAlpha + dateDigitDot + dateDigitDotDot + dateDigitSlash + dateDigitSlashWS + dateDigitSlashWSColon + dateDigitSlashWSColonAMPM + dateDigitSlashWSColonColon + dateDigitSlashWSColonColonAMPM + dateDigitChineseYear + dateDigitChineseYearWs + dateDigitWs + dateDigitWsMoShort + dateDigitWsMoShortColon + dateDigitWsMoShortColonColon + dateDigitWsMoShortComma + dateAlpha + dateAlphaWS + dateAlphaWSDigit + dateAlphaWSDigitComma + dateAlphaWSDigitCommaWs + dateAlphaWSDigitCommaWsYear + dateAlphaWSAlpha + dateAlphaWSAlphaColon + dateAlphaWSAlphaColonOffset + dateAlphaWSAlphaColonAlpha + dateAlphaWSAlphaColonAlphaOffset + dateAlphaWSAlphaColonAlphaOffsetAlpha + dateWeekdayComma + dateWeekdayCommaDash + dateWeekdayCommaOffset + dateWeekdayAbbrevComma + dateWeekdayAbbrevCommaDash + dateWeekdayAbbrevCommaOffset + dateWeekdayAbbrevCommaOffsetZone + + // Now time ones + timeIgnore timeState = iota + timeStart + timeWs + timeWsAMPMMaybe + timeWsAMPM + timeWsOffset + timeWsAlpha + timeWsOffsetAlpha + timeWsOffsetColonAlpha + timeWsOffsetColon + timeOffset + timeOffsetColon + timeAlpha + timePeriod + timePeriodAlpha + timePeriodOffset + timePeriodOffsetAlpha + timeZ + timeZDigit ) var ( @@ -125,7 +142,11 @@ func parse(layout, datestr string, loc *time.Location) (time.Time, error) { } func parseTime(datestr string, loc *time.Location) (time.Time, error) { - state := stateStart + + stateDate := dateStart + stateTime := timeIgnore + dateFormat := []byte(datestr) + i := 0 part1Len := 0 part2Len := 0 @@ -136,52 +157,52 @@ func parseTime(datestr string, loc *time.Location) (time.Time, error) { // Hopefully we only need to read about 5 or 6 bytes before // we figure it out and then attempt a parse iterRunes: - for i := 0; i < len(datestr); i++ { + for ; i < len(datestr); i++ { //r := rune(datestr[i]) r, bytesConsumed := utf8.DecodeRuneInString(datestr[i:]) if bytesConsumed > 1 { i += (bytesConsumed - 1) } - switch state { - case stateStart: + switch stateDate { + case dateStart: if unicode.IsDigit(r) { - state = stateDigit + stateDate = dateDigit } else if unicode.IsLetter(r) { - state = stateAlpha + stateDate = dateAlpha } - case stateDigit: // starts digits + case dateDigit: // starts digits if unicode.IsDigit(r) { continue } else if unicode.IsLetter(r) { if r == '年' { // Chinese Year - state = stateDigitChineseYear + stateDate = dateDigitChineseYear } } switch r { case '-', '\u2212': - state = stateDigitDash + stateDate = dateDigitDash part1Len = i case '/': - state = stateDigitSlash + stateDate = dateDigitSlash part1Len = i case '.': - state = stateDigitDot + stateDate = dateDigitDot part1Len = i case ' ': - state = stateDigitWs + stateDate = dateDigitWs part1Len = i } - case stateDigitDash: // starts digit then dash 02- + case dateDigitDash: // starts digit then dash 02- // 2006-01-02 - // stateDigitDashT + // dateDigitDashT // 2006-01-02T15:04:05Z07:00 // 2017-06-25T17:46:57.45706582-07:00 // 2006-01-02T15:04:05.999999999Z07:00 // 2006-01-02T15:04:05+0000 - // stateDigitDashWs + // dateDigitDashWs // 2012-08-03 18:31:59.257000000 // 2014-04-26 17:24:37.3186369 // 2017-01-27 00:07:31.945167 @@ -190,197 +211,27 @@ iterRunes: // 2017-07-19 03:21:51+00:00 // 2013-04-01 22:43:22 // 2014-04-26 05:24:37 PM - // stateDigitDashAlpha + // dateDigitDashAlpha // 2013-Feb-03 switch { case r == '-': part2Len = i - part1Len - 1 case r == ' ': part3Len = i - part1Len - part2Len - 1 - 1 - state = stateDigitDashWs + stateDate = dateDigitDashWs + stateTime = timeStart + break iterRunes case r == 'T': - state = stateDigitDashT + stateDate = dateDigitDashT + stateTime = timeStart + break iterRunes default: if unicode.IsLetter(r) { - state = stateDigitDashAlpha + stateDate = dateDigitDashAlpha break iterRunes } } - case stateDigitDashWs: - // 2013-04-01 22:43:22 - // 2014-05-11 08:20:13,787 - // stateDigitDashWsWs - // 2014-04-26 05:24:37 PM - // 2014-12-16 06:20:00 UTC - // 2015-02-18 00:12:00 +0000 UTC - // 2006-01-02 15:04:05 -0700 - // 2006-01-02 15:04:05 -07:00 - // stateDigitDashWsOffset - // 2017-07-19 03:21:51+00:00 - // stateDigitDashWsPeriod - // 2014-04-26 17:24:37.3186369 - // 2017-01-27 00:07:31.945167 - // 2012-08-03 18:31:59.257000000 - // 2016-03-14 00:00:00.000 - // stateDigitDashWsPeriodOffset - // 2017-01-27 00:07:31.945167 +0000 - // 2016-03-14 00:00:00.000 +0000 - // stateDigitDashWsPeriodOffsetAlpha - // 2017-01-27 00:07:31.945167 +0000 UTC - // 2016-03-14 00:00:00.000 +0000 UTC - // stateDigitDashWsPeriodAlpha - // 2014-12-16 06:20:00.000 UTC - switch r { - case ',': - if len(datestr) == len("2014-05-11 08:20:13,787") { - // go doesn't seem to parse this one natively? or did i miss it? - t, err := parse("2006-01-02 03:04:05", datestr[:i], loc) - if err == nil { - ms, err := strconv.Atoi(datestr[i+1:]) - if err == nil { - return time.Unix(0, t.UnixNano()+int64(ms)*1e6), nil - } - } - return t, err - } - case '-', '+': - state = stateDigitDashWsOffset - case '.': - state = stateDigitDashWsPeriod - case ' ': - state = stateDigitDashWsWs - } - - case stateDigitDashWsWs: - // stateDigitDashWsWsAlpha - // 2014-12-16 06:20:00 UTC - // stateDigitDashWsWsAMPMMaybe - // 2014-04-26 05:24:37 PM - // stateDigitDashWsWsOffset - // 2006-01-02 15:04:05 -0700 - // stateDigitDashWsWsOffsetColon - // 2006-01-02 15:04:05 -07:00 - // stateDigitDashWsWsOffsetColonAlpha - // 2015-02-18 00:12:00 +00:00 UTC - // stateDigitDashWsWsOffsetAlpha - // 2015-02-18 00:12:00 +0000 UTC - switch r { - case 'A', 'P': - state = stateDigitDashWsWsAMPMMaybe - case '+', '-': - state = stateDigitDashWsWsOffset - default: - if unicode.IsLetter(r) { - // 2014-12-16 06:20:00 UTC - state = stateDigitDashWsWsAlpha - break iterRunes - } - } - - case stateDigitDashWsWsAMPMMaybe: - if r == 'M' { - return parse("2006-01-02 03:04:05 PM", datestr, loc) - } - state = stateDigitDashWsWsAlpha - - case stateDigitDashWsWsOffset: - // stateDigitDashWsWsOffset - // 2006-01-02 15:04:05 -0700 - // stateDigitDashWsWsOffsetColon - // 2006-01-02 15:04:05 -07:00 - // stateDigitDashWsWsOffsetColonAlpha - // 2015-02-18 00:12:00 +00:00 UTC - // stateDigitDashWsWsOffsetAlpha - // 2015-02-18 00:12:00 +0000 UTC - if r == ':' { - state = stateDigitDashWsWsOffsetColon - } else if unicode.IsLetter(r) { - // 2015-02-18 00:12:00 +0000 UTC - state = stateDigitDashWsWsOffsetAlpha - break iterRunes - } - - case stateDigitDashWsWsOffsetColon: - // stateDigitDashWsWsOffsetColon - // 2006-01-02 15:04:05 -07:00 - // stateDigitDashWsWsOffsetColonAlpha - // 2015-02-18 00:12:00 +00:00 UTC - if unicode.IsLetter(r) { - // 2015-02-18 00:12:00 +00:00 UTC - state = stateDigitDashWsWsOffsetColonAlpha - break iterRunes - } - - case stateDigitDashWsPeriod: - // 2014-04-26 17:24:37.3186369 - // 2017-01-27 00:07:31.945167 - // 2012-08-03 18:31:59.257000000 - // 2016-03-14 00:00:00.000 - // stateDigitDashWsPeriodOffset - // 2017-01-27 00:07:31.945167 +0000 - // 2016-03-14 00:00:00.000 +0000 - // stateDigitDashWsPeriodOffsetAlpha - // 2017-01-27 00:07:31.945167 +0000 UTC - // 2016-03-14 00:00:00.000 +0000 UTC - // stateDigitDashWsPeriodAlpha - // 2014-12-16 06:20:00.000 UTC - if unicode.IsLetter(r) { - // 2014-12-16 06:20:00.000 UTC - state = stateDigitDashWsPeriodAlpha - break iterRunes - } else if r == '+' || r == '-' { - state = stateDigitDashWsPeriodOffset - } - case stateDigitDashWsPeriodOffset: - // 2017-01-27 00:07:31.945167 +0000 - // 2016-03-14 00:00:00.000 +0000 - // stateDigitDashWsPeriodOffsetAlpha - // 2017-01-27 00:07:31.945167 +0000 UTC - // 2016-03-14 00:00:00.000 +0000 UTC - if unicode.IsLetter(r) { - // 2014-12-16 06:20:00.000 UTC - // 2017-01-27 00:07:31.945167 +0000 UTC - // 2016-03-14 00:00:00.000 +0000 UTC - state = stateDigitDashWsPeriodOffsetAlpha - break iterRunes - } - case stateDigitDashT: // starts digit then dash 02- then T - // stateDigitDashT - // 2006-01-02T15:04:05 - // stateDigitDashTZ - // 2006-01-02T15:04:05.999999999Z - // 2006-01-02T15:04:05.99999999Z - // 2006-01-02T15:04:05.9999999Z - // 2006-01-02T15:04:05.999999Z - // 2006-01-02T15:04:05.99999Z - // 2006-01-02T15:04:05.9999Z - // 2006-01-02T15:04:05.999Z - // 2006-01-02T15:04:05.99Z - // 2009-08-12T22:15Z - // stateDigitDashTZDigit - // 2006-01-02T15:04:05.999999999Z07:00 - // 2006-01-02T15:04:05Z07:00 - // With another dash aka time-zone at end - // stateDigitDashTOffset - // stateDigitDashTOffsetColon - // 2017-06-25T17:46:57.45706582-07:00 - // 2017-06-25T17:46:57+04:00 - // 2006-01-02T15:04:05+0000 - switch r { - case '-', '+': - state = stateDigitDashTOffset - case 'Z': - state = stateDigitDashTZ - } - case stateDigitDashTZ: - if unicode.IsDigit(r) { - state = stateDigitDashTZDigit - } - case stateDigitDashTOffset: - if r == ':' { - state = stateDigitDashTOffsetColon - } - case stateDigitSlash: // starts digit then slash 02/ + case dateDigitSlash: // starts digit then slash 02/ // 2014/07/10 06:55:38.156283 // 03/19/2012 10:11:59 // 04/2/2014 03:00:37 @@ -395,9 +246,9 @@ iterRunes: } switch r { case ' ': - state = stateDigitSlashWS + stateDate = dateDigitSlashWS } - case stateDigitSlashWS: // starts digit then slash 02/ more digits/slashes then whitespace + case dateDigitSlashWS: // starts digit then slash 02/ more digits/slashes then whitespace // 2014/07/10 06:55:38.156283 // 03/19/2012 10:11:59 // 04/2/2014 03:00:37 @@ -406,9 +257,9 @@ iterRunes: // 4/8/14 22:05 switch r { case ':': - state = stateDigitSlashWSColon + stateDate = dateDigitSlashWSColon } - case stateDigitSlashWSColon: // starts digit then slash 02/ more digits/slashes then whitespace + case dateDigitSlashWSColon: // starts digit then slash 02/ more digits/slashes then whitespace // 2014/07/10 06:55:38.156283 // 03/19/2012 10:11:59 // 04/2/2014 03:00:37 @@ -418,11 +269,11 @@ iterRunes: // 3/1/2012 10:11:59 AM switch r { case ':': - state = stateDigitSlashWSColonColon + stateDate = dateDigitSlashWSColonColon case 'A', 'P': - state = stateDigitSlashWSColonAMPM + stateDate = dateDigitSlashWSColonAMPM } - case stateDigitSlashWSColonColon: // starts digit then slash 02/ more digits/slashes then whitespace + case dateDigitSlashWSColonColon: // starts digit then slash 02/ more digits/slashes then whitespace // 2014/07/10 06:55:38.156283 // 03/19/2012 10:11:59 // 04/2/2014 03:00:37 @@ -432,229 +283,472 @@ iterRunes: // 3/1/2012 10:11:59 AM switch r { case 'A', 'P': - state = stateDigitSlashWSColonColonAMPM + stateDate = dateDigitSlashWSColonColonAMPM } - case stateDigitWs: + case dateDigitWs: // 18 January 2018 // 8 January 2018 - //stateDigitWsMoShort + //dateDigitWsMoShort // 02 Jan 2018 23:59 // 02 Jan 2018 23:59:34 - // stateDigitWsMoShortComma + // dateDigitWsMoShortComma // 12 Feb 2006, 19:17 // 12 Feb 2006, 19:17:22 if r == ' ' { if i <= part1Len+len(" Feb") { - state = stateDigitWsMoShort + stateDate = dateDigitWsMoShort } else { break iterRunes } } - case stateDigitWsMoShort: + case dateDigitWsMoShort: // 18 January 2018 // 8 January 2018 - // stateDigitWsMoShort - // stateDigitWsMoShortColon + // dateDigitWsMoShort + // dateDigitWsMoShortColon // 02 Jan 2018 23:59 - // stateDigitWsMoShortComma + // dateDigitWsMoShortComma // 12 Feb 2006, 19:17 // 12 Feb 2006, 19:17:22 switch r { case ':': - state = stateDigitWsMoShortColon + stateDate = dateDigitWsMoShortColon case ',': - state = stateDigitWsMoShortComma + stateDate = dateDigitWsMoShortComma } - case stateDigitWsMoShortColon: + case dateDigitWsMoShortColon: // 02 Jan 2018 23:59 - // stateDigitWsMoShortColonColon + // dateDigitWsMoShortColonColon // 02 Jan 2018 23:59:45 switch r { case ':': - state = stateDigitWsMoShortColonColon + stateDate = dateDigitWsMoShortColonColon break iterRunes } - case stateDigitChineseYear: - // stateDigitChineseYear + case dateDigitChineseYear: + // dateDigitChineseYear // 2014年04月08日 // weekday %Y年%m月%e日 %A %I:%M %p // 2013年07月18日 星期四 10:27 上午 if r == ' ' { - state = stateDigitChineseYearWs + stateDate = dateDigitChineseYearWs break } - case stateDigitDot: + case dateDigitDot: // 3.31.2014 if r == '.' { - state = stateDigitDotDot + stateDate = dateDigitDotDot part2Len = i } - case stateDigitDotDot: + case dateDigitDotDot: // iterate all the way through - case stateAlpha: // starts alpha - // stateAlphaWS + case dateAlpha: + // dateAlphaWS // Mon Jan _2 15:04:05 2006 // Mon Jan _2 15:04:05 MST 2006 // Mon Jan 02 15:04:05 -0700 2006 // Mon Aug 10 15:44:11 UTC+0100 2015 // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - // stateAlphaWSDigitComma + // dateAlphaWSDigit // May 8, 2009 5:57:51 PM // - // stateWeekdayComma + // dateWeekdayComma // Monday, 02 Jan 2006 15:04:05 MST - // stateWeekdayCommaDash + // dateWeekdayCommaDash // Monday, 02-Jan-06 15:04:05 MST - // stateWeekdayCommaOffset + // dateWeekdayCommaOffset // Monday, 02 Jan 2006 15:04:05 -0700 // Monday, 02 Jan 2006 15:04:05 +0100 - // stateWeekdayAbbrevComma + // dateWeekdayAbbrevComma // Mon, 02 Jan 2006 15:04:05 MST - // stateWeekdayAbbrevCommaDash + // dateWeekdayAbbrevCommaDash // Mon, 02-Jan-06 15:04:05 MST - // stateWeekdayAbbrevCommaOffset + // dateWeekdayAbbrevCommaOffset // Mon, 02 Jan 2006 15:04:05 -0700 // Thu, 13 Jul 2017 08:58:40 +0100 - // stateWeekdayAbbrevCommaOffsetZone + // dateWeekdayAbbrevCommaOffsetZone // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) switch { - case unicode.IsLetter(r): - continue case r == ' ': - state = stateAlphaWS + stateDate = dateAlphaWS + dateFormat[0], dateFormat[1], dateFormat[2] = 'M', 'o', 'n' + part1Len = i case r == ',': part1Len = i if i == 3 { - state = stateWeekdayAbbrevComma + stateDate = dateWeekdayAbbrevComma + dateFormat[0], dateFormat[1], dateFormat[2] = 'M', 'o', 'n' } else { - state = stateWeekdayComma + stateDate = dateWeekdayComma + dateFormat[0], dateFormat[1], dateFormat[2], dateFormat[3], dateFormat[4], dateFormat[5] = 'M', 'o', 'n', 'd', 'a', 'y' } i++ } - case stateWeekdayComma: // Starts alpha then comma + case dateWeekdayComma: // Starts alpha then comma // Monday, 02 Jan 2006 15:04:05 MST - // stateWeekdayCommaDash + // dateWeekdayCommaDash // Monday, 02-Jan-06 15:04:05 MST - // stateWeekdayCommaOffset + // dateWeekdayCommaOffset // Monday, 02 Jan 2006 15:04:05 -0700 // Monday, 02 Jan 2006 15:04:05 +0100 switch { case r == '-': if i < 15 { - state = stateWeekdayCommaDash + stateDate = dateWeekdayCommaDash break iterRunes } - state = stateWeekdayCommaOffset + stateDate = dateWeekdayCommaOffset case r == '+': - state = stateWeekdayCommaOffset + stateDate = dateWeekdayCommaOffset } - case stateWeekdayAbbrevComma: // Starts alpha then comma + case dateWeekdayAbbrevComma: // Starts alpha then comma // Mon, 02 Jan 2006 15:04:05 MST - // stateWeekdayAbbrevCommaDash + // dateWeekdayAbbrevCommaDash // Mon, 02-Jan-06 15:04:05 MST - // stateWeekdayAbbrevCommaOffset + // dateWeekdayAbbrevCommaOffset // Mon, 02 Jan 2006 15:04:05 -0700 // Thu, 13 Jul 2017 08:58:40 +0100 // Thu, 4 Jan 2018 17:53:36 +0000 - // stateWeekdayAbbrevCommaOffsetZone + // dateWeekdayAbbrevCommaOffsetZone // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) switch { case r == ' ' && part3Len == 0: part3Len = i - part1Len - 2 case r == '-': if i < 15 { - state = stateWeekdayAbbrevCommaDash + stateDate = dateWeekdayAbbrevCommaDash break iterRunes } - state = stateWeekdayAbbrevCommaOffset + stateDate = dateWeekdayAbbrevCommaOffset case r == '+': - state = stateWeekdayAbbrevCommaOffset + stateDate = dateWeekdayAbbrevCommaOffset } - case stateWeekdayAbbrevCommaOffset: - // stateWeekdayAbbrevCommaOffset + case dateWeekdayAbbrevCommaOffset: + // dateWeekdayAbbrevCommaOffset // Mon, 02 Jan 2006 15:04:05 -0700 // Thu, 13 Jul 2017 08:58:40 +0100 // Thu, 4 Jan 2018 17:53:36 +0000 - // stateWeekdayAbbrevCommaOffsetZone + // dateWeekdayAbbrevCommaOffsetZone // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) if r == '(' { - state = stateWeekdayAbbrevCommaOffsetZone + stateDate = dateWeekdayAbbrevCommaOffsetZone } - case stateAlphaWS: // Starts alpha then whitespace + case dateAlphaWS: // Starts alpha then whitespace // Mon Jan _2 15:04:05 2006 // Mon Jan _2 15:04:05 MST 2006 // Mon Jan 02 15:04:05 -0700 2006 // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) // Mon Aug 10 15:44:11 UTC+0100 2015 switch { + case r == ' ': + part2Len = i - part1Len case unicode.IsLetter(r): - state = stateAlphaWSAlpha + stateDate = dateAlphaWSAlpha case unicode.IsDigit(r): - state = stateAlphaWSDigitComma + stateDate = dateAlphaWSDigit + default: + u.Warnf("can we drop isLetter? case r=%s", string(r)) } - case stateAlphaWSDigitComma: // Starts Alpha, whitespace, digit, comma - // May 8, 2009 5:57:51 PM - for _, layout := range []string{ - "Jan 2, 2006 3:04:05 PM", - "Jan 2, 2006 3:4:05 PM", - "Jan 2, 2006 3:4:5 PM", - "Jan 2, 2006 3:04:5 PM", - "Jan 02, 2006 3:04:05 PM", - "Jan 02, 2006 3:4:05 PM", - "Jan 02, 2006 3:4:5 PM", - "Jan 02, 2006 3:04:5 PM", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } + case dateAlphaWSDigit: // Starts Alpha, whitespace, digit, comma + // dateAlphaWSDigit + // May 8, 2009 5:57:51 PM + switch { + case r == ',': + stateDate = dateAlphaWSDigitComma + case unicode.IsDigit(r): + stateDate = dateAlphaWSDigit + default: + u.Warnf("hm, can we drop a case here? %v", string(r)) + } + case dateAlphaWSDigitComma: + // x + // May 8, 2009 5:57:51 PM + switch { + case r == ' ': + stateDate = dateAlphaWSDigitCommaWs + default: + u.Warnf("hm, can we drop a case here? %v", string(r)) + return time.Time{}, fmt.Errorf("could not find format for %v expected white-space after comma", datestr) + } + case dateAlphaWSDigitCommaWs: + // x + // May 8, 2009 5:57:51 PM + if !unicode.IsDigit(r) { + stateDate = dateAlphaWSDigitCommaWsYear + break iterRunes } - case stateAlphaWSAlpha: // Alpha, whitespace, alpha + case dateAlphaWSAlpha: // Alpha, whitespace, alpha // Mon Jan _2 15:04:05 2006 // Mon Jan 02 15:04:05 -0700 2006 // Mon Jan _2 15:04:05 MST 2006 // Mon Aug 10 15:44:11 UTC+0100 2015 // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) if r == ':' { - state = stateAlphaWSAlphaColon + stateDate = dateAlphaWSAlphaColon } - case stateAlphaWSAlphaColon: // Alpha, whitespace, alpha, : + case dateAlphaWSAlphaColon: // Alpha, whitespace, alpha, : // Mon Jan _2 15:04:05 2006 // Mon Jan 02 15:04:05 -0700 2006 // Mon Jan _2 15:04:05 MST 2006 // Mon Aug 10 15:44:11 UTC+0100 2015 // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) if unicode.IsLetter(r) { - state = stateAlphaWSAlphaColonAlpha + stateDate = dateAlphaWSAlphaColonAlpha } else if r == '-' || r == '+' { - state = stateAlphaWSAlphaColonOffset + stateDate = dateAlphaWSAlphaColonOffset } - case stateAlphaWSAlphaColonAlpha: // Alpha, whitespace, alpha, :, alpha + case dateAlphaWSAlphaColonAlpha: // Alpha, whitespace, alpha, :, alpha // Mon Jan _2 15:04:05 MST 2006 // Mon Aug 10 15:44:11 UTC+0100 2015 // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) if r == '+' { - state = stateAlphaWSAlphaColonAlphaOffset + stateDate = dateAlphaWSAlphaColonAlphaOffset } - case stateAlphaWSAlphaColonAlphaOffset: // Alpha, whitespace, alpha, : , alpha, offset, ? + case dateAlphaWSAlphaColonAlphaOffset: // Alpha, whitespace, alpha, : , alpha, offset, ? // Mon Aug 10 15:44:11 UTC+0100 2015 // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) if unicode.IsLetter(r) { - state = stateAlphaWSAlphaColonAlphaOffsetAlpha + stateDate = dateAlphaWSAlphaColonAlphaOffsetAlpha } default: break iterRunes } } - switch state { - case stateDigit: + timeMarker := i + time1Len := 0 + time2Len := 0 + time3Len := 0 + +iterTimeRunes: + for ; i < len(datestr); i++ { + r, bytesConsumed := utf8.DecodeRuneInString(datestr[i:]) + if bytesConsumed > 1 { + i += (bytesConsumed - 1) + } + switch stateTime { + case timeIgnore: + // not used + case timeStart: + // 22:43:22 + // timeComma + // 08:20:13,787 + // timeWs + // 05:24:37 PM + // 06:20:00 UTC + // 00:12:00 +0000 UTC + // 15:04:05 -0700 + // 15:04:05 -07:00 + // timeOffset + // 03:21:51+00:00 + // timePeriod + // 17:24:37.3186369 + // 00:07:31.945167 + // 18:31:59.257000000 + // 00:00:00.000 + // timePeriodOffset + // 00:07:31.945167 +0000 + // 00:00:00.000 +0000 + // timePeriodOffsetAlpha + // 00:07:31.945167 +0000 UTC + // 00:00:00.000 +0000 UTC + // timePeriodAlpha + // 06:20:00.000 UTC + switch r { + case ',': + if len(datestr) == len("2014-05-11 08:20:13,787") { + // go doesn't seem to parse this one natively? or did i miss it? + t, err := parse("2006-01-02 03:04:05", datestr[:i], loc) + if err == nil { + ms, err := strconv.Atoi(datestr[i+1:]) + if err == nil { + return time.Unix(0, t.UnixNano()+int64(ms)*1e6), nil + } + } + return t, err + } + case '-', '+': + stateTime = timeOffset + case '.': + stateTime = timePeriod + case ' ': + stateTime = timeWs + case ':': + if time2Len > 0 { + time3Len = i - timeMarker + } else if time1Len > 0 { + time2Len = i - timeMarker + } else if time1Len == 0 { + time1Len = i - timeMarker + } + timeMarker = i + u.Infof("time len: i=%d r=%s marker:%d 1:%d 2:%d 3:%d", i, string(r), timeMarker, time1Len, time2Len, time3Len) + } + case timeOffset: + // With another +/- time-zone at end + // 15:04:05.999999999+07:00 + // 15:04:05.999999999-07:00 + // 15:04:05.999999+07:00 + // 15:04:05.999999-07:00 + // 15:04:05.999+07:00 + // 15:04:05.999-07:00 + // 15:04:05+07:00 + // 15:04:05-07:00 + if r == ':' { + stateTime = timeOffsetColon + } + /* + case dateDigitDashT: // starts digit then dash 02- then T + // dateDigitDashT + // 2006-01-02T15:04:05 + // dateDigitDashTZ + // 2006-01-02T15:04:05.999999999Z + // 2006-01-02T15:04:05.99999999Z + // 2006-01-02T15:04:05.9999999Z + // 2006-01-02T15:04:05.999999Z + // 2006-01-02T15:04:05.99999Z + // 2006-01-02T15:04:05.9999Z + // 2006-01-02T15:04:05.999Z + // 2006-01-02T15:04:05.99Z + // 2009-08-12T22:15Z + // dateDigitDashTZDigit + // 2006-01-02T15:04:05.999999999Z07:00 + // 2006-01-02T15:04:05Z07:00 + // With another dash aka time-zone at end + // dateDigitDashTOffset + // dateDigitDashTOffsetColon + // 2017-06-25T17:46:57.45706582-07:00 + // 2017-06-25T17:46:57+04:00 + // 2006-01-02T15:04:05+0000 + switch r { + case '-', '+': + stateTime = dateDigitDashTOffset + case 'Z': + stateTime = dateDigitDashTZ + } + */ + case timeWs: + // timeAlpha + // 06:20:00 UTC + // timeWsAMPMMaybe + // 05:24:37 PM + // timeWsOffset + // 15:04:05 -0700 + // timeWsOffsetColon + // 15:04:05 -07:00 + // timeWsOffsetColonAlpha + // 00:12:00 +00:00 UTC + // timeWsOffsetAlpha + // 00:12:00 +0000 UTC + // timeZ + // 15:04:05.99Z + switch r { + case 'A', 'P': + // Could be AM/PM or could be PST or similar + stateTime = timeWsAMPMMaybe + case '+', '-': + stateTime = timeWsOffset + default: + if unicode.IsLetter(r) { + // 06:20:00 UTC + stateTime = timeWsAlpha + break iterTimeRunes + } + } + + case timeWsAMPMMaybe: + // timeWsAMPMMaybe + // timeWsAMPM + // 05:24:37 PM + // timeWsAlpha + // 00:12:00 PST + if r == 'M' { + //return parse("2006-01-02 03:04:05 PM", datestr, loc) + stateTime = timeWsAMPM + } else { + stateTime = timeWsAlpha + } + + case timeWsOffset: + // timeWsOffset + // 15:04:05 -0700 + // timeWsOffsetColon + // 15:04:05 -07:00 + // timeWsOffsetColonAlpha + // 00:12:00 +00:00 UTC + // timeWsOffsetAlpha + // 00:12:00 +0000 UTC + if r == ':' { + stateTime = timeWsOffsetColon + } else if unicode.IsLetter(r) { + // 2015-02-18 00:12:00 +0000 UTC + stateTime = timeWsOffsetAlpha + break iterTimeRunes + } + + case timeWsOffsetColon: + // timeWsOffsetColon + // 15:04:05 -07:00 + // timeWsOffsetColonAlpha + // 2015-02-18 00:12:00 +00:00 UTC + if unicode.IsLetter(r) { + // 2015-02-18 00:12:00 +00:00 UTC + stateTime = timeWsOffsetColonAlpha + break iterTimeRunes + } + + case timePeriod: + // 2014-04-26 17:24:37.3186369 + // 2017-01-27 00:07:31.945167 + // 2012-08-03 18:31:59.257000000 + // 2016-03-14 00:00:00.000 + // timePeriodOffset + // 2017-01-27 00:07:31.945167 +0000 + // 2016-03-14 00:00:00.000 +0000 + // timePeriodOffsetAlpha + // 2017-01-27 00:07:31.945167 +0000 UTC + // 2016-03-14 00:00:00.000 +0000 UTC + // timePeriodAlpha + // 2014-12-16 06:20:00.000 UTC + if unicode.IsLetter(r) { + // 2014-12-16 06:20:00.000 UTC + stateTime = timePeriodAlpha + break iterTimeRunes + } else if r == '+' || r == '-' { + stateTime = timePeriodOffset + } + case timePeriodOffset: + // 00:07:31.945167 +0000 + // 00:00:00.000 +0000 + // timePeriodOffsetAlpha + // 00:07:31.945167 +0000 UTC + // 00:00:00.000 +0000 UTC + if unicode.IsLetter(r) { + // 06:20:00.000 UTC + // 00:07:31.945167 +0000 UTC + // 00:00:00.000 +0000 UTC + stateTime = timePeriodOffsetAlpha + break iterTimeRunes + } + case timeZ: + if unicode.IsDigit(r) { + stateTime = timeZDigit + } + + } + } + + u.Infof("time %60s marker:%2d 1:%d 2:%d 3:%d timeFormat=%q", datestr, timeMarker, time1Len, time2Len, time3Len, string(dateFormat)) + + switch stateDate { + case dateDigit: // unixy timestamps ish // 1499979655583057426 nanoseconds // 1499979795437000 micro-seconds @@ -698,7 +792,7 @@ iterRunes: return t.In(loc), nil } - case stateDigitDash: // starts digit then dash 02- + case dateDigitDash: // starts digit then dash 02- // 2006-01-02 // 2006-1-02 // 2006-1-2 @@ -716,7 +810,7 @@ iterRunes: } } - case stateDigitDashAlpha: + case dateDigitDashAlpha: // 2013-Feb-03 // 2013-Feb-3 for _, layout := range []string{ @@ -728,524 +822,524 @@ iterRunes: } } - case stateDigitDashTOffset: - // 2006-01-02T15:04:05+0000 - for _, layout := range []string{ - "2006-01-02T15:04:05-0700", - "2006-01-02T15:04:5-0700", - "2006-01-02T15:4:05-0700", - "2006-01-02T15:4:5-0700", - "2006-1-02T15:04:05-0700", - "2006-1-02T15:4:05-0700", - "2006-1-02T15:04:5-0700", - "2006-1-02T15:4:5-0700", - "2006-01-2T15:04:05-0700", - "2006-01-2T15:04:5-0700", - "2006-01-2T15:4:05-0700", - "2006-01-2T15:4:5-0700", - "2006-1-2T15:04:05-0700", - "2006-1-2T15:04:5-0700", - "2006-1-2T15:4:05-0700", - "2006-1-2T15:4:5-0700", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + /* + case dateDigitDashTOffset: + // 2006-01-02T15:04:05+0000 + for _, layout := range []string{ + "2006-01-02T15:04:05-0700", + "2006-01-02T15:04:5-0700", + "2006-01-02T15:4:05-0700", + "2006-01-02T15:4:5-0700", + "2006-1-02T15:04:05-0700", + "2006-1-02T15:4:05-0700", + "2006-1-02T15:04:5-0700", + "2006-1-02T15:4:5-0700", + "2006-01-2T15:04:05-0700", + "2006-01-2T15:04:5-0700", + "2006-01-2T15:4:05-0700", + "2006-01-2T15:4:5-0700", + "2006-1-2T15:04:05-0700", + "2006-1-2T15:04:5-0700", + "2006-1-2T15:4:05-0700", + "2006-1-2T15:4:5-0700", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } - case stateDigitDashTOffsetColon: - // With another +/- time-zone at end - // 2006-01-02T15:04:05.999999999+07:00 - // 2006-01-02T15:04:05.999999999-07:00 - // 2006-01-02T15:04:05.999999+07:00 - // 2006-01-02T15:04:05.999999-07:00 - // 2006-01-02T15:04:05.999+07:00 - // 2006-01-02T15:04:05.999-07:00 - // 2006-01-02T15:04:05+07:00 - // 2006-01-02T15:04:05-07:00 - for _, layout := range []string{ - "2006-01-02T15:04:05-07:00", - "2006-01-02T15:04:5-07:00", - "2006-01-02T15:4:05-07:00", - "2006-01-02T15:4:5-07:00", - "2006-1-02T15:04:05-07:00", - "2006-1-02T15:4:05-07:00", - "2006-1-02T15:04:5-07:00", - "2006-1-02T15:4:5-07:00", - "2006-01-2T15:04:05-07:00", - "2006-01-2T15:04:5-07:00", - "2006-01-2T15:4:05-07:00", - "2006-01-2T15:4:5-07:00", - "2006-1-2T15:04:05-07:00", - "2006-1-2T15:04:5-07:00", - "2006-1-2T15:4:05-07:00", - "2006-1-2T15:4:5-07:00", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + case dateDigitDashTOffsetColon: + // With another +/- time-zone at end + // 2006-01-02T15:04:05.999999999+07:00 + // 2006-01-02T15:04:05.999999999-07:00 + // 2006-01-02T15:04:05.999999+07:00 + // 2006-01-02T15:04:05.999999-07:00 + // 2006-01-02T15:04:05.999+07:00 + // 2006-01-02T15:04:05.999-07:00 + // 2006-01-02T15:04:05+07:00 + // 2006-01-02T15:04:05-07:00 + for _, layout := range []string{ + "2006-01-02T15:04:05-07:00", + "2006-01-02T15:04:5-07:00", + "2006-01-02T15:4:05-07:00", + "2006-01-02T15:4:5-07:00", + "2006-1-02T15:04:05-07:00", + "2006-1-02T15:4:05-07:00", + "2006-1-02T15:04:5-07:00", + "2006-1-02T15:4:5-07:00", + "2006-01-2T15:04:05-07:00", + "2006-01-2T15:04:5-07:00", + "2006-01-2T15:4:05-07:00", + "2006-01-2T15:4:5-07:00", + "2006-1-2T15:04:05-07:00", + "2006-1-2T15:04:5-07:00", + "2006-1-2T15:4:05-07:00", + "2006-1-2T15:4:5-07:00", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } - case stateDigitDashT: // starts digit then dash 02- then T - // 2006-01-02T15:04:05.999999 - // 2006-01-02T15:04:05.999999 - for _, layout := range []string{ - "2006-01-02T15:04:05", - "2006-01-02T15:04:5", - "2006-01-02T15:4:05", - "2006-01-02T15:4:5", - "2006-1-02T15:04:05", - "2006-1-02T15:4:05", - "2006-1-02T15:04:5", - "2006-1-02T15:4:5", - "2006-01-2T15:04:05", - "2006-01-2T15:04:5", - "2006-01-2T15:4:05", - "2006-01-2T15:4:5", - "2006-1-2T15:04:05", - "2006-1-2T15:04:5", - "2006-1-2T15:4:05", - "2006-1-2T15:4:5", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + case dateDigitDashT: // starts digit then dash 02- then T + // 2006-01-02T15:04:05.999999 + // 2006-01-02T15:04:05.999999 + for _, layout := range []string{ + "2006-01-02T15:04:05", + "2006-01-02T15:04:5", + "2006-01-02T15:4:05", + "2006-01-02T15:4:5", + "2006-1-02T15:04:05", + "2006-1-02T15:4:05", + "2006-1-02T15:04:5", + "2006-1-02T15:4:5", + "2006-01-2T15:04:05", + "2006-01-2T15:04:5", + "2006-01-2T15:4:05", + "2006-01-2T15:4:5", + "2006-1-2T15:04:05", + "2006-1-2T15:04:5", + "2006-1-2T15:4:05", + "2006-1-2T15:4:5", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } - case stateDigitDashTZDigit: - // With a time-zone at end after Z - // 2006-01-02T15:04:05.999999999Z07:00 - // 2006-01-02T15:04:05Z07:00 - // RFC3339 = "2006-01-02T15:04:05Z07:00" - // RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" - return time.Time{}, fmt.Errorf("RFC339 Dates may not contain both Z & Offset for %q see https://github.com/golang/go/issues/5294", datestr) + case dateDigitDashTZDigit: + // With a time-zone at end after Z + // 2006-01-02T15:04:05.999999999Z07:00 + // 2006-01-02T15:04:05Z07:00 + // RFC3339 = "2006-01-02T15:04:05Z07:00" + // RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" + return time.Time{}, fmt.Errorf("RFC339 Dates may not contain both Z & Offset for %q see https://github.com/golang/go/issues/5294", datestr) - case stateDigitDashTZ: // starts digit then dash 02- then T Then Z - // 2006-01-02T15:04:05.999999999Z - // 2006-01-02T15:04:05.99999999Z - // 2006-01-02T15:04:05.9999999Z - // 2006-01-02T15:04:05.999999Z - // 2006-01-02T15:04:05.99999Z - // 2006-01-02T15:04:05.9999Z - // 2006-01-02T15:04:05.999Z - // 2006-01-02T15:04:05.99Z - // 2009-08-12T22:15Z -- No seconds/milliseconds - for _, layout := range []string{ - "2006-01-02T15:04:05Z", - "2006-01-02T15:04:5Z", - "2006-01-02T15:4:05Z", - "2006-01-02T15:4:5Z", - "2006-01-02T15:4Z", - "2006-01-02T15:04Z", - "2006-1-02T15:04:05Z", - "2006-1-02T15:4:05Z", - "2006-1-02T15:04:5Z", - "2006-1-02T15:4:5Z", - "2006-1-02T15:04Z", - "2006-1-02T15:4Z", - "2006-01-2T15:04:05Z", - "2006-01-2T15:04:5Z", - "2006-01-2T15:4:05Z", - "2006-01-2T15:4:5Z", - "2006-01-2T15:4Z", - "2006-01-2T15:04Z", - "2006-1-2T15:04:05Z", - "2006-1-2T15:04:5Z", - "2006-1-2T15:4:05Z", - "2006-1-2T15:4:5Z", - "2006-1-2T15:04Z", - "2006-1-2T15:4Z", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + case dateDigitDashTZ: // starts digit then dash 02- then T Then Z + // 2006-01-02T15:04:05.999999999Z + // 2006-01-02T15:04:05.99999999Z + // 2006-01-02T15:04:05.9999999Z + // 2006-01-02T15:04:05.999999Z + // 2006-01-02T15:04:05.99999Z + // 2006-01-02T15:04:05.9999Z + // 2006-01-02T15:04:05.999Z + // 2006-01-02T15:04:05.99Z + // 2009-08-12T22:15Z -- No seconds/milliseconds + for _, layout := range []string{ + "2006-01-02T15:04:05Z", + "2006-01-02T15:04:5Z", + "2006-01-02T15:4:05Z", + "2006-01-02T15:4:5Z", + "2006-01-02T15:4Z", + "2006-01-02T15:04Z", + "2006-1-02T15:04:05Z", + "2006-1-02T15:4:05Z", + "2006-1-02T15:04:5Z", + "2006-1-02T15:4:5Z", + "2006-1-02T15:04Z", + "2006-1-02T15:4Z", + "2006-01-2T15:04:05Z", + "2006-01-2T15:04:5Z", + "2006-01-2T15:4:05Z", + "2006-01-2T15:4:5Z", + "2006-01-2T15:4Z", + "2006-01-2T15:04Z", + "2006-1-2T15:04:05Z", + "2006-1-2T15:04:5Z", + "2006-1-2T15:4:05Z", + "2006-1-2T15:4:5Z", + "2006-1-2T15:04Z", + "2006-1-2T15:4Z", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } - case stateDigitDashWs: // starts digit then dash 02- then whitespace 1 << 2 << 5 + 3 - // 2013-04-01 22:43:22 - // 2013-04-01 22:43 - for _, layout := range []string{ - "2006-01-02 15:04:05", - "2006-01-02 15:04:5", - "2006-01-02 15:4:05", - "2006-01-02 15:4:5", - "2006-01-02 15:4", - "2006-01-02 15:04", - "2006-1-02 15:04:05", - "2006-1-02 15:4:05", - "2006-1-02 15:04:5", - "2006-1-02 15:4:5", - "2006-1-02 15:04", - "2006-1-02 15:4", - "2006-01-2 15:04:05", - "2006-01-2 15:04:5", - "2006-01-2 15:4:05", - "2006-01-2 15:4:5", - "2006-01-2 15:4", - "2006-01-2 15:04", - "2006-1-2 15:04:05", - "2006-1-2 15:04:5", - "2006-1-2 15:4:05", - "2006-1-2 15:4:5", - "2006-1-2 15:04", - "2006-1-2 15:4", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + case dateDigitDashWs: // starts digit then dash 02- then whitespace 1 << 2 << 5 + 3 + // 2013-04-01 22:43:22 + // 2013-04-01 22:43 + for _, layout := range []string{ + "2006-01-02 15:04:05", + "2006-01-02 15:04:5", + "2006-01-02 15:4:05", + "2006-01-02 15:4:5", + "2006-01-02 15:4", + "2006-01-02 15:04", + "2006-1-02 15:04:05", + "2006-1-02 15:4:05", + "2006-1-02 15:04:5", + "2006-1-02 15:4:5", + "2006-1-02 15:04", + "2006-1-02 15:4", + "2006-01-2 15:04:05", + "2006-01-2 15:04:5", + "2006-01-2 15:4:05", + "2006-01-2 15:4:5", + "2006-01-2 15:4", + "2006-01-2 15:04", + "2006-1-2 15:04:05", + "2006-1-2 15:04:5", + "2006-1-2 15:4:05", + "2006-1-2 15:4:5", + "2006-1-2 15:04", + "2006-1-2 15:4", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } - case stateDigitDashWsWsOffset: - // 2006-01-02 15:04:05 -0700 - for _, layout := range []string{ - "2006-01-02 15:04:05 -0700", - "2006-01-02 15:04:5 -0700", - "2006-01-02 15:4:05 -0700", - "2006-01-02 15:4:5 -0700", - "2006-1-02 15:04:05 -0700", - "2006-1-02 15:4:05 -0700", - "2006-1-02 15:04:5 -0700", - "2006-1-02 15:4:5 -0700", - "2006-01-2 15:04:05 -0700", - "2006-01-2 15:04:5 -0700", - "2006-01-2 15:4:05 -0700", - "2006-01-2 15:4:5 -0700", - "2006-1-2 15:04:05 -0700", - "2006-1-2 15:04:5 -0700", - "2006-1-2 15:4:05 -0700", - "2006-1-2 15:4:5 -0700", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + case dateDigitDashWsWsOffset: + // 2006-01-02 15:04:05 -0700 + for _, layout := range []string{ + "2006-01-02 15:04:05 -0700", + "2006-01-02 15:04:5 -0700", + "2006-01-02 15:4:05 -0700", + "2006-01-02 15:4:5 -0700", + "2006-1-02 15:04:05 -0700", + "2006-1-02 15:4:05 -0700", + "2006-1-02 15:04:5 -0700", + "2006-1-02 15:4:5 -0700", + "2006-01-2 15:04:05 -0700", + "2006-01-2 15:04:5 -0700", + "2006-01-2 15:4:05 -0700", + "2006-01-2 15:4:5 -0700", + "2006-1-2 15:04:05 -0700", + "2006-1-2 15:04:5 -0700", + "2006-1-2 15:4:05 -0700", + "2006-1-2 15:4:5 -0700", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } - case stateDigitDashWsWsOffsetColon: - // 2006-01-02 15:04:05 -07:00 - switch { - case part2Len == 2 && part3Len == 2: - for _, layout := range []string{ - "2006-01-02 15:04:05 -07:00", - "2006-01-02 15:04:5 -07:00", - "2006-01-02 15:4:05 -07:00", - "2006-01-02 15:4:5 -07:00", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil + case dateDigitDashWsWsOffsetColon: + // 2006-01-02 15:04:05 -07:00 + switch { + case part2Len == 2 && part3Len == 2: + for _, layout := range []string{ + "2006-01-02 15:04:05 -07:00", + "2006-01-02 15:04:5 -07:00", + "2006-01-02 15:4:05 -07:00", + "2006-01-02 15:4:5 -07:00", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } + case part2Len == 2 && part3Len == 1: + for _, layout := range []string{ + "2006-01-2 15:04:05 -07:00", + "2006-01-2 15:04:5 -07:00", + "2006-01-2 15:4:05 -07:00", + "2006-01-2 15:4:5 -07:00", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } + case part2Len == 1 && part3Len == 2: + for _, layout := range []string{ + "2006-1-02 15:04:05 -07:00", + "2006-1-02 15:4:05 -07:00", + "2006-1-02 15:04:5 -07:00", + "2006-1-02 15:4:5 -07:00", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } + case part2Len == 1 && part3Len == 1: + for _, layout := range []string{ + "2006-1-2 15:04:05 -07:00", + "2006-1-2 15:04:5 -07:00", + "2006-1-2 15:4:05 -07:00", + "2006-1-2 15:4:5 -07:00", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } + } + + case dateDigitDashWsWsOffsetAlpha: + // 2015-02-18 00:12:00 +0000 UTC + + switch { + case part2Len == 2 && part3Len == 2: + for _, layout := range []string{ + "2006-01-02 15:04:05 -0700 MST", + "2006-01-02 15:04:5 -0700 MST", + "2006-01-02 15:4:05 -0700 MST", + "2006-01-02 15:4:5 -0700 MST", + "2006-01-02 15:04:05 +0000 GMT", + "2006-01-02 15:04:5 +0000 GMT", + "2006-01-02 15:4:05 +0000 GMT", + "2006-01-02 15:4:5 +0000 GMT", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } + case part2Len == 2 && part3Len == 1: + for _, layout := range []string{ + "2006-01-2 15:04:05 -0700 MST", + "2006-01-2 15:04:5 -0700 MST", + "2006-01-2 15:4:05 -0700 MST", + "2006-01-2 15:4:5 -0700 MST", + "2006-01-2 15:04:05 +0000 GMT", + "2006-01-2 15:04:5 +0000 GMT", + "2006-01-2 15:4:05 +0000 GMT", + "2006-01-2 15:4:5 +0000 GMT", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } + case part2Len == 1 && part3Len == 2: + for _, layout := range []string{ + "2006-1-02 15:04:05 -0700 MST", + "2006-1-02 15:4:05 -0700 MST", + "2006-1-02 15:04:5 -0700 MST", + "2006-1-02 15:4:5 -0700 MST", + "2006-1-02 15:04:05 +0000 GMT", + "2006-1-02 15:4:05 +0000 GMT", + "2006-1-02 15:04:5 +0000 GMT", + "2006-1-02 15:4:5 +0000 GMT", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } + case part2Len == 1 && part3Len == 1: + for _, layout := range []string{ + "2006-1-2 15:04:05 -0700 MST", + "2006-1-2 15:04:5 -0700 MST", + "2006-1-2 15:4:05 -0700 MST", + "2006-1-2 15:4:5 -0700 MST", + "2006-1-2 15:04:05 +0000 GMT", + "2006-1-2 15:04:5 +0000 GMT", + "2006-1-2 15:4:05 +0000 GMT", + "2006-1-2 15:4:5 +0000 GMT", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } + } + + case dateDigitDashWsWsOffsetColonAlpha: + // 2015-02-18 00:12:00 +00:00 UTC + for _, layout := range []string{ + "2006-01-02 15:04:05 -07:00 UTC", + "2006-01-02 15:04:5 -07:00 UTC", + "2006-01-02 15:4:05 -07:00 UTC", + "2006-01-02 15:4:5 -07:00 UTC", + "2006-1-02 15:04:05 -07:00 UTC", + "2006-1-02 15:4:05 -07:00 UTC", + "2006-1-02 15:04:5 -07:00 UTC", + "2006-1-02 15:4:5 -07:00 UTC", + "2006-01-2 15:04:05 -07:00 UTC", + "2006-01-2 15:04:5 -07:00 UTC", + "2006-01-2 15:4:05 -07:00 UTC", + "2006-01-2 15:4:5 -07:00 UTC", + "2006-1-2 15:04:05 -07:00 UTC", + "2006-1-2 15:04:5 -07:00 UTC", + "2006-1-2 15:4:05 -07:00 UTC", + "2006-1-2 15:4:5 -07:00 UTC", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } + + case dateDigitDashWsOffset: + // 2017-07-19 03:21:51+00:00 + for _, layout := range []string{ + "2006-01-02 15:04:05-07:00", + "2006-01-02 15:04:5-07:00", + "2006-01-02 15:4:05-07:00", + "2006-01-02 15:4:5-07:00", + "2006-1-02 15:04:05-07:00", + "2006-1-02 15:4:05-07:00", + "2006-1-02 15:04:5-07:00", + "2006-1-02 15:4:5-07:00", + "2006-01-2 15:04:05-07:00", + "2006-01-2 15:04:5-07:00", + "2006-01-2 15:4:05-07:00", + "2006-01-2 15:4:5-07:00", + "2006-1-2 15:04:05-07:00", + "2006-1-2 15:04:5-07:00", + "2006-1-2 15:4:05-07:00", + "2006-1-2 15:4:5-07:00", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } + + case dateDigitDashWsWsAlpha: + // 2014-12-16 06:20:00 UTC + for _, layout := range []string{ + "2006-01-02 15:04:05 UTC", + "2006-01-02 15:04:5 UTC", + "2006-01-02 15:4:05 UTC", + "2006-01-02 15:4:5 UTC", + "2006-1-02 15:04:05 UTC", + "2006-1-02 15:4:05 UTC", + "2006-1-02 15:04:5 UTC", + "2006-1-02 15:4:5 UTC", + "2006-01-2 15:04:05 UTC", + "2006-01-2 15:04:5 UTC", + "2006-01-2 15:4:05 UTC", + "2006-01-2 15:4:5 UTC", + "2006-1-2 15:04:05 UTC", + "2006-1-2 15:04:5 UTC", + "2006-1-2 15:4:05 UTC", + "2006-1-2 15:4:5 UTC", + "2006-01-02 15:04:05 GMT", + "2006-01-02 15:04:5 GMT", + "2006-01-02 15:4:05 GMT", + "2006-01-02 15:4:5 GMT", + "2006-1-02 15:04:05 GMT", + "2006-1-02 15:4:05 GMT", + "2006-1-02 15:04:5 GMT", + "2006-1-02 15:4:5 GMT", + "2006-01-2 15:04:05 GMT", + "2006-01-2 15:04:5 GMT", + "2006-01-2 15:4:05 GMT", + "2006-01-2 15:4:5 GMT", + "2006-1-2 15:04:05 GMT", + "2006-1-2 15:04:5 GMT", + "2006-1-2 15:4:05 GMT", + "2006-1-2 15:4:5 GMT", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } + + if len(datestr) > len("2006-01-02 03:04:05") { + t, err := parse("2006-01-02 03:04:05", datestr[:len("2006-01-02 03:04:05")], loc) + if err == nil { + return t, nil + } + } + + case dateDigitDashWsPeriod: + // 2012-08-03 18:31:59.257000000 + // 2014-04-26 17:24:37.3186369 + // 2017-01-27 00:07:31.945167 + // 2016-03-14 00:00:00.000 + for _, layout := range []string{ + "2006-01-02 15:04:05", + "2006-01-02 15:04:5", + "2006-01-02 15:4:05", + "2006-01-02 15:4:5", + "2006-1-02 15:04:05", + "2006-1-02 15:4:05", + "2006-1-02 15:04:5", + "2006-1-02 15:4:5", + "2006-01-2 15:04:05", + "2006-01-2 15:04:5", + "2006-01-2 15:4:05", + "2006-01-2 15:4:5", + "2006-1-2 15:04:05", + "2006-1-2 15:04:5", + "2006-1-2 15:4:05", + "2006-1-2 15:4:5", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } + + case dateDigitDashWsPeriodAlpha: + // 2012-08-03 18:31:59.257000000 UTC + // 2014-04-26 17:24:37.3186369 UTC + // 2017-01-27 00:07:31.945167 UTC + // 2016-03-14 00:00:00.000 UTC + for _, layout := range []string{ + "2006-01-02 15:04:05 UTC", + "2006-01-02 15:04:5 UTC", + "2006-01-02 15:4:05 UTC", + "2006-01-02 15:4:5 UTC", + "2006-1-02 15:04:05 UTC", + "2006-1-02 15:4:05 UTC", + "2006-1-02 15:04:5 UTC", + "2006-1-02 15:4:5 UTC", + "2006-01-2 15:04:05 UTC", + "2006-01-2 15:04:5 UTC", + "2006-01-2 15:4:05 UTC", + "2006-01-2 15:4:5 UTC", + "2006-1-2 15:04:05 UTC", + "2006-1-2 15:04:5 UTC", + "2006-1-2 15:4:05 UTC", + "2006-1-2 15:4:5 UTC", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } + + case dateDigitDashWsPeriodOffset: + // 2012-08-03 18:31:59.257000000 +0000 + // 2014-04-26 17:24:37.3186369 +0000 + // 2017-01-27 00:07:31.945167 +0000 + // 2016-03-14 00:00:00.000 +0000 + for _, layout := range []string{ + "2006-01-02 15:04:05 -0700", + "2006-01-02 15:04:5 -0700", + "2006-01-02 15:4:05 -0700", + "2006-01-02 15:4:5 -0700", + "2006-1-02 15:04:05 -0700", + "2006-1-02 15:4:05 -0700", + "2006-1-02 15:04:5 -0700", + "2006-1-02 15:4:5 -0700", + "2006-01-2 15:04:05 -0700", + "2006-01-2 15:04:5 -0700", + "2006-01-2 15:4:05 -0700", + "2006-01-2 15:4:5 -0700", + "2006-1-2 15:04:05 -0700", + "2006-1-2 15:04:5 -0700", + "2006-1-2 15:4:05 -0700", + "2006-1-2 15:4:5 -0700", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } + case dateDigitDashWsPeriodOffsetAlpha: + // 2012-08-03 18:31:59.257000000 +0000 UTC + // 2014-04-26 17:24:37.3186369 +0000 UTC + // 2017-01-27 00:07:31.945167 +0000 UTC + // 2016-03-14 00:00:00.000 +0000 UTC + for _, layout := range []string{ + "2006-01-02 15:04:05 -0700 UTC", + "2006-01-02 15:04:5 -0700 UTC", + "2006-01-02 15:4:05 -0700 UTC", + "2006-01-02 15:4:5 -0700 UTC", + "2006-1-02 15:04:05 -0700 UTC", + "2006-1-02 15:4:05 -0700 UTC", + "2006-1-02 15:04:5 -0700 UTC", + "2006-1-02 15:4:5 -0700 UTC", + "2006-01-2 15:04:05 -0700 UTC", + "2006-01-2 15:04:5 -0700 UTC", + "2006-01-2 15:4:05 -0700 UTC", + "2006-01-2 15:4:5 -0700 UTC", + "2006-1-2 15:04:05 -0700 UTC", + "2006-1-2 15:04:5 -0700 UTC", + "2006-1-2 15:4:05 -0700 UTC", + "2006-1-2 15:4:5 -0700 UTC", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } } - } - case part2Len == 2 && part3Len == 1: - for _, layout := range []string{ - "2006-01-2 15:04:05 -07:00", - "2006-01-2 15:04:5 -07:00", - "2006-01-2 15:4:05 -07:00", - "2006-01-2 15:4:5 -07:00", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - case part2Len == 1 && part3Len == 2: - for _, layout := range []string{ - "2006-1-02 15:04:05 -07:00", - "2006-1-02 15:4:05 -07:00", - "2006-1-02 15:04:5 -07:00", - "2006-1-02 15:4:5 -07:00", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - case part2Len == 1 && part3Len == 1: - for _, layout := range []string{ - "2006-1-2 15:04:05 -07:00", - "2006-1-2 15:04:5 -07:00", - "2006-1-2 15:4:05 -07:00", - "2006-1-2 15:4:5 -07:00", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - } - - case stateDigitDashWsWsOffsetAlpha: - // 2015-02-18 00:12:00 +0000 UTC - - switch { - case part2Len == 2 && part3Len == 2: - for _, layout := range []string{ - "2006-01-02 15:04:05 -0700 MST", - "2006-01-02 15:04:5 -0700 MST", - "2006-01-02 15:4:05 -0700 MST", - "2006-01-02 15:4:5 -0700 MST", - "2006-01-02 15:04:05 +0000 GMT", - "2006-01-02 15:04:5 +0000 GMT", - "2006-01-02 15:4:05 +0000 GMT", - "2006-01-02 15:4:5 +0000 GMT", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - case part2Len == 2 && part3Len == 1: - for _, layout := range []string{ - "2006-01-2 15:04:05 -0700 MST", - "2006-01-2 15:04:5 -0700 MST", - "2006-01-2 15:4:05 -0700 MST", - "2006-01-2 15:4:5 -0700 MST", - "2006-01-2 15:04:05 +0000 GMT", - "2006-01-2 15:04:5 +0000 GMT", - "2006-01-2 15:4:05 +0000 GMT", - "2006-01-2 15:4:5 +0000 GMT", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - case part2Len == 1 && part3Len == 2: - for _, layout := range []string{ - "2006-1-02 15:04:05 -0700 MST", - "2006-1-02 15:4:05 -0700 MST", - "2006-1-02 15:04:5 -0700 MST", - "2006-1-02 15:4:5 -0700 MST", - "2006-1-02 15:04:05 +0000 GMT", - "2006-1-02 15:4:05 +0000 GMT", - "2006-1-02 15:04:5 +0000 GMT", - "2006-1-02 15:4:5 +0000 GMT", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - case part2Len == 1 && part3Len == 1: - for _, layout := range []string{ - "2006-1-2 15:04:05 -0700 MST", - "2006-1-2 15:04:5 -0700 MST", - "2006-1-2 15:4:05 -0700 MST", - "2006-1-2 15:4:5 -0700 MST", - "2006-1-2 15:04:05 +0000 GMT", - "2006-1-2 15:04:5 +0000 GMT", - "2006-1-2 15:4:05 +0000 GMT", - "2006-1-2 15:4:5 +0000 GMT", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - } - - case stateDigitDashWsWsOffsetColonAlpha: - // 2015-02-18 00:12:00 +00:00 UTC - for _, layout := range []string{ - "2006-01-02 15:04:05 -07:00 UTC", - "2006-01-02 15:04:5 -07:00 UTC", - "2006-01-02 15:4:05 -07:00 UTC", - "2006-01-02 15:4:5 -07:00 UTC", - "2006-1-02 15:04:05 -07:00 UTC", - "2006-1-02 15:4:05 -07:00 UTC", - "2006-1-02 15:04:5 -07:00 UTC", - "2006-1-02 15:4:5 -07:00 UTC", - "2006-01-2 15:04:05 -07:00 UTC", - "2006-01-2 15:04:5 -07:00 UTC", - "2006-01-2 15:4:05 -07:00 UTC", - "2006-01-2 15:4:5 -07:00 UTC", - "2006-1-2 15:04:05 -07:00 UTC", - "2006-1-2 15:04:5 -07:00 UTC", - "2006-1-2 15:4:05 -07:00 UTC", - "2006-1-2 15:4:5 -07:00 UTC", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - - case stateDigitDashWsOffset: - // 2017-07-19 03:21:51+00:00 - for _, layout := range []string{ - "2006-01-02 15:04:05-07:00", - "2006-01-02 15:04:5-07:00", - "2006-01-02 15:4:05-07:00", - "2006-01-02 15:4:5-07:00", - "2006-1-02 15:04:05-07:00", - "2006-1-02 15:4:05-07:00", - "2006-1-02 15:04:5-07:00", - "2006-1-02 15:4:5-07:00", - "2006-01-2 15:04:05-07:00", - "2006-01-2 15:04:5-07:00", - "2006-01-2 15:4:05-07:00", - "2006-01-2 15:4:5-07:00", - "2006-1-2 15:04:05-07:00", - "2006-1-2 15:04:5-07:00", - "2006-1-2 15:4:05-07:00", - "2006-1-2 15:4:5-07:00", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - - case stateDigitDashWsWsAlpha: - // 2014-12-16 06:20:00 UTC - for _, layout := range []string{ - "2006-01-02 15:04:05 UTC", - "2006-01-02 15:04:5 UTC", - "2006-01-02 15:4:05 UTC", - "2006-01-02 15:4:5 UTC", - "2006-1-02 15:04:05 UTC", - "2006-1-02 15:4:05 UTC", - "2006-1-02 15:04:5 UTC", - "2006-1-02 15:4:5 UTC", - "2006-01-2 15:04:05 UTC", - "2006-01-2 15:04:5 UTC", - "2006-01-2 15:4:05 UTC", - "2006-01-2 15:4:5 UTC", - "2006-1-2 15:04:05 UTC", - "2006-1-2 15:04:5 UTC", - "2006-1-2 15:4:05 UTC", - "2006-1-2 15:4:5 UTC", - "2006-01-02 15:04:05 GMT", - "2006-01-02 15:04:5 GMT", - "2006-01-02 15:4:05 GMT", - "2006-01-02 15:4:5 GMT", - "2006-1-02 15:04:05 GMT", - "2006-1-02 15:4:05 GMT", - "2006-1-02 15:04:5 GMT", - "2006-1-02 15:4:5 GMT", - "2006-01-2 15:04:05 GMT", - "2006-01-2 15:04:5 GMT", - "2006-01-2 15:4:05 GMT", - "2006-01-2 15:4:5 GMT", - "2006-1-2 15:04:05 GMT", - "2006-1-2 15:04:5 GMT", - "2006-1-2 15:4:05 GMT", - "2006-1-2 15:4:5 GMT", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - - if len(datestr) > len("2006-01-02 03:04:05") { - t, err := parse("2006-01-02 03:04:05", datestr[:len("2006-01-02 03:04:05")], loc) - if err == nil { - return t, nil - } - } - - case stateDigitDashWsPeriod: - // 2012-08-03 18:31:59.257000000 - // 2014-04-26 17:24:37.3186369 - // 2017-01-27 00:07:31.945167 - // 2016-03-14 00:00:00.000 - for _, layout := range []string{ - "2006-01-02 15:04:05", - "2006-01-02 15:04:5", - "2006-01-02 15:4:05", - "2006-01-02 15:4:5", - "2006-1-02 15:04:05", - "2006-1-02 15:4:05", - "2006-1-02 15:04:5", - "2006-1-02 15:4:5", - "2006-01-2 15:04:05", - "2006-01-2 15:04:5", - "2006-01-2 15:4:05", - "2006-01-2 15:4:5", - "2006-1-2 15:04:05", - "2006-1-2 15:04:5", - "2006-1-2 15:4:05", - "2006-1-2 15:4:5", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - - case stateDigitDashWsPeriodAlpha: - // 2012-08-03 18:31:59.257000000 UTC - // 2014-04-26 17:24:37.3186369 UTC - // 2017-01-27 00:07:31.945167 UTC - // 2016-03-14 00:00:00.000 UTC - for _, layout := range []string{ - "2006-01-02 15:04:05 UTC", - "2006-01-02 15:04:5 UTC", - "2006-01-02 15:4:05 UTC", - "2006-01-02 15:4:5 UTC", - "2006-1-02 15:04:05 UTC", - "2006-1-02 15:4:05 UTC", - "2006-1-02 15:04:5 UTC", - "2006-1-02 15:4:5 UTC", - "2006-01-2 15:04:05 UTC", - "2006-01-2 15:04:5 UTC", - "2006-01-2 15:4:05 UTC", - "2006-01-2 15:4:5 UTC", - "2006-1-2 15:04:05 UTC", - "2006-1-2 15:04:5 UTC", - "2006-1-2 15:4:05 UTC", - "2006-1-2 15:4:5 UTC", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - - case stateDigitDashWsPeriodOffset: - // 2012-08-03 18:31:59.257000000 +0000 - // 2014-04-26 17:24:37.3186369 +0000 - // 2017-01-27 00:07:31.945167 +0000 - // 2016-03-14 00:00:00.000 +0000 - for _, layout := range []string{ - "2006-01-02 15:04:05 -0700", - "2006-01-02 15:04:5 -0700", - "2006-01-02 15:4:05 -0700", - "2006-01-02 15:4:5 -0700", - "2006-1-02 15:04:05 -0700", - "2006-1-02 15:4:05 -0700", - "2006-1-02 15:04:5 -0700", - "2006-1-02 15:4:5 -0700", - "2006-01-2 15:04:05 -0700", - "2006-01-2 15:04:5 -0700", - "2006-01-2 15:4:05 -0700", - "2006-01-2 15:4:5 -0700", - "2006-1-2 15:04:05 -0700", - "2006-1-2 15:04:5 -0700", - "2006-1-2 15:4:05 -0700", - "2006-1-2 15:4:5 -0700", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - - case stateDigitDashWsPeriodOffsetAlpha: - // 2012-08-03 18:31:59.257000000 +0000 UTC - // 2014-04-26 17:24:37.3186369 +0000 UTC - // 2017-01-27 00:07:31.945167 +0000 UTC - // 2016-03-14 00:00:00.000 +0000 UTC - for _, layout := range []string{ - "2006-01-02 15:04:05 -0700 UTC", - "2006-01-02 15:04:5 -0700 UTC", - "2006-01-02 15:4:05 -0700 UTC", - "2006-01-02 15:4:5 -0700 UTC", - "2006-1-02 15:04:05 -0700 UTC", - "2006-1-02 15:4:05 -0700 UTC", - "2006-1-02 15:04:5 -0700 UTC", - "2006-1-02 15:4:5 -0700 UTC", - "2006-01-2 15:04:05 -0700 UTC", - "2006-01-2 15:04:5 -0700 UTC", - "2006-01-2 15:4:05 -0700 UTC", - "2006-01-2 15:4:5 -0700 UTC", - "2006-1-2 15:04:05 -0700 UTC", - "2006-1-2 15:04:5 -0700 UTC", - "2006-1-2 15:4:05 -0700 UTC", - "2006-1-2 15:4:5 -0700 UTC", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - - case stateDigitDotDot: + */ + case dateDigitDotDot: switch { case len(datestr) == len("01.02.2006"): return parse("01.02.2006", datestr, loc) @@ -1263,7 +1357,7 @@ iterRunes: } } - case stateDigitWs: + case dateDigitWs: // 18 January 2018 // 8 January 2018 if part1Len == 1 { @@ -1271,7 +1365,7 @@ iterRunes: } return parse("02 January 2006", datestr, loc) // 02 Jan 2018 23:59 - case stateDigitWsMoShortColon: + case dateDigitWsMoShortColon: // 2 Jan 2018 23:59 // 02 Jan 2018 23:59 if part1Len == 1 { @@ -1293,7 +1387,7 @@ iterRunes: return t, nil } } - case stateDigitWsMoShortColonColon: + case dateDigitWsMoShortColonColon: // 02 Jan 2018 23:59:45 if part1Len == 1 { for _, layout := range []string{ @@ -1318,7 +1412,7 @@ iterRunes: } } - case stateDigitWsMoShortComma: + case dateDigitWsMoShortComma: // 12 Feb 2006, 19:17 // 12 Feb 2006, 19:17:22 for _, layout := range []string{ @@ -1340,23 +1434,39 @@ iterRunes: } } - case stateAlphaWSAlphaColon: + case dateAlphaWSDigitCommaWsYear: + // May 8, 2009 5:57:51 PM + for _, layout := range []string{ + "Jan 2, 2006 3:04:05 PM", + "Jan 2, 2006 3:4:05 PM", + "Jan 2, 2006 3:4:5 PM", + "Jan 2, 2006 3:04:5 PM", + "Jan 02, 2006 3:04:05 PM", + "Jan 02, 2006 3:4:05 PM", + "Jan 02, 2006 3:4:5 PM", + "Jan 02, 2006 3:04:5 PM", + } { + if t, err := parse(layout, datestr, loc); err == nil { + return t, nil + } + } + case dateAlphaWSAlphaColon: // Mon Jan _2 15:04:05 2006 return parse(time.ANSIC, datestr, loc) - case stateAlphaWSAlphaColonOffset: + case dateAlphaWSAlphaColonOffset: // Mon Jan 02 15:04:05 -0700 2006 return parse(time.RubyDate, datestr, loc) - case stateAlphaWSAlphaColonAlpha: + case dateAlphaWSAlphaColonAlpha: // Mon Jan _2 15:04:05 MST 2006 return parse(time.UnixDate, datestr, loc) - case stateAlphaWSAlphaColonAlphaOffset: + case dateAlphaWSAlphaColonAlphaOffset: // Mon Aug 10 15:44:11 UTC+0100 2015 return parse("Mon Jan 02 15:04:05 MST-0700 2006", datestr, loc) - case stateAlphaWSAlphaColonAlphaOffsetAlpha: + case dateAlphaWSAlphaColonAlphaOffsetAlpha: // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) if len(datestr) > len("Mon Jan 02 2006 15:04:05 MST-0700") { // What effing time stamp is this? @@ -1364,7 +1474,7 @@ iterRunes: dateTmp := datestr[:33] return parse("Mon Jan 02 2006 15:04:05 MST-0700", dateTmp, loc) } - case stateDigitSlash: // starts digit then slash 02/ (but nothing else) + case dateDigitSlash: // starts digit then slash 02/ (but nothing else) // 3/1/2014 // 10/13/2014 // 01/02/2006 @@ -1381,7 +1491,7 @@ iterRunes: } } - case stateDigitSlashWSColon: // starts digit then slash 02/ more digits/slashes then whitespace + case dateDigitSlashWSColon: // starts digit then slash 02/ more digits/slashes then whitespace // 4/8/2014 22:05 // 04/08/2014 22:05 // 2014/4/8 22:05 @@ -1401,7 +1511,7 @@ iterRunes: } } - case stateDigitSlashWSColonAMPM: // starts digit then slash 02/ more digits/slashes then whitespace + case dateDigitSlashWSColonAMPM: // starts digit then slash 02/ more digits/slashes then whitespace // 4/8/2014 22:05 PM // 04/08/2014 22:05 PM // 04/08/2014 1:05 PM @@ -1430,7 +1540,7 @@ iterRunes: } } - case stateDigitSlashWSColonColon: // starts digit then slash 02/ more digits/slashes then whitespace double colons + case dateDigitSlashWSColonColon: // starts digit then slash 02/ more digits/slashes then whitespace double colons // 2014/07/10 06:55:38.156283 // 03/19/2012 10:11:59 // 3/1/2012 10:11:59 @@ -1457,7 +1567,7 @@ iterRunes: } } - case stateDigitSlashWSColonColonAMPM: // starts digit then slash 02/ more digits/slashes then whitespace double colons + case dateDigitSlashWSColonColonAMPM: // starts digit then slash 02/ more digits/slashes then whitespace double colons // 2014/07/10 06:55:38.156283 PM // 03/19/2012 10:11:59 PM // 3/1/2012 10:11:59 PM @@ -1488,13 +1598,13 @@ iterRunes: } } } - case stateDigitChineseYear: - // stateDigitChineseYear + case dateDigitChineseYear: + // dateDigitChineseYear // 2014年04月08日 return parse("2006年01月02日", datestr, loc) - case stateDigitChineseYearWs: + case dateDigitChineseYearWs: return parse("2006年01月02日 15:04:05", datestr, loc) - case stateWeekdayCommaOffset: + case dateWeekdayCommaOffset: // Monday, 02 Jan 2006 15:04:05 -0700 // Monday, 02 Jan 2006 15:04:05 +0100 for _, layout := range []string{ @@ -1508,7 +1618,7 @@ iterRunes: } } - case stateWeekdayCommaDash: + case dateWeekdayCommaDash: // Monday, 02-Jan-06 15:04:05 MST for _, layout := range []string{ "Monday, 02-Jan-06 15:04:05 MST", @@ -1529,7 +1639,7 @@ iterRunes: } } - case stateWeekdayAbbrevComma: // Starts alpha then comma + case dateWeekdayAbbrevComma: // Starts alpha then comma // Mon, 02-Jan-06 15:04:05 MST // Mon, 02 Jan 2006 15:04:05 MST for _, layout := range []string{ @@ -1543,7 +1653,7 @@ iterRunes: } } - case stateWeekdayAbbrevCommaDash: + case dateWeekdayAbbrevCommaDash: // Mon, 02-Jan-06 15:04:05 MST // Mon, 2-Jan-06 15:04:05 MST for _, layout := range []string{ @@ -1565,7 +1675,7 @@ iterRunes: } } - case stateWeekdayAbbrevCommaOffset: + case dateWeekdayAbbrevCommaOffset: // Mon, 02 Jan 2006 15:04:05 -0700 // Thu, 13 Jul 2017 08:58:40 +0100 // RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone @@ -1582,7 +1692,7 @@ iterRunes: } } - case stateWeekdayAbbrevCommaOffsetZone: + case dateWeekdayAbbrevCommaOffsetZone: // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) if part3Len == 1 { return parse("Mon, 2 Jan 2006 15:04:05 -0700 (MST)", datestr, loc) diff --git a/parseany_test.go b/parseany_test.go index 0aeeade..38961d4 100644 --- a/parseany_test.go +++ b/parseany_test.go @@ -222,6 +222,16 @@ func TestParse(t *testing.T) { ts = MustParse("2 Feb 2006 19:17:22") assert.Equal(t, "2006-02-02 19:17:22 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) + // // 19 Dec 2013 12:15:23 GMT + // ts = MustParse("12 Feb 2006 19:17:22 GMT") + // assert.Equal(t, "2006-02-12 19:17:22 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) + // ts = MustParse("2 Feb 2006 19:17:22 GMT") + // assert.Equal(t, "2006-02-02 19:17:22 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) + + // // 28 Mar 2010 15:45:30 +1100 + // ts = MustParse("12 Feb 2006 19:17:22") + // assert.Equal(t, "2006-02-12 19:17:22 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) + ts = MustParse("2013-Feb-03") assert.Equal(t, "2013-02-03 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) From 7825aa09f161ee65c27a6814e5a90bc93a3ca57e Mon Sep 17 00:00:00 2001 From: Aaron Raddon Date: Sun, 25 Feb 2018 22:46:45 -0800 Subject: [PATCH 02/10] Dateparse seperate time, date parsing --- parseany.go | 1437 ++++++++++++++++++++++----------------------------- 1 file changed, 611 insertions(+), 826 deletions(-) diff --git a/parseany.go b/parseany.go index 65198f8..4d15e37 100644 --- a/parseany.go +++ b/parseany.go @@ -25,10 +25,11 @@ const ( dateStart dateState = iota dateDigit dateDigitDash - dateDigitDashWs - dateDigitDashT - dateDigitDashTZ - dateDigitDashAlpha + dateDigitDashDash + dateDigitDashDashWs + dateDigitDashDashT + dateDigitDashDashTZ + dateDigitDashDashAlpha dateDigitDot dateDigitDotDot dateDigitSlash @@ -63,8 +64,9 @@ const ( dateWeekdayAbbrevCommaDash dateWeekdayAbbrevCommaOffset dateWeekdayAbbrevCommaOffsetZone - - // Now time ones +) +const ( + // Time state timeIgnore timeState = iota timeStart timeWs @@ -75,13 +77,15 @@ const ( timeWsOffsetAlpha timeWsOffsetColonAlpha timeWsOffsetColon - timeOffset + timeOffset // 10 timeOffsetColon timeAlpha timePeriod timePeriodAlpha timePeriodOffset - timePeriodOffsetAlpha + timePeriodWs + timePeriodWsOffset + timePeriodWsOffsetAlpha // 18 timeZ timeZDigit ) @@ -141,17 +145,131 @@ func parse(layout, datestr string, loc *time.Location) (time.Time, error) { return time.ParseInLocation(layout, datestr, loc) } +type parser struct { + loc *time.Location + preferMonthFirst bool + stateDate dateState + stateTime timeState + format []byte + datestr string + part1Len int + part2Len int + part3Len int + yeari int + yearlen int + moi int + molen int + dayi int + daylen int + houri int + hourlen int + mini int + minlen int + seci int + seclen int + msi int + mslen int + offseti int +} + +func newParser(dateStr string, loc *time.Location) parser { + p := parser{ + stateDate: dateStart, + stateTime: timeIgnore, + datestr: dateStr, + } + p.format = []byte(dateStr) + return p +} +func (p parser) set(start int, val string) { + if len(p.format) < start+len(val) { + u.Warnf("invalid format %d: %v", start, val) + return + } + for i, r := range val { + p.format[start+i] = byte(r) + } +} +func (p parser) setMonth() { + if p.moi == 0 { + u.Warnf("not set %+v", p) + } + if p.molen == 2 { + p.set(p.moi, "01") + } else { + p.set(p.moi, "1") + } +} +func (p parser) setDay() { + if p.dayi == 0 { + u.Warnf("not set %+v", p) + } + if p.daylen == 2 { + p.set(p.dayi, "02") + } else { + p.set(p.dayi, "2") + } +} +func (p parser) coalesceTime(end int) { + // 03:04:05 + // 15:04:05 + // 3:04:05 + // 3:4:5 + // 15:04:05.00 + u.Infof("coalesceTime: %+v", p) + if p.houri > 0 { + if p.hourlen == 2 { + p.set(p.houri, "15") + } else { + p.set(p.houri, "3") + } + } + if p.mini > 0 { + if p.minlen == 0 { + p.minlen = end - p.mini + } + if p.minlen == 2 { + p.set(p.mini, "04") + } else { + p.set(p.mini, "4") + } + } + if p.seci > 0 { + if p.seclen == 0 { + p.seclen = end - p.seci + u.Infof("fixing seconds p.seci=%d seclen=%d end=%d", p.seci, p.seclen, end) + } + if p.seclen == 2 { + p.set(p.seci, "05") + } else { + p.set(p.seci, "5") + } + } + + if p.msi > 0 { + if p.mslen == 0 { + p.mslen = end - p.msi + u.Warnf("set mslen??? %v", p.datestr) + } + for i := 0; i < p.mslen; i++ { + p.format[p.msi+i] = '0' + } + } + + u.Debugf("coalesce %+v", p) +} +func (p parser) parse() (time.Time, error) { + u.Debugf("parse() %50s AS %50s", p.datestr, p.format) + if p.loc == nil { + return time.Parse(string(p.format), p.datestr) + } + return time.ParseInLocation(string(p.format), p.datestr, p.loc) +} func parseTime(datestr string, loc *time.Location) (time.Time, error) { - stateDate := dateStart - stateTime := timeIgnore - dateFormat := []byte(datestr) + p := newParser(datestr, loc) i := 0 - part1Len := 0 - part2Len := 0 - part3Len := 0 - // General strategy is to read rune by rune through the date looking for // certain hints of what type of date we are dealing with. // Hopefully we only need to read about 5 or 6 bytes before @@ -164,45 +282,51 @@ iterRunes: i += (bytesConsumed - 1) } - switch stateDate { + switch p.stateDate { case dateStart: if unicode.IsDigit(r) { - stateDate = dateDigit + p.stateDate = dateDigit } else if unicode.IsLetter(r) { - stateDate = dateAlpha - } - case dateDigit: // starts digits - if unicode.IsDigit(r) { - continue - } else if unicode.IsLetter(r) { - if r == '年' { - // Chinese Year - stateDate = dateDigitChineseYear - } + p.stateDate = dateAlpha } + case dateDigit: + switch r { case '-', '\u2212': - stateDate = dateDigitDash - part1Len = i + // 2006-01-02 + // 2006-01-02T15:04:05Z07:00 + // 13-Feb-03 + // 2013-Feb-03 + p.stateDate = dateDigitDash + p.yeari = 0 + p.yearlen = i + p.moi = i + 1 + if i == 4 { + p.set(0, "2006") + } case '/': - stateDate = dateDigitSlash - part1Len = i + p.stateDate = dateDigitSlash case '.': - stateDate = dateDigitDot - part1Len = i + p.stateDate = dateDigitDot case ' ': - stateDate = dateDigitWs - part1Len = i + p.stateDate = dateDigitWs + case '年': + // Chinese Year + p.stateDate = dateDigitChineseYear + default: + //if unicode.IsDigit(r) { + continue } + p.part1Len = i - case dateDigitDash: // starts digit then dash 02- + case dateDigitDash: // 2006-01-02 - // dateDigitDashT + // dateDigitDashDashT // 2006-01-02T15:04:05Z07:00 // 2017-06-25T17:46:57.45706582-07:00 // 2006-01-02T15:04:05.999999999Z07:00 // 2006-01-02T15:04:05+0000 - // dateDigitDashWs + // dateDigitDashDashWs // 2012-08-03 18:31:59.257000000 // 2014-04-26 17:24:37.3186369 // 2017-01-27 00:07:31.945167 @@ -211,27 +335,62 @@ iterRunes: // 2017-07-19 03:21:51+00:00 // 2013-04-01 22:43:22 // 2014-04-26 05:24:37 PM - // dateDigitDashAlpha + // dateDigitDashDashAlpha // 2013-Feb-03 - switch { - case r == '-': - part2Len = i - part1Len - 1 - case r == ' ': - part3Len = i - part1Len - part2Len - 1 - 1 - stateDate = dateDigitDashWs - stateTime = timeStart - break iterRunes - case r == 'T': - stateDate = dateDigitDashT - stateTime = timeStart - break iterRunes + // 13-Feb-03 + switch r { + case '-': + p.molen = i - p.moi + p.dayi = i + 1 + p.stateDate = dateDigitDashDash + p.setMonth() default: - if unicode.IsLetter(r) { - stateDate = dateDigitDashAlpha - break iterRunes + if unicode.IsDigit(r) { + //continue + } else if unicode.IsLetter(r) { + p.stateDate = dateDigitDashDashAlpha } } - case dateDigitSlash: // starts digit then slash 02/ + case dateDigitDashDash: + // 2006-01-02 + // dateDigitDashDashT + // 2006-01-02T15:04:05Z07:00 + // 2017-06-25T17:46:57.45706582-07:00 + // 2006-01-02T15:04:05.999999999Z07:00 + // 2006-01-02T15:04:05+0000 + // dateDigitDashDashWs + // 2012-08-03 18:31:59.257000000 + // 2014-04-26 17:24:37.3186369 + // 2017-01-27 00:07:31.945167 + // 2016-03-14 00:00:00.000 + // 2014-05-11 08:20:13,787 + // 2017-07-19 03:21:51+00:00 + // 2013-04-01 22:43:22 + // 2014-04-26 05:24:37 PM + + switch r { + case ' ': + p.daylen = i - p.dayi + p.stateDate = dateDigitDashDashWs + p.stateTime = timeStart + p.setDay() + break iterRunes + case 'T': + p.daylen = i - p.dayi + p.stateDate = dateDigitDashDashT + p.stateTime = timeStart + p.setDay() + break iterRunes + } + case dateDigitDashDashAlpha: + // 2013-Feb-03 + // 13-Feb-03 + switch r { + case '-': + p.set(p.moi, "Jan") + p.dayi = i + 1 + } + case dateDigitSlash: // 2014/07/10 06:55:38.156283 // 03/19/2012 10:11:59 // 04/2/2014 03:00:37 @@ -241,14 +400,18 @@ iterRunes: // 10/13/2014 // 01/02/2006 // 1/2/06 - if unicode.IsDigit(r) || r == '/' { - continue - } + switch r { case ' ': - stateDate = dateDigitSlashWS + p.stateDate = dateDigitSlashWS + case '/': + continue + default: + // if unicode.IsDigit(r) || r == '/' { + // continue + // } } - case dateDigitSlashWS: // starts digit then slash 02/ more digits/slashes then whitespace + case dateDigitSlashWS: // 2014/07/10 06:55:38.156283 // 03/19/2012 10:11:59 // 04/2/2014 03:00:37 @@ -257,9 +420,9 @@ iterRunes: // 4/8/14 22:05 switch r { case ':': - stateDate = dateDigitSlashWSColon + p.stateDate = dateDigitSlashWSColon } - case dateDigitSlashWSColon: // starts digit then slash 02/ more digits/slashes then whitespace + case dateDigitSlashWSColon: // 2014/07/10 06:55:38.156283 // 03/19/2012 10:11:59 // 04/2/2014 03:00:37 @@ -269,11 +432,11 @@ iterRunes: // 3/1/2012 10:11:59 AM switch r { case ':': - stateDate = dateDigitSlashWSColonColon + p.stateDate = dateDigitSlashWSColonColon case 'A', 'P': - stateDate = dateDigitSlashWSColonAMPM + p.stateDate = dateDigitSlashWSColonAMPM } - case dateDigitSlashWSColonColon: // starts digit then slash 02/ more digits/slashes then whitespace + case dateDigitSlashWSColonColon: // 2014/07/10 06:55:38.156283 // 03/19/2012 10:11:59 // 04/2/2014 03:00:37 @@ -283,7 +446,7 @@ iterRunes: // 3/1/2012 10:11:59 AM switch r { case 'A', 'P': - stateDate = dateDigitSlashWSColonColonAMPM + p.stateDate = dateDigitSlashWSColonColonAMPM } case dateDigitWs: @@ -296,8 +459,8 @@ iterRunes: // 12 Feb 2006, 19:17 // 12 Feb 2006, 19:17:22 if r == ' ' { - if i <= part1Len+len(" Feb") { - stateDate = dateDigitWsMoShort + if i <= p.part1Len+len(" Feb") { + p.stateDate = dateDigitWsMoShort } else { break iterRunes } @@ -313,9 +476,9 @@ iterRunes: // 12 Feb 2006, 19:17:22 switch r { case ':': - stateDate = dateDigitWsMoShortColon + p.stateDate = dateDigitWsMoShortColon case ',': - stateDate = dateDigitWsMoShortComma + p.stateDate = dateDigitWsMoShortComma } case dateDigitWsMoShortColon: // 02 Jan 2018 23:59 @@ -324,7 +487,7 @@ iterRunes: switch r { case ':': - stateDate = dateDigitWsMoShortColonColon + p.stateDate = dateDigitWsMoShortColonColon break iterRunes } @@ -334,14 +497,14 @@ iterRunes: // weekday %Y年%m月%e日 %A %I:%M %p // 2013年07月18日 星期四 10:27 上午 if r == ' ' { - stateDate = dateDigitChineseYearWs + p.stateDate = dateDigitChineseYearWs break } case dateDigitDot: // 3.31.2014 if r == '.' { - stateDate = dateDigitDotDot - part2Len = i + p.stateDate = dateDigitDotDot + p.part2Len = i } case dateDigitDotDot: // iterate all the way through @@ -373,17 +536,17 @@ iterRunes: // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) switch { case r == ' ': - stateDate = dateAlphaWS - dateFormat[0], dateFormat[1], dateFormat[2] = 'M', 'o', 'n' - part1Len = i + p.stateDate = dateAlphaWS + p.set(0, "Mon") + p.part1Len = i case r == ',': - part1Len = i + p.part1Len = i if i == 3 { - stateDate = dateWeekdayAbbrevComma - dateFormat[0], dateFormat[1], dateFormat[2] = 'M', 'o', 'n' + p.stateDate = dateWeekdayAbbrevComma + p.set(0, "Mon") } else { - stateDate = dateWeekdayComma - dateFormat[0], dateFormat[1], dateFormat[2], dateFormat[3], dateFormat[4], dateFormat[5] = 'M', 'o', 'n', 'd', 'a', 'y' + p.stateDate = dateWeekdayComma + p.set(0, "Monday") } i++ } @@ -397,12 +560,12 @@ iterRunes: switch { case r == '-': if i < 15 { - stateDate = dateWeekdayCommaDash + p.stateDate = dateWeekdayCommaDash break iterRunes } - stateDate = dateWeekdayCommaOffset + p.stateDate = dateWeekdayCommaOffset case r == '+': - stateDate = dateWeekdayCommaOffset + p.stateDate = dateWeekdayCommaOffset } case dateWeekdayAbbrevComma: // Starts alpha then comma // Mon, 02 Jan 2006 15:04:05 MST @@ -415,16 +578,16 @@ iterRunes: // dateWeekdayAbbrevCommaOffsetZone // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) switch { - case r == ' ' && part3Len == 0: - part3Len = i - part1Len - 2 + case r == ' ' && p.part3Len == 0: + p.part3Len = i - p.part1Len - 2 case r == '-': if i < 15 { - stateDate = dateWeekdayAbbrevCommaDash + p.stateDate = dateWeekdayAbbrevCommaDash break iterRunes } - stateDate = dateWeekdayAbbrevCommaOffset + p.stateDate = dateWeekdayAbbrevCommaOffset case r == '+': - stateDate = dateWeekdayAbbrevCommaOffset + p.stateDate = dateWeekdayAbbrevCommaOffset } case dateWeekdayAbbrevCommaOffset: @@ -435,7 +598,7 @@ iterRunes: // dateWeekdayAbbrevCommaOffsetZone // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) if r == '(' { - stateDate = dateWeekdayAbbrevCommaOffsetZone + p.stateDate = dateWeekdayAbbrevCommaOffsetZone } case dateAlphaWS: // Starts alpha then whitespace @@ -446,11 +609,11 @@ iterRunes: // Mon Aug 10 15:44:11 UTC+0100 2015 switch { case r == ' ': - part2Len = i - part1Len + p.part2Len = i - p.part1Len case unicode.IsLetter(r): - stateDate = dateAlphaWSAlpha + p.stateDate = dateAlphaWSAlpha case unicode.IsDigit(r): - stateDate = dateAlphaWSDigit + p.stateDate = dateAlphaWSDigit default: u.Warnf("can we drop isLetter? case r=%s", string(r)) } @@ -460,9 +623,9 @@ iterRunes: // May 8, 2009 5:57:51 PM switch { case r == ',': - stateDate = dateAlphaWSDigitComma + p.stateDate = dateAlphaWSDigitComma case unicode.IsDigit(r): - stateDate = dateAlphaWSDigit + p.stateDate = dateAlphaWSDigit default: u.Warnf("hm, can we drop a case here? %v", string(r)) } @@ -471,7 +634,7 @@ iterRunes: // May 8, 2009 5:57:51 PM switch { case r == ' ': - stateDate = dateAlphaWSDigitCommaWs + p.stateDate = dateAlphaWSDigitCommaWs default: u.Warnf("hm, can we drop a case here? %v", string(r)) return time.Time{}, fmt.Errorf("could not find format for %v expected white-space after comma", datestr) @@ -480,7 +643,7 @@ iterRunes: // x // May 8, 2009 5:57:51 PM if !unicode.IsDigit(r) { - stateDate = dateAlphaWSDigitCommaWsYear + p.stateDate = dateAlphaWSDigitCommaWsYear break iterRunes } @@ -491,263 +654,304 @@ iterRunes: // Mon Aug 10 15:44:11 UTC+0100 2015 // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) if r == ':' { - stateDate = dateAlphaWSAlphaColon + p.stateDate = dateAlphaWSAlphaColon } - case dateAlphaWSAlphaColon: // Alpha, whitespace, alpha, : + case dateAlphaWSAlphaColon: // Mon Jan _2 15:04:05 2006 // Mon Jan 02 15:04:05 -0700 2006 // Mon Jan _2 15:04:05 MST 2006 // Mon Aug 10 15:44:11 UTC+0100 2015 // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) if unicode.IsLetter(r) { - stateDate = dateAlphaWSAlphaColonAlpha + p.stateDate = dateAlphaWSAlphaColonAlpha } else if r == '-' || r == '+' { - stateDate = dateAlphaWSAlphaColonOffset + p.stateDate = dateAlphaWSAlphaColonOffset } - case dateAlphaWSAlphaColonAlpha: // Alpha, whitespace, alpha, :, alpha + case dateAlphaWSAlphaColonAlpha: // Mon Jan _2 15:04:05 MST 2006 // Mon Aug 10 15:44:11 UTC+0100 2015 // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) if r == '+' { - stateDate = dateAlphaWSAlphaColonAlphaOffset + p.stateDate = dateAlphaWSAlphaColonAlphaOffset } - case dateAlphaWSAlphaColonAlphaOffset: // Alpha, whitespace, alpha, : , alpha, offset, ? + case dateAlphaWSAlphaColonAlphaOffset: // Mon Aug 10 15:44:11 UTC+0100 2015 // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) if unicode.IsLetter(r) { - stateDate = dateAlphaWSAlphaColonAlphaOffsetAlpha + p.stateDate = dateAlphaWSAlphaColonAlphaOffsetAlpha } default: break iterRunes } } - timeMarker := i - time1Len := 0 - time2Len := 0 - time3Len := 0 + if p.stateTime == timeStart { + // increment first one, since the i++ occurs at end of loop + i++ -iterTimeRunes: - for ; i < len(datestr); i++ { - r, bytesConsumed := utf8.DecodeRuneInString(datestr[i:]) - if bytesConsumed > 1 { - i += (bytesConsumed - 1) - } - switch stateTime { - case timeIgnore: - // not used - case timeStart: - // 22:43:22 - // timeComma - // 08:20:13,787 - // timeWs - // 05:24:37 PM - // 06:20:00 UTC - // 00:12:00 +0000 UTC - // 15:04:05 -0700 - // 15:04:05 -07:00 - // timeOffset - // 03:21:51+00:00 - // timePeriod - // 17:24:37.3186369 - // 00:07:31.945167 - // 18:31:59.257000000 - // 00:00:00.000 - // timePeriodOffset - // 00:07:31.945167 +0000 - // 00:00:00.000 +0000 - // timePeriodOffsetAlpha - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - // timePeriodAlpha - // 06:20:00.000 UTC - switch r { - case ',': - if len(datestr) == len("2014-05-11 08:20:13,787") { - // go doesn't seem to parse this one natively? or did i miss it? - t, err := parse("2006-01-02 03:04:05", datestr[:i], loc) - if err == nil { - ms, err := strconv.Atoi(datestr[i+1:]) - if err == nil { - return time.Unix(0, t.UnixNano()+int64(ms)*1e6), nil - } - } - return t, err - } - case '-', '+': - stateTime = timeOffset - case '.': - stateTime = timePeriod - case ' ': - stateTime = timeWs - case ':': - if time2Len > 0 { - time3Len = i - timeMarker - } else if time1Len > 0 { - time2Len = i - timeMarker - } else if time1Len == 0 { - time1Len = i - timeMarker - } - timeMarker = i - u.Infof("time len: i=%d r=%s marker:%d 1:%d 2:%d 3:%d", i, string(r), timeMarker, time1Len, time2Len, time3Len) + iterTimeRunes: + for ; i < len(datestr); i++ { + r, bytesConsumed := utf8.DecodeRuneInString(datestr[i:]) + if bytesConsumed > 1 { + i += (bytesConsumed - 1) } - case timeOffset: - // With another +/- time-zone at end - // 15:04:05.999999999+07:00 - // 15:04:05.999999999-07:00 - // 15:04:05.999999+07:00 - // 15:04:05.999999-07:00 - // 15:04:05.999+07:00 - // 15:04:05.999-07:00 - // 15:04:05+07:00 - // 15:04:05-07:00 - if r == ':' { - stateTime = timeOffsetColon - } - /* - case dateDigitDashT: // starts digit then dash 02- then T - // dateDigitDashT - // 2006-01-02T15:04:05 - // dateDigitDashTZ - // 2006-01-02T15:04:05.999999999Z - // 2006-01-02T15:04:05.99999999Z - // 2006-01-02T15:04:05.9999999Z - // 2006-01-02T15:04:05.999999Z - // 2006-01-02T15:04:05.99999Z - // 2006-01-02T15:04:05.9999Z - // 2006-01-02T15:04:05.999Z - // 2006-01-02T15:04:05.99Z - // 2009-08-12T22:15Z - // dateDigitDashTZDigit - // 2006-01-02T15:04:05.999999999Z07:00 - // 2006-01-02T15:04:05Z07:00 - // With another dash aka time-zone at end - // dateDigitDashTOffset - // dateDigitDashTOffsetColon - // 2017-06-25T17:46:57.45706582-07:00 - // 2017-06-25T17:46:57+04:00 - // 2006-01-02T15:04:05+0000 + + switch p.stateTime { + case timeIgnore: + // not used + case timeStart: + // 22:43:22 + // timeComma + // 08:20:13,787 + // timeWs + // 05:24:37 PM + // 06:20:00 UTC + // 00:12:00 +0000 UTC + // 15:04:05 -0700 + // 15:04:05 -07:00 + // timeOffset + // 03:21:51+00:00 + // timePeriod + // 17:24:37.3186369 + // 00:07:31.945167 + // 18:31:59.257000000 + // 00:00:00.000 + // 15:04:05.999-07:00 + // timePeriodOffset + // 00:07:31.945167 +0000 + // 00:00:00.000 +0000 + // timePeriodOffsetAlpha + // 00:07:31.945167 +0000 UTC + // 00:00:00.000 +0000 UTC + // timePeriodAlpha + // 06:20:00.000 UTC + + if p.houri == 0 { + p.houri = i + } switch r { + case ',': + if len(datestr) == len("2014-05-11 08:20:13,787") { + // go doesn't seem to parse this one natively? or did i miss it? + t, err := parse("2006-01-02 03:04:05", datestr[:i], loc) + if err == nil { + ms, err := strconv.Atoi(datestr[i+1:]) + if err == nil { + return time.Unix(0, t.UnixNano()+int64(ms)*1e6), nil + } + } + return t, err + } case '-', '+': - stateTime = dateDigitDashTOffset + // 03:21:51+00:00 + p.stateTime = timeOffset + p.seclen = i - p.seci + p.offseti = i + case '.': + p.stateTime = timePeriod + p.seclen = i - p.seci + p.msi = i + 1 case 'Z': - stateTime = dateDigitDashTZ + p.stateTime = timeZ + if p.seci == 0 { + p.minlen = i - p.mini + u.Infof("mini:%d minlen:%d", p.mini, p.minlen) + } else { + p.seclen = i - p.seci + u.Infof("seci:%d seclen:%d", p.seci, p.seclen) + } + case ' ': + p.stateTime = timeWs + p.seclen = i - p.seci + case ':': + u.Infof("sss %s for %v", string(r), datestr) + if p.mini == 0 { + p.mini = i + 1 + p.hourlen = i - p.houri + } else if p.seci == 0 { + p.seci = i + 1 + p.minlen = i - p.mini + } + } - */ - case timeWs: - // timeAlpha - // 06:20:00 UTC - // timeWsAMPMMaybe - // 05:24:37 PM - // timeWsOffset - // 15:04:05 -0700 - // timeWsOffsetColon - // 15:04:05 -07:00 - // timeWsOffsetColonAlpha - // 00:12:00 +00:00 UTC - // timeWsOffsetAlpha - // 00:12:00 +0000 UTC - // timeZ - // 15:04:05.99Z - switch r { - case 'A', 'P': - // Could be AM/PM or could be PST or similar - stateTime = timeWsAMPMMaybe - case '+', '-': - stateTime = timeWsOffset - default: - if unicode.IsLetter(r) { - // 06:20:00 UTC - stateTime = timeWsAlpha + case timeOffset: + // timeOffsetColon + // 15:04:05+07:00 + // 15:04:05-07:00 + if r == ':' { + p.stateTime = timeOffsetColon + } + case timeWs: + // timeAlpha + // 06:20:00 UTC + // timeWsAMPMMaybe + // 05:24:37 PM + // timeWsOffset + // 15:04:05 -0700 + // timeWsOffsetColon + // 15:04:05 -07:00 + // timeWsOffsetColonAlpha + // 00:12:00 +00:00 UTC + // timeWsOffsetAlpha + // 00:12:00 +0000 UTC + // timeZ + // 15:04:05.99Z + switch r { + case 'A', 'P': + // Could be AM/PM or could be PST or similar + p.stateTime = timeWsAMPMMaybe + case '+', '-': + p.stateTime = timeWsOffset + default: + if unicode.IsLetter(r) { + // 06:20:00 UTC + p.stateTime = timeWsAlpha + break iterTimeRunes + } + } + + case timeWsAMPMMaybe: + // timeWsAMPMMaybe + // timeWsAMPM + // 05:24:37 PM + // timeWsAlpha + // 00:12:00 PST + if r == 'M' { + //return parse("2006-01-02 03:04:05 PM", datestr, loc) + p.stateTime = timeWsAMPM + } else { + p.stateTime = timeWsAlpha + } + + case timeWsOffset: + // timeWsOffset + // 15:04:05 -0700 + // timeWsOffsetColon + // 15:04:05 -07:00 + // timeWsOffsetColonAlpha + // 00:12:00 +00:00 UTC + // timeWsOffsetAlpha + // 00:12:00 +0000 UTC + if r == ':' { + p.stateTime = timeWsOffsetColon + } else if unicode.IsLetter(r) { + // 2015-02-18 00:12:00 +0000 UTC + p.stateTime = timeWsOffsetAlpha break iterTimeRunes } - } - case timeWsAMPMMaybe: - // timeWsAMPMMaybe - // timeWsAMPM - // 05:24:37 PM - // timeWsAlpha - // 00:12:00 PST - if r == 'M' { - //return parse("2006-01-02 03:04:05 PM", datestr, loc) - stateTime = timeWsAMPM - } else { - stateTime = timeWsAlpha - } + case timeWsOffsetColon: + // timeWsOffsetColon + // 15:04:05 -07:00 + // timeWsOffsetColonAlpha + // 2015-02-18 00:12:00 +00:00 UTC + if unicode.IsLetter(r) { + // 2015-02-18 00:12:00 +00:00 UTC + p.stateTime = timeWsOffsetColonAlpha + break iterTimeRunes + } - case timeWsOffset: - // timeWsOffset - // 15:04:05 -0700 - // timeWsOffsetColon - // 15:04:05 -07:00 - // timeWsOffsetColonAlpha - // 00:12:00 +00:00 UTC - // timeWsOffsetAlpha - // 00:12:00 +0000 UTC - if r == ':' { - stateTime = timeWsOffsetColon - } else if unicode.IsLetter(r) { - // 2015-02-18 00:12:00 +0000 UTC - stateTime = timeWsOffsetAlpha - break iterTimeRunes - } + case timePeriod: + // 15:04:05.999999999+07:00 + // 15:04:05.999999999-07:00 + // 15:04:05.999999+07:00 + // 15:04:05.999999-07:00 + // 15:04:05.999+07:00 + // 15:04:05.999-07:00 + // timePeriod + // 17:24:37.3186369 + // 00:07:31.945167 + // 18:31:59.257000000 + // 00:00:00.000 + // timePeriodWs + // timePeriodWsOffset + // 00:07:31.945167 +0000 + // 00:00:00.000 +0000 + // timePeriodWsOffsetAlpha + // 00:07:31.945167 +0000 UTC + // 00:00:00.000 +0000 UTC + // timePeriodWsAlpha + // 06:20:00.000 UTC + switch r { + // case '.': + // p.stateTime = timePeriod + // p.seclen = i - p.seci + // p.msi = i + 1 + case ' ': + p.mslen = i - p.msi + p.stateTime = timePeriodWs + case '+', '-': + p.mslen = i - p.msi + p.stateTime = timePeriodOffset + default: + if unicode.IsLetter(r) { + // 06:20:00.000 UTC + p.mslen = i - p.msi + p.stateTime = timePeriodAlpha + } + } + case timePeriodWs: + if unicode.IsLetter(r) { + // 00:07:31.945167 +0000 UTC + // 00:00:00.000 +0000 UTC + p.stateTime = timePeriodWsOffsetAlpha + break iterTimeRunes + } + case timePeriodWsOffset: + // 00:07:31.945167 +0000 + // 00:00:00.000 +0000 + // timePeriodWsOffsetAlpha + // 00:07:31.945167 +0000 UTC + // 00:00:00.000 +0000 UTC + if unicode.IsLetter(r) { + // 00:07:31.945167 +0000 UTC + // 00:00:00.000 +0000 UTC + p.stateTime = timePeriodWsOffsetAlpha + break iterTimeRunes + } + case timeZ: + // timeZ + // 15:04:05.99Z + // With a time-zone at end after Z + // 2006-01-02T15:04:05.999999999Z07:00 + // 2006-01-02T15:04:05Z07:00 + // RFC3339 = "2006-01-02T15:04:05Z07:00" + // RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" + if unicode.IsDigit(r) { + p.stateTime = timeZDigit + } - case timeWsOffsetColon: - // timeWsOffsetColon - // 15:04:05 -07:00 - // timeWsOffsetColonAlpha - // 2015-02-18 00:12:00 +00:00 UTC - if unicode.IsLetter(r) { - // 2015-02-18 00:12:00 +00:00 UTC - stateTime = timeWsOffsetColonAlpha - break iterTimeRunes } + } + switch p.stateTime { case timePeriod: - // 2014-04-26 17:24:37.3186369 - // 2017-01-27 00:07:31.945167 - // 2012-08-03 18:31:59.257000000 - // 2016-03-14 00:00:00.000 - // timePeriodOffset - // 2017-01-27 00:07:31.945167 +0000 - // 2016-03-14 00:00:00.000 +0000 - // timePeriodOffsetAlpha - // 2017-01-27 00:07:31.945167 +0000 UTC - // 2016-03-14 00:00:00.000 +0000 UTC - // timePeriodAlpha - // 2014-12-16 06:20:00.000 UTC - if unicode.IsLetter(r) { - // 2014-12-16 06:20:00.000 UTC - stateTime = timePeriodAlpha - break iterTimeRunes - } else if r == '+' || r == '-' { - stateTime = timePeriodOffset - } - case timePeriodOffset: - // 00:07:31.945167 +0000 - // 00:00:00.000 +0000 - // timePeriodOffsetAlpha - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - if unicode.IsLetter(r) { - // 06:20:00.000 UTC - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - stateTime = timePeriodOffsetAlpha - break iterTimeRunes - } + p.mslen = i - p.msi case timeZ: - if unicode.IsDigit(r) { - stateTime = timeZDigit - } + u.Warnf("wtf? timeZ") + case timeZDigit: + u.Warnf("got Z00:00") + default: + u.Warnf("un-handled statetime: %d for %v", p.stateTime, p.datestr) + } + p.coalesceTime(i) + switch p.stateTime { + case timeOffset: + // 15:04:05+0700 + u.Warnf("not handled timeOffset?") + case timeOffsetColon: + // 15:04:05+07:00 + u.Warnf("timeoffsetcolon ") + p.set(p.offseti, "-07:00") + case timeWsAMPMMaybe: + case timeZ: } } - u.Infof("time %60s marker:%2d 1:%d 2:%d 3:%d timeFormat=%q", datestr, timeMarker, time1Len, time2Len, time3Len, string(dateFormat)) + //u.Infof("%60s %q\n\t%+v", datestr, string(p.format), p) - switch stateDate { + switch p.stateDate { case dateDigit: // unixy timestamps ish // 1499979655583057426 nanoseconds @@ -792,558 +996,137 @@ iterTimeRunes: return t.In(loc), nil } - case dateDigitDash: // starts digit then dash 02- + case dateDigitDashDash: // 2006-01-02 // 2006-1-02 // 2006-1-2 // 2006-01-2 // 2006-01 - for _, layout := range []string{ - "2006-01-02", - "2006-01", - "2006-1-2", - "2006-01-2", - "2006-1-02", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + return p.parse() - case dateDigitDashAlpha: + case dateDigitDashDashAlpha: // 2013-Feb-03 // 2013-Feb-3 - for _, layout := range []string{ - "2006-Jan-02", - "2006-Jan-2", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + p.daylen = i - p.dayi + p.setDay() + return p.parse() + + case dateDigitDashDashTZ: // starts digit then dash 02- then T Then Z + // 2006-01-02T15:04:05.999999999Z + // 2006-01-02T15:04:05.99999999Z + // 2006-01-02T15:04:05.9999999Z + // 2006-01-02T15:04:05.999999Z + // 2006-01-02T15:04:05.99999Z + // 2006-01-02T15:04:05.9999Z + // 2006-01-02T15:04:05.999Z + // 2006-01-02T15:04:05.99Z + // 2009-08-12T22:15Z -- No seconds/milliseconds + return p.parse() + + case dateDigitDashDashWs: // starts digit then dash 02- then whitespace 1 << 2 << 5 + 3 + // 2013-04-01 22:43:22 + // 2013-04-01 22:43 + return p.parse() + + case dateDigitDashDashT: + return p.parse() /* - case dateDigitDashTOffset: - // 2006-01-02T15:04:05+0000 - for _, layout := range []string{ - "2006-01-02T15:04:05-0700", - "2006-01-02T15:04:5-0700", - "2006-01-02T15:4:05-0700", - "2006-01-02T15:4:5-0700", - "2006-1-02T15:04:05-0700", - "2006-1-02T15:4:05-0700", - "2006-1-02T15:04:5-0700", - "2006-1-02T15:4:5-0700", - "2006-01-2T15:04:05-0700", - "2006-01-2T15:04:5-0700", - "2006-01-2T15:4:05-0700", - "2006-01-2T15:4:5-0700", - "2006-1-2T15:04:05-0700", - "2006-1-2T15:04:5-0700", - "2006-1-2T15:4:05-0700", - "2006-1-2T15:4:5-0700", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + case dateDigitDashDashTOffset: + // 2006-01-02T15:04:05+0000 + return p.parse() - case dateDigitDashTOffsetColon: - // With another +/- time-zone at end - // 2006-01-02T15:04:05.999999999+07:00 - // 2006-01-02T15:04:05.999999999-07:00 - // 2006-01-02T15:04:05.999999+07:00 - // 2006-01-02T15:04:05.999999-07:00 - // 2006-01-02T15:04:05.999+07:00 - // 2006-01-02T15:04:05.999-07:00 - // 2006-01-02T15:04:05+07:00 - // 2006-01-02T15:04:05-07:00 - for _, layout := range []string{ - "2006-01-02T15:04:05-07:00", - "2006-01-02T15:04:5-07:00", - "2006-01-02T15:4:05-07:00", - "2006-01-02T15:4:5-07:00", - "2006-1-02T15:04:05-07:00", - "2006-1-02T15:4:05-07:00", - "2006-1-02T15:04:5-07:00", - "2006-1-02T15:4:5-07:00", - "2006-01-2T15:04:05-07:00", - "2006-01-2T15:04:5-07:00", - "2006-01-2T15:4:05-07:00", - "2006-01-2T15:4:5-07:00", - "2006-1-2T15:04:05-07:00", - "2006-1-2T15:04:5-07:00", - "2006-1-2T15:4:05-07:00", - "2006-1-2T15:4:5-07:00", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + case dateDigitDashDashTOffsetColon: + // With another +/- time-zone at end + // 2006-01-02T15:04:05.999999999+07:00 + // 2006-01-02T15:04:05.999999999-07:00 + // 2006-01-02T15:04:05.999999+07:00 + // 2006-01-02T15:04:05.999999-07:00 + // 2006-01-02T15:04:05.999+07:00 + // 2006-01-02T15:04:05.999-07:00 + // 2006-01-02T15:04:05+07:00 + // 2006-01-02T15:04:05-07:00 + return p.parse() - case dateDigitDashT: // starts digit then dash 02- then T - // 2006-01-02T15:04:05.999999 - // 2006-01-02T15:04:05.999999 - for _, layout := range []string{ - "2006-01-02T15:04:05", - "2006-01-02T15:04:5", - "2006-01-02T15:4:05", - "2006-01-02T15:4:5", - "2006-1-02T15:04:05", - "2006-1-02T15:4:05", - "2006-1-02T15:04:5", - "2006-1-02T15:4:5", - "2006-01-2T15:04:05", - "2006-01-2T15:04:5", - "2006-01-2T15:4:05", - "2006-01-2T15:4:5", - "2006-1-2T15:04:05", - "2006-1-2T15:04:5", - "2006-1-2T15:4:05", - "2006-1-2T15:4:5", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + case dateDigitDashDashT: // starts digit then dash 02- then T + // 2006-01-02T15:04:05.999999 + // 2006-01-02T15:04:05.999999 + return p.parse() - case dateDigitDashTZDigit: - // With a time-zone at end after Z - // 2006-01-02T15:04:05.999999999Z07:00 - // 2006-01-02T15:04:05Z07:00 - // RFC3339 = "2006-01-02T15:04:05Z07:00" - // RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" - return time.Time{}, fmt.Errorf("RFC339 Dates may not contain both Z & Offset for %q see https://github.com/golang/go/issues/5294", datestr) + case dateDigitDashDashTZDigit: + // With a time-zone at end after Z + // 2006-01-02T15:04:05.999999999Z07:00 + // 2006-01-02T15:04:05Z07:00 + // RFC3339 = "2006-01-02T15:04:05Z07:00" + // RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" + return time.Time{}, fmt.Errorf("RFC339 Dates may not contain both Z & Offset for %q see https://github.com/golang/go/issues/5294", datestr) - case dateDigitDashTZ: // starts digit then dash 02- then T Then Z - // 2006-01-02T15:04:05.999999999Z - // 2006-01-02T15:04:05.99999999Z - // 2006-01-02T15:04:05.9999999Z - // 2006-01-02T15:04:05.999999Z - // 2006-01-02T15:04:05.99999Z - // 2006-01-02T15:04:05.9999Z - // 2006-01-02T15:04:05.999Z - // 2006-01-02T15:04:05.99Z - // 2009-08-12T22:15Z -- No seconds/milliseconds - for _, layout := range []string{ - "2006-01-02T15:04:05Z", - "2006-01-02T15:04:5Z", - "2006-01-02T15:4:05Z", - "2006-01-02T15:4:5Z", - "2006-01-02T15:4Z", - "2006-01-02T15:04Z", - "2006-1-02T15:04:05Z", - "2006-1-02T15:4:05Z", - "2006-1-02T15:04:5Z", - "2006-1-02T15:4:5Z", - "2006-1-02T15:04Z", - "2006-1-02T15:4Z", - "2006-01-2T15:04:05Z", - "2006-01-2T15:04:5Z", - "2006-01-2T15:4:05Z", - "2006-01-2T15:4:5Z", - "2006-01-2T15:4Z", - "2006-01-2T15:04Z", - "2006-1-2T15:04:05Z", - "2006-1-2T15:04:5Z", - "2006-1-2T15:4:05Z", - "2006-1-2T15:4:5Z", - "2006-1-2T15:04Z", - "2006-1-2T15:4Z", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - case dateDigitDashWs: // starts digit then dash 02- then whitespace 1 << 2 << 5 + 3 - // 2013-04-01 22:43:22 - // 2013-04-01 22:43 - for _, layout := range []string{ - "2006-01-02 15:04:05", - "2006-01-02 15:04:5", - "2006-01-02 15:4:05", - "2006-01-02 15:4:5", - "2006-01-02 15:4", - "2006-01-02 15:04", - "2006-1-02 15:04:05", - "2006-1-02 15:4:05", - "2006-1-02 15:04:5", - "2006-1-02 15:4:5", - "2006-1-02 15:04", - "2006-1-02 15:4", - "2006-01-2 15:04:05", - "2006-01-2 15:04:5", - "2006-01-2 15:4:05", - "2006-01-2 15:4:5", - "2006-01-2 15:4", - "2006-01-2 15:04", - "2006-1-2 15:04:05", - "2006-1-2 15:04:5", - "2006-1-2 15:4:05", - "2006-1-2 15:4:5", - "2006-1-2 15:04", - "2006-1-2 15:4", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - - case dateDigitDashWsWsOffset: + case dateDigitDashDashWsWsOffset: // 2006-01-02 15:04:05 -0700 - for _, layout := range []string{ - "2006-01-02 15:04:05 -0700", - "2006-01-02 15:04:5 -0700", - "2006-01-02 15:4:05 -0700", - "2006-01-02 15:4:5 -0700", - "2006-1-02 15:04:05 -0700", - "2006-1-02 15:4:05 -0700", - "2006-1-02 15:04:5 -0700", - "2006-1-02 15:4:5 -0700", - "2006-01-2 15:04:05 -0700", - "2006-01-2 15:04:5 -0700", - "2006-01-2 15:4:05 -0700", - "2006-01-2 15:4:5 -0700", - "2006-1-2 15:04:05 -0700", - "2006-1-2 15:04:5 -0700", - "2006-1-2 15:4:05 -0700", - "2006-1-2 15:4:5 -0700", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + return p.parse() - case dateDigitDashWsWsOffsetColon: + case dateDigitDashDashWsWsOffsetColon: // 2006-01-02 15:04:05 -07:00 - switch { - case part2Len == 2 && part3Len == 2: - for _, layout := range []string{ - "2006-01-02 15:04:05 -07:00", - "2006-01-02 15:04:5 -07:00", - "2006-01-02 15:4:05 -07:00", - "2006-01-02 15:4:5 -07:00", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - case part2Len == 2 && part3Len == 1: - for _, layout := range []string{ - "2006-01-2 15:04:05 -07:00", - "2006-01-2 15:04:5 -07:00", - "2006-01-2 15:4:05 -07:00", - "2006-01-2 15:4:5 -07:00", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - case part2Len == 1 && part3Len == 2: - for _, layout := range []string{ - "2006-1-02 15:04:05 -07:00", - "2006-1-02 15:4:05 -07:00", - "2006-1-02 15:04:5 -07:00", - "2006-1-02 15:4:5 -07:00", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - case part2Len == 1 && part3Len == 1: - for _, layout := range []string{ - "2006-1-2 15:04:05 -07:00", - "2006-1-2 15:04:5 -07:00", - "2006-1-2 15:4:05 -07:00", - "2006-1-2 15:4:5 -07:00", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - } + return p.parse() - case dateDigitDashWsWsOffsetAlpha: + case dateDigitDashDashWsWsOffsetAlpha: // 2015-02-18 00:12:00 +0000 UTC + return p.parse() - switch { - case part2Len == 2 && part3Len == 2: - for _, layout := range []string{ - "2006-01-02 15:04:05 -0700 MST", - "2006-01-02 15:04:5 -0700 MST", - "2006-01-02 15:4:05 -0700 MST", - "2006-01-02 15:4:5 -0700 MST", - "2006-01-02 15:04:05 +0000 GMT", - "2006-01-02 15:04:5 +0000 GMT", - "2006-01-02 15:4:05 +0000 GMT", - "2006-01-02 15:4:5 +0000 GMT", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - case part2Len == 2 && part3Len == 1: - for _, layout := range []string{ - "2006-01-2 15:04:05 -0700 MST", - "2006-01-2 15:04:5 -0700 MST", - "2006-01-2 15:4:05 -0700 MST", - "2006-01-2 15:4:5 -0700 MST", - "2006-01-2 15:04:05 +0000 GMT", - "2006-01-2 15:04:5 +0000 GMT", - "2006-01-2 15:4:05 +0000 GMT", - "2006-01-2 15:4:5 +0000 GMT", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - case part2Len == 1 && part3Len == 2: - for _, layout := range []string{ - "2006-1-02 15:04:05 -0700 MST", - "2006-1-02 15:4:05 -0700 MST", - "2006-1-02 15:04:5 -0700 MST", - "2006-1-02 15:4:5 -0700 MST", - "2006-1-02 15:04:05 +0000 GMT", - "2006-1-02 15:4:05 +0000 GMT", - "2006-1-02 15:04:5 +0000 GMT", - "2006-1-02 15:4:5 +0000 GMT", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - case part2Len == 1 && part3Len == 1: - for _, layout := range []string{ - "2006-1-2 15:04:05 -0700 MST", - "2006-1-2 15:04:5 -0700 MST", - "2006-1-2 15:4:05 -0700 MST", - "2006-1-2 15:4:5 -0700 MST", - "2006-1-2 15:04:05 +0000 GMT", - "2006-1-2 15:04:5 +0000 GMT", - "2006-1-2 15:4:05 +0000 GMT", - "2006-1-2 15:4:5 +0000 GMT", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - } - - case dateDigitDashWsWsOffsetColonAlpha: + case dateDigitDashDashWsWsOffsetColonAlpha: // 2015-02-18 00:12:00 +00:00 UTC - for _, layout := range []string{ - "2006-01-02 15:04:05 -07:00 UTC", - "2006-01-02 15:04:5 -07:00 UTC", - "2006-01-02 15:4:05 -07:00 UTC", - "2006-01-02 15:4:5 -07:00 UTC", - "2006-1-02 15:04:05 -07:00 UTC", - "2006-1-02 15:4:05 -07:00 UTC", - "2006-1-02 15:04:5 -07:00 UTC", - "2006-1-02 15:4:5 -07:00 UTC", - "2006-01-2 15:04:05 -07:00 UTC", - "2006-01-2 15:04:5 -07:00 UTC", - "2006-01-2 15:4:05 -07:00 UTC", - "2006-01-2 15:4:5 -07:00 UTC", - "2006-1-2 15:04:05 -07:00 UTC", - "2006-1-2 15:04:5 -07:00 UTC", - "2006-1-2 15:4:05 -07:00 UTC", - "2006-1-2 15:4:5 -07:00 UTC", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + return p.parse() - case dateDigitDashWsOffset: + case dateDigitDashDashWsOffset: // 2017-07-19 03:21:51+00:00 - for _, layout := range []string{ - "2006-01-02 15:04:05-07:00", - "2006-01-02 15:04:5-07:00", - "2006-01-02 15:4:05-07:00", - "2006-01-02 15:4:5-07:00", - "2006-1-02 15:04:05-07:00", - "2006-1-02 15:4:05-07:00", - "2006-1-02 15:04:5-07:00", - "2006-1-02 15:4:5-07:00", - "2006-01-2 15:04:05-07:00", - "2006-01-2 15:04:5-07:00", - "2006-01-2 15:4:05-07:00", - "2006-01-2 15:4:5-07:00", - "2006-1-2 15:04:05-07:00", - "2006-1-2 15:04:5-07:00", - "2006-1-2 15:4:05-07:00", - "2006-1-2 15:4:5-07:00", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + return p.parse() - case dateDigitDashWsWsAlpha: + case dateDigitDashDashWsWsAlpha: // 2014-12-16 06:20:00 UTC - for _, layout := range []string{ - "2006-01-02 15:04:05 UTC", - "2006-01-02 15:04:5 UTC", - "2006-01-02 15:4:05 UTC", - "2006-01-02 15:4:5 UTC", - "2006-1-02 15:04:05 UTC", - "2006-1-02 15:4:05 UTC", - "2006-1-02 15:04:5 UTC", - "2006-1-02 15:4:5 UTC", - "2006-01-2 15:04:05 UTC", - "2006-01-2 15:04:5 UTC", - "2006-01-2 15:4:05 UTC", - "2006-01-2 15:4:5 UTC", - "2006-1-2 15:04:05 UTC", - "2006-1-2 15:04:5 UTC", - "2006-1-2 15:4:05 UTC", - "2006-1-2 15:4:5 UTC", - "2006-01-02 15:04:05 GMT", - "2006-01-02 15:04:5 GMT", - "2006-01-02 15:4:05 GMT", - "2006-01-02 15:4:5 GMT", - "2006-1-02 15:04:05 GMT", - "2006-1-02 15:4:05 GMT", - "2006-1-02 15:04:5 GMT", - "2006-1-02 15:4:5 GMT", - "2006-01-2 15:04:05 GMT", - "2006-01-2 15:04:5 GMT", - "2006-01-2 15:4:05 GMT", - "2006-01-2 15:4:5 GMT", - "2006-1-2 15:04:05 GMT", - "2006-1-2 15:04:5 GMT", - "2006-1-2 15:4:05 GMT", - "2006-1-2 15:4:5 GMT", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + return p.parse() - if len(datestr) > len("2006-01-02 03:04:05") { - t, err := parse("2006-01-02 03:04:05", datestr[:len("2006-01-02 03:04:05")], loc) - if err == nil { - return t, nil - } - } + // if len(datestr) > len("2006-01-02 03:04:05") { + // t, err := parse("2006-01-02 03:04:05", datestr[:len("2006-01-02 03:04:05")], loc) + // if err == nil { + // return t, nil + // } + // } - case dateDigitDashWsPeriod: + case dateDigitDashDashWsPeriod: // 2012-08-03 18:31:59.257000000 // 2014-04-26 17:24:37.3186369 // 2017-01-27 00:07:31.945167 // 2016-03-14 00:00:00.000 - for _, layout := range []string{ - "2006-01-02 15:04:05", - "2006-01-02 15:04:5", - "2006-01-02 15:4:05", - "2006-01-02 15:4:5", - "2006-1-02 15:04:05", - "2006-1-02 15:4:05", - "2006-1-02 15:04:5", - "2006-1-02 15:4:5", - "2006-01-2 15:04:05", - "2006-01-2 15:04:5", - "2006-01-2 15:4:05", - "2006-01-2 15:4:5", - "2006-1-2 15:04:05", - "2006-1-2 15:04:5", - "2006-1-2 15:4:05", - "2006-1-2 15:4:5", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + return p.parse() - case dateDigitDashWsPeriodAlpha: + + case dateDigitDashDashWsPeriodAlpha: // 2012-08-03 18:31:59.257000000 UTC // 2014-04-26 17:24:37.3186369 UTC // 2017-01-27 00:07:31.945167 UTC // 2016-03-14 00:00:00.000 UTC - for _, layout := range []string{ - "2006-01-02 15:04:05 UTC", - "2006-01-02 15:04:5 UTC", - "2006-01-02 15:4:05 UTC", - "2006-01-02 15:4:5 UTC", - "2006-1-02 15:04:05 UTC", - "2006-1-02 15:4:05 UTC", - "2006-1-02 15:04:5 UTC", - "2006-1-02 15:4:5 UTC", - "2006-01-2 15:04:05 UTC", - "2006-01-2 15:04:5 UTC", - "2006-01-2 15:4:05 UTC", - "2006-01-2 15:4:5 UTC", - "2006-1-2 15:04:05 UTC", - "2006-1-2 15:04:5 UTC", - "2006-1-2 15:4:05 UTC", - "2006-1-2 15:4:5 UTC", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + return p.parse() - case dateDigitDashWsPeriodOffset: + case dateDigitDashDashWsPeriodOffset: // 2012-08-03 18:31:59.257000000 +0000 // 2014-04-26 17:24:37.3186369 +0000 // 2017-01-27 00:07:31.945167 +0000 // 2016-03-14 00:00:00.000 +0000 - for _, layout := range []string{ - "2006-01-02 15:04:05 -0700", - "2006-01-02 15:04:5 -0700", - "2006-01-02 15:4:05 -0700", - "2006-01-02 15:4:5 -0700", - "2006-1-02 15:04:05 -0700", - "2006-1-02 15:4:05 -0700", - "2006-1-02 15:04:5 -0700", - "2006-1-02 15:4:5 -0700", - "2006-01-2 15:04:05 -0700", - "2006-01-2 15:04:5 -0700", - "2006-01-2 15:4:05 -0700", - "2006-01-2 15:4:5 -0700", - "2006-1-2 15:04:05 -0700", - "2006-1-2 15:04:5 -0700", - "2006-1-2 15:4:05 -0700", - "2006-1-2 15:4:5 -0700", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - case dateDigitDashWsPeriodOffsetAlpha: - // 2012-08-03 18:31:59.257000000 +0000 UTC - // 2014-04-26 17:24:37.3186369 +0000 UTC - // 2017-01-27 00:07:31.945167 +0000 UTC - // 2016-03-14 00:00:00.000 +0000 UTC - for _, layout := range []string{ - "2006-01-02 15:04:05 -0700 UTC", - "2006-01-02 15:04:5 -0700 UTC", - "2006-01-02 15:4:05 -0700 UTC", - "2006-01-02 15:4:5 -0700 UTC", - "2006-1-02 15:04:05 -0700 UTC", - "2006-1-02 15:4:05 -0700 UTC", - "2006-1-02 15:04:5 -0700 UTC", - "2006-1-02 15:4:5 -0700 UTC", - "2006-01-2 15:04:05 -0700 UTC", - "2006-01-2 15:04:5 -0700 UTC", - "2006-01-2 15:4:05 -0700 UTC", - "2006-01-2 15:4:5 -0700 UTC", - "2006-1-2 15:04:05 -0700 UTC", - "2006-1-2 15:04:5 -0700 UTC", - "2006-1-2 15:4:05 -0700 UTC", - "2006-1-2 15:4:5 -0700 UTC", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + return p.parse() + case dateDigitDashDashWsPeriodOffsetAlpha: + // 2012-08-03 18:31:59.257000000 +0000 UTC + // 2014-04-26 17:24:37.3186369 +0000 UTC + // 2017-01-27 00:07:31.945167 +0000 UTC + // 2016-03-14 00:00:00.000 +0000 UTC + return p.parse() */ + case dateDigitDotDot: switch { case len(datestr) == len("01.02.2006"): return parse("01.02.2006", datestr, loc) - case len(datestr)-part2Len == 3: + case len(datestr)-p.part2Len == 3: for _, layout := range []string{"01.02.06", "1.02.06", "01.2.06", "1.2.06"} { if t, err := parse(layout, datestr, loc); err == nil { return t, nil @@ -1360,7 +1143,7 @@ iterTimeRunes: case dateDigitWs: // 18 January 2018 // 8 January 2018 - if part1Len == 1 { + if p.part1Len == 1 { return parse("2 January 2006", datestr, loc) } return parse("02 January 2006", datestr, loc) @@ -1368,7 +1151,7 @@ iterTimeRunes: case dateDigitWsMoShortColon: // 2 Jan 2018 23:59 // 02 Jan 2018 23:59 - if part1Len == 1 { + if p.part1Len == 1 { for _, layout := range []string{ "2 Jan 2006 15:04", "2 Jan 2006 15:4", @@ -1389,7 +1172,7 @@ iterTimeRunes: } case dateDigitWsMoShortColonColon: // 02 Jan 2018 23:59:45 - if part1Len == 1 { + if p.part1Len == 1 { for _, layout := range []string{ "2 Jan 2006 15:04:05", "2 Jan 2006 15:04:5", @@ -1479,7 +1262,7 @@ iterTimeRunes: // 10/13/2014 // 01/02/2006 // 2014/10/13 - if part1Len == 4 { + if p.part1Len == 4 { if len(datestr) == len("2006/01/02") { return parse("2006/01/02", datestr, loc) } @@ -1497,7 +1280,7 @@ iterTimeRunes: // 2014/4/8 22:05 // 2014/04/08 22:05 - if part1Len == 4 { + if p.part1Len == 4 { for _, layout := range []string{"2006/01/02 15:04", "2006/1/2 15:04", "2006/01/2 15:04", "2006/1/02 15:04", "2006/01/02 15:4", "2006/1/2 15:4", "2006/01/2 15:4", "2006/1/02 15:4"} { if t, err := parse(layout, datestr, loc); err == nil { return t, nil @@ -1518,7 +1301,7 @@ iterTimeRunes: // 2014/4/8 22:05 PM // 2014/04/08 22:05 PM - if part1Len == 4 { + if p.part1Len == 4 { for _, layout := range []string{"2006/01/02 03:04 PM", "2006/01/2 03:04 PM", "2006/1/02 03:04 PM", "2006/1/2 03:04 PM", "2006/01/02 3:04 PM", "2006/01/2 3:04 PM", "2006/1/02 3:04 PM", "2006/1/2 3:04 PM", "2006/01/02 3:4 PM", "2006/01/2 3:4 PM", "2006/1/02 3:4 PM", "2006/1/2 3:4 PM", "2006/01/02 3:4 PM", "2006/01/2 3:4 PM", "2006/1/02 3:4 PM", "2006/1/2 3:4 PM"} { @@ -1547,7 +1330,7 @@ iterTimeRunes: // 03/1/2012 10:11:59 // 3/01/2012 10:11:59 // 4/8/14 22:05 - if part1Len == 4 { + if p.part1Len == 4 { for _, layout := range []string{"2006/01/02 15:04:05", "2006/1/02 15:04:05", "2006/01/2 15:04:05", "2006/1/2 15:04:05", "2006/01/02 15:04:5", "2006/1/02 15:04:5", "2006/01/2 15:04:5", "2006/1/2 15:04:5", "2006/01/02 15:4:05", "2006/1/02 15:4:05", "2006/01/2 15:4:05", "2006/1/2 15:4:05", @@ -1574,7 +1357,7 @@ iterTimeRunes: // 03/1/2012 10:11:59 PM // 3/01/2012 10:11:59 PM - if part1Len == 4 { + if p.part1Len == 4 { for _, layout := range []string{"2006/01/02 03:04:05 PM", "2006/1/02 03:04:05 PM", "2006/01/2 03:04:05 PM", "2006/1/2 03:04:05 PM", "2006/01/02 03:4:5 PM", "2006/1/02 03:4:5 PM", "2006/01/2 03:4:5 PM", "2006/1/2 03:4:5 PM", "2006/01/02 03:4:05 PM", "2006/1/02 03:4:05 PM", "2006/01/2 03:4:05 PM", "2006/1/2 03:4:05 PM", @@ -1694,11 +1477,13 @@ iterTimeRunes: case dateWeekdayAbbrevCommaOffsetZone: // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) - if part3Len == 1 { + if p.part3Len == 1 { return parse("Mon, 2 Jan 2006 15:04:05 -0700 (MST)", datestr, loc) } return parse("Mon, _2 Jan 2006 15:04:05 -0700 (MST)", datestr, loc) } + u.Warnf("no format for %d %d %s", p.stateDate, p.stateTime, p.datestr) + return time.Time{}, fmt.Errorf("Could not find date format for %s", datestr) } From 20cd681f46218368bd868b36518243edaad285ed Mon Sep 17 00:00:00 2001 From: Aaron Raddon Date: Tue, 27 Feb 2018 20:05:14 -0800 Subject: [PATCH 03/10] time-parse fixes --- parseany.go | 570 +++++++++++++++++++++-------------------------- parseany_test.go | 25 +++ 2 files changed, 281 insertions(+), 314 deletions(-) diff --git a/parseany.go b/parseany.go index 4d15e37..ef7c602 100644 --- a/parseany.go +++ b/parseany.go @@ -28,7 +28,6 @@ const ( dateDigitDashDash dateDigitDashDashWs dateDigitDashDashT - dateDigitDashDashTZ dateDigitDashDashAlpha dateDigitDot dateDigitDotDot @@ -41,10 +40,7 @@ const ( dateDigitChineseYear dateDigitChineseYearWs dateDigitWs - dateDigitWsMoShort - dateDigitWsMoShortColon - dateDigitWsMoShortColonColon - dateDigitWsMoShortComma + dateDigitWsMoYear dateAlpha dateAlphaWS dateAlphaWSDigit @@ -72,7 +68,7 @@ const ( timeWs timeWsAMPMMaybe timeWsAMPM - timeWsOffset + timeWsOffset // 5 timeWsAlpha timeWsOffsetAlpha timeWsOffsetColonAlpha @@ -81,11 +77,13 @@ const ( timeOffsetColon timeAlpha timePeriod - timePeriodAlpha timePeriodOffset + timePeriodOffsetColon // 15 timePeriodWs + timePeriodWsAlpha timePeriodWsOffset - timePeriodWsOffsetAlpha // 18 + timePeriodWsOffsetWs + timePeriodWsOffsetWsAlpha // 20 timeZ timeZDigit ) @@ -177,13 +175,16 @@ func newParser(dateStr string, loc *time.Location) parser { stateDate: dateStart, stateTime: timeIgnore, datestr: dateStr, + loc: loc, } p.format = []byte(dateStr) return p } func (p parser) set(start int, val string) { + if start < 0 { + return + } if len(p.format) < start+len(val) { - u.Warnf("invalid format %d: %v", start, val) return } for i, r := range val { @@ -191,8 +192,8 @@ func (p parser) set(start int, val string) { } } func (p parser) setMonth() { - if p.moi == 0 { - u.Warnf("not set %+v", p) + if p.moi <= 0 { + return } if p.molen == 2 { p.set(p.moi, "01") @@ -200,9 +201,19 @@ func (p parser) setMonth() { p.set(p.moi, "1") } } +func (p parser) monthConvert(start, end int, mo string) { + if len(p.format) <= end { + return + } + for i := start; i < end; i++ { + p.format[i] = ' ' + } + p.set(start, mo) +} + func (p parser) setDay() { - if p.dayi == 0 { - u.Warnf("not set %+v", p) + if p.dayi < 0 { + return } if p.daylen == 2 { p.set(p.dayi, "02") @@ -210,13 +221,34 @@ func (p parser) setDay() { p.set(p.dayi, "2") } } + +func (p parser) coalesceDate(end int) { + if p.yeari > 0 { + if p.yearlen == 0 { + p.yearlen = end - p.yeari + } + if p.yearlen == 2 { + p.set(p.yeari, "06") + } else if p.yearlen == 4 { + p.set(p.yeari, "2006") + } + } + if p.moi > 0 && p.molen == 0 { + p.molen = end - p.moi + p.setMonth() + } + if p.dayi > 0 && p.daylen == 0 { + p.daylen = end - p.dayi + p.setDay() + } +} + func (p parser) coalesceTime(end int) { // 03:04:05 // 15:04:05 // 3:04:05 // 3:4:5 // 15:04:05.00 - u.Infof("coalesceTime: %+v", p) if p.houri > 0 { if p.hourlen == 2 { p.set(p.houri, "15") @@ -237,7 +269,7 @@ func (p parser) coalesceTime(end int) { if p.seci > 0 { if p.seclen == 0 { p.seclen = end - p.seci - u.Infof("fixing seconds p.seci=%d seclen=%d end=%d", p.seci, p.seclen, end) + //u.Infof("fixing seconds p.seci=%d seclen=%d end=%d", p.seci, p.seclen, end) } if p.seclen == 2 { p.set(p.seci, "05") @@ -249,17 +281,17 @@ func (p parser) coalesceTime(end int) { if p.msi > 0 { if p.mslen == 0 { p.mslen = end - p.msi - u.Warnf("set mslen??? %v", p.datestr) + //u.Warnf("set mslen??? %v", p.datestr) } for i := 0; i < p.mslen; i++ { p.format[p.msi+i] = '0' } } - - u.Debugf("coalesce %+v", p) + //u.Debugf("coalesce %+v", p) } + func (p parser) parse() (time.Time, error) { - u.Debugf("parse() %50s AS %50s", p.datestr, p.format) + u.Debugf("parse() loc=%v %50s AS %50s", p.loc.String(), p.datestr, p.format) if p.loc == nil { return time.Parse(string(p.format), p.datestr) } @@ -307,9 +339,21 @@ iterRunes: case '/': p.stateDate = dateDigitSlash case '.': + // 3.31.2014 + p.moi = 0 + p.molen = i + p.dayi = i + 1 p.stateDate = dateDigitDot case ' ': + // 18 January 2018 + // 8 January 2018 + // 02 Jan 2018 23:59 + // 02 Jan 2018 23:59:34 + // 12 Feb 2006, 19:17 + // 12 Feb 2006, 19:17:22 p.stateDate = dateDigitWs + p.dayi = 0 + p.daylen = i case '年': // Chinese Year p.stateDate = dateDigitChineseYear @@ -320,6 +364,7 @@ iterRunes: p.part1Len = i case dateDigitDash: + // 2006-01 // 2006-01-02 // dateDigitDashDashT // 2006-01-02T15:04:05Z07:00 @@ -387,6 +432,7 @@ iterRunes: // 13-Feb-03 switch r { case '-': + p.molen = i - p.moi p.set(p.moi, "Jan") p.dayi = i + 1 } @@ -452,42 +498,43 @@ iterRunes: case dateDigitWs: // 18 January 2018 // 8 January 2018 - //dateDigitWsMoShort - // 02 Jan 2018 23:59 - // 02 Jan 2018 23:59:34 - // dateDigitWsMoShortComma - // 12 Feb 2006, 19:17 - // 12 Feb 2006, 19:17:22 - if r == ' ' { - if i <= p.part1Len+len(" Feb") { - p.stateDate = dateDigitWsMoShort - } else { - break iterRunes - } - } - case dateDigitWsMoShort: - // 18 January 2018 - // 8 January 2018 - // dateDigitWsMoShort - // dateDigitWsMoShortColon - // 02 Jan 2018 23:59 - // dateDigitWsMoShortComma - // 12 Feb 2006, 19:17 - // 12 Feb 2006, 19:17:22 + // 02 Jan 2018 23:59 + // 02 Jan 2018 23:59:34 + // 12 Feb 2006, 19:17 + // 12 Feb 2006, 19:17:22 switch r { - case ':': - p.stateDate = dateDigitWsMoShortColon - case ',': - p.stateDate = dateDigitWsMoShortComma - } - case dateDigitWsMoShortColon: - // 02 Jan 2018 23:59 - // dateDigitWsMoShortColonColon - // 02 Jan 2018 23:59:45 + case ' ': + u.Infof("part1=%d i=%d", p.part1Len, i) + p.yeari = i + 1 + p.yearlen = 4 + p.dayi = 0 + p.daylen = p.part1Len + p.setDay() + p.stateTime = timeStart + if i <= len("12 Feb") { + p.moi = p.daylen + 1 + p.molen = 3 + p.set(p.moi, "Jan") + u.Infof("set day dayi=%d len=%d", p.dayi, p.daylen) + } else { + u.Warnf("unhandled long month") + p.monthConvert(p.daylen+1, i, "Jan") + } + p.stateDate = dateDigitWsMoYear + } + + case dateDigitWsMoYear: + u.Debugf("dateDigitWsMoYear ") + // 02 Jan 2018 23:59 + // 02 Jan 2018 23:59:34 + // 12 Feb 2006, 19:17 + // 12 Feb 2006, 19:17:22 switch r { - case ':': - p.stateDate = dateDigitWsMoShortColonColon + case ',': + i++ + break iterRunes + case ' ': break iterRunes } @@ -503,8 +550,9 @@ iterRunes: case dateDigitDot: // 3.31.2014 if r == '.' { + p.daylen = i - p.dayi + p.yeari = i + 1 p.stateDate = dateDigitDotDot - p.part2Len = i } case dateDigitDotDot: // iterate all the way through @@ -685,20 +733,18 @@ iterRunes: } } + p.coalesceDate(i) + if p.stateTime == timeStart { // increment first one, since the i++ occurs at end of loop i++ iterTimeRunes: for ; i < len(datestr); i++ { - r, bytesConsumed := utf8.DecodeRuneInString(datestr[i:]) - if bytesConsumed > 1 { - i += (bytesConsumed - 1) - } + r := rune(datestr[i]) + u.Debugf("i=%d r=%s timeState=%d", i, string(r), p.stateTime) switch p.stateTime { - case timeIgnore: - // not used case timeStart: // 22:43:22 // timeComma @@ -711,21 +757,25 @@ iterRunes: // 15:04:05 -07:00 // timeOffset // 03:21:51+00:00 + // 19:55:00+0100 // timePeriod // 17:24:37.3186369 // 00:07:31.945167 // 18:31:59.257000000 // 00:00:00.000 - // 15:04:05.999-07:00 // timePeriodOffset - // 00:07:31.945167 +0000 - // 00:00:00.000 +0000 - // timePeriodOffsetAlpha + // 19:55:00.799+0100 + // timePeriodOffsetColon + // 15:04:05.999-07:00 + // timePeriodWs + // timePeriodWsOffset + // 00:07:31.945167 +0000 + // 00:00:00.000 +0000 + // timePeriodWsOffsetAlpha // 00:07:31.945167 +0000 UTC // 00:00:00.000 +0000 UTC - // timePeriodAlpha - // 06:20:00.000 UTC - + // timePeriodWsAlpha + // 06:20:00.000 UTC if p.houri == 0 { p.houri = i } @@ -755,16 +805,13 @@ iterRunes: p.stateTime = timeZ if p.seci == 0 { p.minlen = i - p.mini - u.Infof("mini:%d minlen:%d", p.mini, p.minlen) } else { p.seclen = i - p.seci - u.Infof("seci:%d seclen:%d", p.seci, p.seclen) } case ' ': p.stateTime = timeWs p.seclen = i - p.seci case ':': - u.Infof("sss %s for %v", string(r), datestr) if p.mini == 0 { p.mini = i + 1 p.hourlen = i - p.houri @@ -775,9 +822,10 @@ iterRunes: } case timeOffset: + // 19:55:00+0100 // timeOffsetColon - // 15:04:05+07:00 - // 15:04:05-07:00 + // 15:04:05+07:00 + // 15:04:05-07:00 if r == ':' { p.stateTime = timeOffsetColon } @@ -801,6 +849,7 @@ iterRunes: // Could be AM/PM or could be PST or similar p.stateTime = timeWsAMPMMaybe case '+', '-': + p.offseti = i p.stateTime = timeWsOffset default: if unicode.IsLetter(r) { @@ -819,6 +868,7 @@ iterRunes: if r == 'M' { //return parse("2006-01-02 03:04:05 PM", datestr, loc) p.stateTime = timeWsAMPM + p.set(i-1, "PM") } else { p.stateTime = timeWsAlpha } @@ -832,12 +882,18 @@ iterRunes: // 00:12:00 +00:00 UTC // timeWsOffsetAlpha // 00:12:00 +0000 UTC - if r == ':' { + switch r { + case ':': p.stateTime = timeWsOffsetColon - } else if unicode.IsLetter(r) { - // 2015-02-18 00:12:00 +0000 UTC - p.stateTime = timeWsOffsetAlpha - break iterTimeRunes + case ' ': + p.set(p.offseti, "-0700") + p.stateTime = timeWsOffset + default: + if unicode.IsLetter(r) { + // 00:12:00 +0000 UTC + p.stateTime = timeWsOffsetAlpha + break iterTimeRunes + } } case timeWsOffsetColon: @@ -859,56 +915,111 @@ iterRunes: // 15:04:05.999+07:00 // 15:04:05.999-07:00 // timePeriod - // 17:24:37.3186369 - // 00:07:31.945167 - // 18:31:59.257000000 - // 00:00:00.000 - // timePeriodWs - // timePeriodWsOffset - // 00:07:31.945167 +0000 - // 00:00:00.000 +0000 - // timePeriodWsOffsetAlpha - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - // timePeriodWsAlpha - // 06:20:00.000 UTC + // 17:24:37.3186369 + // 00:07:31.945167 + // 18:31:59.257000000 + // 00:00:00.000 + // timePeriodOffset + // 19:55:00.799+0100 + // timePeriodOffsetColon + // 15:04:05.999-07:00 + // timePeriodWs + // timePeriodWsOffset + // 00:07:31.945167 +0000 + // 00:00:00.000 +0000 + // timePeriodWsOffsetAlpha + // 00:07:31.945167 +0000 UTC + // 00:00:00.000 +0000 UTC + // timePeriodWsAlpha + // 06:20:00.000 UTC switch r { - // case '.': - // p.stateTime = timePeriod - // p.seclen = i - p.seci - // p.msi = i + 1 case ' ': p.mslen = i - p.msi p.stateTime = timePeriodWs case '+', '-': + // This really shouldn't happen p.mslen = i - p.msi + p.offseti = i p.stateTime = timePeriodOffset default: if unicode.IsLetter(r) { // 06:20:00.000 UTC p.mslen = i - p.msi - p.stateTime = timePeriodAlpha + p.stateTime = timePeriodWsAlpha } } + case timePeriodOffset: + // timePeriodOffset + // 19:55:00.799+0100 + // timePeriodOffsetColon + // 15:04:05.999-07:00 + switch r { + case ':': + p.stateTime = timePeriodOffsetColon + default: + if unicode.IsLetter(r) { + // 00:07:31.945167 +0000 UTC + // 00:00:00.000 +0000 UTC + p.stateTime = timePeriodWsOffsetWsAlpha + break iterTimeRunes + } + } + case timePeriodOffsetColon: + // timePeriodOffset + // timePeriodOffsetColon + // 15:04:05.999-07:00 + case timePeriodWs: - if unicode.IsLetter(r) { - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - p.stateTime = timePeriodWsOffsetAlpha - break iterTimeRunes + // timePeriodWs + // timePeriodWsOffset + // 00:07:31.945167 +0000 + // 00:00:00.000 +0000 + // timePeriodWsOffsetAlpha + // 00:07:31.945167 +0000 UTC + // 00:00:00.000 +0000 UTC + // timePeriodWsAlpha + // 06:20:00.000 UTC + if p.offseti == 0 { + p.offseti = i } + switch r { + case '+', '-': + p.mslen = i - p.msi - 1 + p.stateTime = timePeriodWsOffset + default: + if unicode.IsLetter(r) { + // 00:07:31.945167 +0000 UTC + // 00:00:00.000 +0000 UTC + p.stateTime = timePeriodWsOffsetWsAlpha + break iterTimeRunes + } + } + case timePeriodWsOffset: - // 00:07:31.945167 +0000 - // 00:00:00.000 +0000 - // timePeriodWsOffsetAlpha - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - if unicode.IsLetter(r) { - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - p.stateTime = timePeriodWsOffsetAlpha - break iterTimeRunes + + // timePeriodWs + // timePeriodWsOffset + // 00:07:31.945167 +0000 + // 00:00:00.000 +0000 + // timePeriodWsOffsetAlpha + // 00:07:31.945167 +0000 UTC + // 00:00:00.000 +0000 UTC + // timePeriodWsAlpha + // 06:20:00.000 UTC + switch r { + case ' ': + p.set(p.offseti, "-0700") + case ':': + u.Errorf("timePeriodWsOffset UNHANDLED COLON") + default: + if unicode.IsLetter(r) { + // 00:07:31.945167 +0000 UTC + // 00:00:00.000 +0000 UTC + p.stateTime = timePeriodWsOffsetWsAlpha + break iterTimeRunes + } } + case timeZ: // timeZ // 15:04:05.99Z @@ -927,26 +1038,32 @@ iterRunes: switch p.stateTime { case timePeriod: p.mslen = i - p.msi - case timeZ: - u.Warnf("wtf? timeZ") - case timeZDigit: - u.Warnf("got Z00:00") + case timeOffset: + // 19:55:00+0100 + p.set(p.offseti, "-0700") + case timeOffsetColon: + // 15:04:05+07:00 + p.set(p.offseti, "-07:00") + // case timeZ: + // u.Warnf("wtf? timeZ") + // case timeZDigit: + // u.Warnf("got timeZDigit Z00:00") + case timePeriodOffset: + // 19:55:00.799+0100 + p.set(p.offseti, "-0700") + case timePeriodOffsetColon: + p.set(p.offseti, "-07:00") + case timePeriodWsOffset: + p.set(p.offseti, "-0700") + // case timePeriodWsOffsetWsAlpha: + // u.Warnf("timePeriodWsOffsetAlpha") + // case timeWsOffsetAlpha: + // u.Warnf("timeWsOffsetAlpha offseti=%d", p.offseti) default: - u.Warnf("un-handled statetime: %d for %v", p.stateTime, p.datestr) + //u.Warnf("un-handled statetime: %d for %v", p.stateTime, p.datestr) } p.coalesceTime(i) - switch p.stateTime { - case timeOffset: - // 15:04:05+0700 - u.Warnf("not handled timeOffset?") - case timeOffsetColon: - // 15:04:05+07:00 - u.Warnf("timeoffsetcolon ") - p.set(p.offseti, "-07:00") - case timeWsAMPMMaybe: - case timeZ: - } } //u.Infof("%60s %q\n\t%+v", datestr, string(p.format), p) @@ -996,12 +1113,15 @@ iterRunes: return t.In(loc), nil } + case dateDigitDash: + // 2006-01 + return p.parse() + case dateDigitDashDash: // 2006-01-02 // 2006-1-02 // 2006-1-2 // 2006-01-2 - // 2006-01 return p.parse() case dateDigitDashDashAlpha: @@ -1011,18 +1131,6 @@ iterRunes: p.setDay() return p.parse() - case dateDigitDashDashTZ: // starts digit then dash 02- then T Then Z - // 2006-01-02T15:04:05.999999999Z - // 2006-01-02T15:04:05.99999999Z - // 2006-01-02T15:04:05.9999999Z - // 2006-01-02T15:04:05.999999Z - // 2006-01-02T15:04:05.99999Z - // 2006-01-02T15:04:05.9999Z - // 2006-01-02T15:04:05.999Z - // 2006-01-02T15:04:05.99Z - // 2009-08-12T22:15Z -- No seconds/milliseconds - return p.parse() - case dateDigitDashDashWs: // starts digit then dash 02- then whitespace 1 << 2 << 5 + 3 // 2013-04-01 22:43:22 // 2013-04-01 22:43 @@ -1031,191 +1139,25 @@ iterRunes: case dateDigitDashDashT: return p.parse() - /* - case dateDigitDashDashTOffset: - // 2006-01-02T15:04:05+0000 - return p.parse() - - case dateDigitDashDashTOffsetColon: - // With another +/- time-zone at end - // 2006-01-02T15:04:05.999999999+07:00 - // 2006-01-02T15:04:05.999999999-07:00 - // 2006-01-02T15:04:05.999999+07:00 - // 2006-01-02T15:04:05.999999-07:00 - // 2006-01-02T15:04:05.999+07:00 - // 2006-01-02T15:04:05.999-07:00 - // 2006-01-02T15:04:05+07:00 - // 2006-01-02T15:04:05-07:00 - return p.parse() - - case dateDigitDashDashT: // starts digit then dash 02- then T - // 2006-01-02T15:04:05.999999 - // 2006-01-02T15:04:05.999999 - return p.parse() - - case dateDigitDashDashTZDigit: - // With a time-zone at end after Z - // 2006-01-02T15:04:05.999999999Z07:00 - // 2006-01-02T15:04:05Z07:00 - // RFC3339 = "2006-01-02T15:04:05Z07:00" - // RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" - return time.Time{}, fmt.Errorf("RFC339 Dates may not contain both Z & Offset for %q see https://github.com/golang/go/issues/5294", datestr) - - - case dateDigitDashDashWsWsOffset: - // 2006-01-02 15:04:05 -0700 - return p.parse() - - case dateDigitDashDashWsWsOffsetColon: - // 2006-01-02 15:04:05 -07:00 - return p.parse() - - case dateDigitDashDashWsWsOffsetAlpha: - // 2015-02-18 00:12:00 +0000 UTC - return p.parse() - - case dateDigitDashDashWsWsOffsetColonAlpha: - // 2015-02-18 00:12:00 +00:00 UTC - return p.parse() - - case dateDigitDashDashWsOffset: - // 2017-07-19 03:21:51+00:00 - return p.parse() - - case dateDigitDashDashWsWsAlpha: - // 2014-12-16 06:20:00 UTC - return p.parse() - - // if len(datestr) > len("2006-01-02 03:04:05") { - // t, err := parse("2006-01-02 03:04:05", datestr[:len("2006-01-02 03:04:05")], loc) - // if err == nil { - // return t, nil - // } - // } - - case dateDigitDashDashWsPeriod: - // 2012-08-03 18:31:59.257000000 - // 2014-04-26 17:24:37.3186369 - // 2017-01-27 00:07:31.945167 - // 2016-03-14 00:00:00.000 - return p.parse() - - - case dateDigitDashDashWsPeriodAlpha: - // 2012-08-03 18:31:59.257000000 UTC - // 2014-04-26 17:24:37.3186369 UTC - // 2017-01-27 00:07:31.945167 UTC - // 2016-03-14 00:00:00.000 UTC - return p.parse() - - case dateDigitDashDashWsPeriodOffset: - // 2012-08-03 18:31:59.257000000 +0000 - // 2014-04-26 17:24:37.3186369 +0000 - // 2017-01-27 00:07:31.945167 +0000 - // 2016-03-14 00:00:00.000 +0000 - return p.parse() - case dateDigitDashDashWsPeriodOffsetAlpha: - // 2012-08-03 18:31:59.257000000 +0000 UTC - // 2014-04-26 17:24:37.3186369 +0000 UTC - // 2017-01-27 00:07:31.945167 +0000 UTC - // 2016-03-14 00:00:00.000 +0000 UTC - return p.parse() - */ - case dateDigitDotDot: - switch { - case len(datestr) == len("01.02.2006"): - return parse("01.02.2006", datestr, loc) - case len(datestr)-p.part2Len == 3: - for _, layout := range []string{"01.02.06", "1.02.06", "01.2.06", "1.2.06"} { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - default: - for _, layout := range []string{"1.02.2006", "01.2.2006", "1.2.2006"} { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - } + // 03.31.1981 + // 3.2.1981 + // 3.2.81 + p.yearlen = i - p.yeari + return p.parse() case dateDigitWs: // 18 January 2018 // 8 January 2018 - if p.part1Len == 1 { - return parse("2 January 2006", datestr, loc) - } - return parse("02 January 2006", datestr, loc) - // 02 Jan 2018 23:59 - case dateDigitWsMoShortColon: + return p.parse() + + case dateDigitWsMoYear: // 2 Jan 2018 23:59 // 02 Jan 2018 23:59 - if p.part1Len == 1 { - for _, layout := range []string{ - "2 Jan 2006 15:04", - "2 Jan 2006 15:4", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - } - - for _, layout := range []string{ - "02 Jan 2006 15:04", - "02 Jan 2006 15:4", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - case dateDigitWsMoShortColonColon: // 02 Jan 2018 23:59:45 - if p.part1Len == 1 { - for _, layout := range []string{ - "2 Jan 2006 15:04:05", - "2 Jan 2006 15:04:5", - "2 Jan 2006 15:4:5", - "2 Jan 2006 15:4:05", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - } - for _, layout := range []string{ - "2 Jan 2006 15:04:05", - "2 Jan 2006 15:04:5", - "2 Jan 2006 15:4:5", - "2 Jan 2006 15:4:05", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - - case dateDigitWsMoShortComma: // 12 Feb 2006, 19:17 // 12 Feb 2006, 19:17:22 - for _, layout := range []string{ - "02 Jan 2006, 15:04", - "02 Jan 2006, 15:4", - "2 Jan 2006, 15:04", - "2 Jan 2006, 15:4", - "02 Jan 2006, 15:04:05", - "02 Jan 2006, 15:4:05", - "02 Jan 2006, 15:4:5", - "02 Jan 2006, 15:04:5", - "2 Jan 2006, 15:04:05", - "2 Jan 2006, 15:04:5", - "2 Jan 2006, 15:4:5", - "2 Jan 2006, 15:4:05", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + return p.parse() case dateAlphaWSDigitCommaWsYear: // May 8, 2009 5:57:51 PM @@ -1483,7 +1425,7 @@ iterRunes: return parse("Mon, _2 Jan 2006 15:04:05 -0700 (MST)", datestr, loc) } - u.Warnf("no format for %d %d %s", p.stateDate, p.stateTime, p.datestr) + //u.Warnf("no format for %d %d %s", p.stateDate, p.stateTime, p.datestr) return time.Time{}, fmt.Errorf("Could not find date format for %s", datestr) } diff --git a/parseany_test.go b/parseany_test.go index 38961d4..5733e0f 100644 --- a/parseany_test.go +++ b/parseany_test.go @@ -106,6 +106,13 @@ func TestInLocation(t *testing.T) { assert.Equal(t, "2006-01-02 22:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) } +func TestOne(t *testing.T) { + time.Local = time.UTC + var ts time.Time + ts = MustParse("03 February 2013") + assert.Equal(t, "2013-02-03 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) + +} func TestParse(t *testing.T) { // Lets ensure we are operating on UTC @@ -632,3 +639,21 @@ func testDidPanic(datestr string) (paniced bool) { MustParse(datestr) return false } + +func TestPStruct(t *testing.T) { + + denverLoc, err := time.LoadLocation("America/Denver") + assert.Equal(t, nil, err) + + p := newParser("08.21.71", denverLoc) + + p.setMonth() + assert.Equal(t, 0, p.moi) + p.setDay() + assert.Equal(t, 0, p.dayi) + p.set(-1, "not") + p.set(15, "not") + assert.Equal(t, "08.21.71", p.datestr) + assert.Equal(t, "08.21.71", string(p.format)) + +} From e152c5aaf69a8dcb04ac72375d608bf967663d71 Mon Sep 17 00:00:00 2001 From: Aaron Raddon Date: Thu, 1 Mar 2018 18:36:46 -0800 Subject: [PATCH 04/10] Dateparse refactor to split time/date --- dateparse/main.go | 2 +- parseany.go | 714 ++++++++++++++++++++++++---------------------- parseany_test.go | 31 +- 3 files changed, 396 insertions(+), 351 deletions(-) diff --git a/dateparse/main.go b/dateparse/main.go index b052f13..78d4ba4 100644 --- a/dateparse/main.go +++ b/dateparse/main.go @@ -105,7 +105,7 @@ func parseAny(datestr string, loc *time.Location, utc bool) string { return err.Error() } if utc { - return t.In(time.UTC).String() + return fmt.Sprintf("%s day=%d", t.In(time.UTC), t.In(time.UTC).Weekday()) } return t.String() } diff --git a/parseany.go b/parseany.go index ef7c602..51914c7 100644 --- a/parseany.go +++ b/parseany.go @@ -32,27 +32,24 @@ const ( dateDigitDot dateDigitDotDot dateDigitSlash - dateDigitSlashWS - dateDigitSlashWSColon - dateDigitSlashWSColonAMPM - dateDigitSlashWSColonColon - dateDigitSlashWSColonColonAMPM + dateDigitSlashWs + dateDigitSlashWsColon + dateDigitSlashWsColonAMPM + dateDigitSlashWsColonColon + dateDigitSlashWsColonColonAMPM dateDigitChineseYear dateDigitChineseYearWs dateDigitWs dateDigitWsMoYear + dateDigitWsMolong dateAlpha - dateAlphaWS - dateAlphaWSDigit - dateAlphaWSDigitComma - dateAlphaWSDigitCommaWs - dateAlphaWSDigitCommaWsYear - dateAlphaWSAlpha - dateAlphaWSAlphaColon - dateAlphaWSAlphaColonOffset - dateAlphaWSAlphaColonAlpha - dateAlphaWSAlphaColonAlphaOffset - dateAlphaWSAlphaColonAlphaOffsetAlpha + dateAlphaWs + dateAlphaWsDigit + dateAlphaWsDigitComma + dateAlphaWsDigitCommaWs + dateAlphaWsDigitCommaWsYear + dateAlphaWsAlpha + dateAlphaWsAlphaYearmaybe dateWeekdayComma dateWeekdayCommaDash dateWeekdayCommaOffset @@ -66,24 +63,29 @@ const ( timeIgnore timeState = iota timeStart timeWs + timeWsAlpha // 3 + timeWsAlphaZoneOffset + timeWsAlphaZoneOffsetWs + timeWsAlphaZoneOffsetWsYear + timeWsAlphaZoneOffsetWsExtra timeWsAMPMMaybe timeWsAMPM - timeWsOffset // 5 - timeWsAlpha + timeWsOffset timeWsOffsetAlpha timeWsOffsetColonAlpha timeWsOffsetColon - timeOffset // 10 + timeWsYear + timeOffset timeOffsetColon timeAlpha timePeriod timePeriodOffset - timePeriodOffsetColon // 15 + timePeriodOffsetColon timePeriodWs timePeriodWsAlpha timePeriodWsOffset timePeriodWsOffsetWs - timePeriodWsOffsetWsAlpha // 20 + timePeriodWsOffsetWsAlpha timeZ timeZDigit ) @@ -150,6 +152,8 @@ type parser struct { stateTime timeState format []byte datestr string + skip int + extra int part1Len int part2Len int part3Len int @@ -168,9 +172,12 @@ type parser struct { msi int mslen int offseti int + offsetlen int + tzi int + tzlen int } -func newParser(dateStr string, loc *time.Location) parser { +func newParser(dateStr string, loc *time.Location) *parser { p := parser{ stateDate: dateStart, stateTime: timeIgnore, @@ -178,60 +185,49 @@ func newParser(dateStr string, loc *time.Location) parser { loc: loc, } p.format = []byte(dateStr) - return p + return &p } -func (p parser) set(start int, val string) { +func (p *parser) set(start int, val string) { if start < 0 { return } if len(p.format) < start+len(val) { + u.Warnf("not enough space %d %d", len(p.format), start+len(val)) + u.Warnf("%s", p.format[start:]) return } for i, r := range val { p.format[start+i] = byte(r) } } -func (p parser) setMonth() { - if p.moi <= 0 { - return - } +func (p *parser) setMonth() { if p.molen == 2 { p.set(p.moi, "01") - } else { + } else if p.molen == 1 { p.set(p.moi, "1") } } -func (p parser) monthConvert(start, end int, mo string) { - if len(p.format) <= end { - return - } - for i := start; i < end; i++ { - p.format[i] = ' ' - } - p.set(start, mo) -} -func (p parser) setDay() { - if p.dayi < 0 { - return - } +func (p *parser) setDay() { if p.daylen == 2 { p.set(p.dayi, "02") - } else { + } else if p.daylen == 1 { p.set(p.dayi, "2") } } - -func (p parser) coalesceDate(end int) { +func (p *parser) setYear() { + if p.yearlen == 2 { + p.set(p.yeari, "06") + } else if p.yearlen == 4 { + p.set(p.yeari, "2006") + } +} +func (p *parser) coalesceDate(end int) { if p.yeari > 0 { if p.yearlen == 0 { p.yearlen = end - p.yeari } - if p.yearlen == 2 { - p.set(p.yeari, "06") - } else if p.yearlen == 4 { - p.set(p.yeari, "2006") - } + p.setYear() } if p.moi > 0 && p.molen == 0 { p.molen = end - p.moi @@ -243,7 +239,7 @@ func (p parser) coalesceDate(end int) { } } -func (p parser) coalesceTime(end int) { +func (p *parser) coalesceTime(end int) { // 03:04:05 // 15:04:05 // 3:04:05 @@ -252,7 +248,8 @@ func (p parser) coalesceTime(end int) { if p.houri > 0 { if p.hourlen == 2 { p.set(p.houri, "15") - } else { + } else if p.hourlen == 1 { + u.Warnf("WTF houri is wrong? houri=%d hourlen=%d", p.houri, p.hourlen) p.set(p.houri, "3") } } @@ -279,10 +276,10 @@ func (p parser) coalesceTime(end int) { } if p.msi > 0 { - if p.mslen == 0 { - p.mslen = end - p.msi - //u.Warnf("set mslen??? %v", p.datestr) - } + // if p.mslen == 0 { + // p.mslen = end - p.msi + // //u.Warnf("set mslen??? %v", p.datestr) + // } for i := 0; i < p.mslen; i++ { p.format[p.msi+i] = '0' } @@ -290,8 +287,25 @@ func (p parser) coalesceTime(end int) { //u.Debugf("coalesce %+v", p) } -func (p parser) parse() (time.Time, error) { - u.Debugf("parse() loc=%v %50s AS %50s", p.loc.String(), p.datestr, p.format) +func (p *parser) trimExtra() { + if p.extra > 0 && len(p.format) > p.extra { + u.Debugf("trim extra %d", p.extra) + p.format = p.format[0:p.extra] + p.datestr = p.datestr[0:p.extra] + } +} + +// func (p parser) fill(start, ct int, b byte) { +// for i := start; i < start+ct; i++ { +// p.format[i] = b +// } +// } +func (p *parser) parse() (time.Time, error) { + if p.skip > 0 && len(p.format) > p.skip { + p.format = p.format[p.skip:] + p.datestr = p.datestr[p.skip:] + } + u.Debugf("parse() loc=%v %50s AS %q", p.loc.String(), p.datestr, p.format) if p.loc == nil { return time.Parse(string(p.format), p.datestr) } @@ -342,6 +356,7 @@ iterRunes: // 3.31.2014 p.moi = 0 p.molen = i + p.setMonth() p.dayi = i + 1 p.stateDate = dateDigitDot case ' ': @@ -449,7 +464,7 @@ iterRunes: switch r { case ' ': - p.stateDate = dateDigitSlashWS + p.stateDate = dateDigitSlashWs case '/': continue default: @@ -457,7 +472,7 @@ iterRunes: // continue // } } - case dateDigitSlashWS: + case dateDigitSlashWs: // 2014/07/10 06:55:38.156283 // 03/19/2012 10:11:59 // 04/2/2014 03:00:37 @@ -466,9 +481,9 @@ iterRunes: // 4/8/14 22:05 switch r { case ':': - p.stateDate = dateDigitSlashWSColon + p.stateDate = dateDigitSlashWsColon } - case dateDigitSlashWSColon: + case dateDigitSlashWsColon: // 2014/07/10 06:55:38.156283 // 03/19/2012 10:11:59 // 04/2/2014 03:00:37 @@ -478,11 +493,11 @@ iterRunes: // 3/1/2012 10:11:59 AM switch r { case ':': - p.stateDate = dateDigitSlashWSColonColon + p.stateDate = dateDigitSlashWsColonColon case 'A', 'P': - p.stateDate = dateDigitSlashWSColonAMPM + p.stateDate = dateDigitSlashWsColonAMPM } - case dateDigitSlashWSColonColon: + case dateDigitSlashWsColonColon: // 2014/07/10 06:55:38.156283 // 03/19/2012 10:11:59 // 04/2/2014 03:00:37 @@ -492,7 +507,7 @@ iterRunes: // 3/1/2012 10:11:59 AM switch r { case 'A', 'P': - p.stateDate = dateDigitSlashWSColonColonAMPM + p.stateDate = dateDigitSlashWsColonColonAMPM } case dateDigitWs: @@ -504,7 +519,6 @@ iterRunes: // 12 Feb 2006, 19:17:22 switch r { case ' ': - u.Infof("part1=%d i=%d", p.part1Len, i) p.yeari = i + 1 p.yearlen = 4 p.dayi = 0 @@ -512,20 +526,16 @@ iterRunes: p.setDay() p.stateTime = timeStart if i <= len("12 Feb") { - p.moi = p.daylen + 1 p.molen = 3 p.set(p.moi, "Jan") - u.Infof("set day dayi=%d len=%d", p.dayi, p.daylen) + p.stateDate = dateDigitWsMoYear } else { - u.Warnf("unhandled long month") - p.monthConvert(p.daylen+1, i, "Jan") + p.stateDate = dateDigitWsMolong } - p.stateDate = dateDigitWsMoYear } case dateDigitWsMoYear: - u.Debugf("dateDigitWsMoYear ") // 02 Jan 2018 23:59 // 02 Jan 2018 23:59:34 // 12 Feb 2006, 19:17 @@ -537,6 +547,9 @@ iterRunes: case ' ': break iterRunes } + case dateDigitWsMolong: + // 18 January 2018 + // 8 January 2018 case dateDigitChineseYear: // dateDigitChineseYear @@ -552,6 +565,7 @@ iterRunes: if r == '.' { p.daylen = i - p.dayi p.yeari = i + 1 + p.setDay() p.stateDate = dateDigitDotDot } case dateDigitDotDot: @@ -584,17 +598,20 @@ iterRunes: // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) switch { case r == ' ': - p.stateDate = dateAlphaWS - p.set(0, "Mon") - p.part1Len = i + p.stateDate = dateAlphaWs case r == ',': - p.part1Len = i + p.moi = 0 + p.molen = i if i == 3 { p.stateDate = dateWeekdayAbbrevComma p.set(0, "Mon") } else { p.stateDate = dateWeekdayComma - p.set(0, "Monday") + //p.set(0, "Monday") + p.skip = i + 2 + // TODO: lets just make this "skip" as we don't need + // the mon, monday, they are all superfelous and not needed + // just lay down the skip, no need to fill and then skip } i++ } @@ -605,15 +622,27 @@ iterRunes: // dateWeekdayCommaOffset // Monday, 02 Jan 2006 15:04:05 -0700 // Monday, 02 Jan 2006 15:04:05 +0100 - switch { - case r == '-': - if i < 15 { - p.stateDate = dateWeekdayCommaDash + if p.dayi == 0 { + p.dayi = i + } + u.Debugf("weekday %d %q", i, string(r)) + switch r { + case ' ', '-': + if p.moi == 0 { + p.moi = i + 1 + p.daylen = i - p.dayi + p.setDay() + } else if p.yeari == 0 { + + p.yeari = i + 1 + p.molen = i - p.moi + p.set(p.moi, "Jan") + u.Warnf("set month? %v %v", p.moi, p.molen) + } else { + p.stateTime = timeStart break iterRunes } - p.stateDate = dateWeekdayCommaOffset - case r == '+': - p.stateDate = dateWeekdayCommaOffset + //case r == '-': } case dateWeekdayAbbrevComma: // Starts alpha then comma // Mon, 02 Jan 2006 15:04:05 MST @@ -649,92 +678,142 @@ iterRunes: p.stateDate = dateWeekdayAbbrevCommaOffsetZone } - case dateAlphaWS: // Starts alpha then whitespace - // Mon Jan _2 15:04:05 2006 - // Mon Jan _2 15:04:05 MST 2006 - // Mon Jan 02 15:04:05 -0700 2006 - // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - // Mon Aug 10 15:44:11 UTC+0100 2015 + case dateAlphaWs: + // dateAlphaWsAlpha + // Mon Jan _2 15:04:05 2006 + // Mon Jan _2 15:04:05 MST 2006 + // Mon Jan 02 15:04:05 -0700 2006 + // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) + // Mon Aug 10 15:44:11 UTC+0100 2015 + // dateAlphaWsDigit + // May 8, 2009 5:57:51 PM + u.Infof("dateAlphaWs %s %d", string(r), i) switch { - case r == ' ': - p.part2Len = i - p.part1Len + // case r == ' ': + // p.part2Len = i - p.part1Len case unicode.IsLetter(r): - p.stateDate = dateAlphaWSAlpha + p.set(0, "Mon") + p.stateDate = dateAlphaWsAlpha + //p.moi = i + p.set(i, "Jan") case unicode.IsDigit(r): - p.stateDate = dateAlphaWSDigit + p.set(0, "Jan") + p.stateDate = dateAlphaWsDigit + p.dayi = i default: - u.Warnf("can we drop isLetter? case r=%s", string(r)) + u.Warnf("what is this? case r=%s", string(r)) } - case dateAlphaWSDigit: // Starts Alpha, whitespace, digit, comma - // dateAlphaWSDigit + case dateAlphaWsDigit: + // dateAlphaWsDigit // May 8, 2009 5:57:51 PM switch { case r == ',': - p.stateDate = dateAlphaWSDigitComma + p.daylen = i - p.dayi + p.setDay() + p.stateDate = dateAlphaWsDigitComma case unicode.IsDigit(r): - p.stateDate = dateAlphaWSDigit - default: - u.Warnf("hm, can we drop a case here? %v", string(r)) + p.stateDate = dateAlphaWsDigit + u.Warnf("wtf not possible? %s", datestr) } - case dateAlphaWSDigitComma: + case dateAlphaWsDigitComma: // x // May 8, 2009 5:57:51 PM switch { case r == ' ': - p.stateDate = dateAlphaWSDigitCommaWs + p.stateDate = dateAlphaWsDigitCommaWs + p.yeari = i + 1 default: u.Warnf("hm, can we drop a case here? %v", string(r)) return time.Time{}, fmt.Errorf("could not find format for %v expected white-space after comma", datestr) } - case dateAlphaWSDigitCommaWs: + case dateAlphaWsDigitCommaWs: // x // May 8, 2009 5:57:51 PM - if !unicode.IsDigit(r) { - p.stateDate = dateAlphaWSDigitCommaWsYear + if r == ' ' { + p.stateDate = dateAlphaWsDigitCommaWsYear + p.yearlen = i - p.yeari + p.setYear() + p.stateTime = timeStart + u.Debugf("here we are %s i=%d yeari=%d yearlen=%d", string(r), i, p.yeari, p.yearlen) break iterRunes } - case dateAlphaWSAlpha: // Alpha, whitespace, alpha + case dateAlphaWsAlpha: // Mon Jan _2 15:04:05 2006 // Mon Jan 02 15:04:05 -0700 2006 // Mon Jan _2 15:04:05 MST 2006 // Mon Aug 10 15:44:11 UTC+0100 2015 // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) + u.Debugf("dateAlphaWsAlpha i=%d r=%s", i, string(r)) + if r == ' ' { + if p.dayi > 0 { + p.daylen = i - p.dayi + u.Warnf("%s dayi=%v daylen=%v format=%s", datestr, p.dayi, p.daylen, string(p.format)) + p.setDay() + u.Warnf("%s", string(p.format)) + p.yeari = i + 1 + p.stateDate = dateAlphaWsAlphaYearmaybe + p.stateTime = timeStart + } + } else if unicode.IsDigit(r) { + if p.dayi == 0 { + p.dayi = i + u.Warnf("found dayi %d", p.dayi) + } + } + case dateAlphaWsAlphaYearmaybe: + // x + // Mon Jan _2 15:04:05 2006 + // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) + u.Warnf("%s i=%d yeari+3=%d", string(r), i, p.yeari+4) if r == ':' { - p.stateDate = dateAlphaWSAlphaColon - } - case dateAlphaWSAlphaColon: - // Mon Jan _2 15:04:05 2006 - // Mon Jan 02 15:04:05 -0700 2006 - // Mon Jan _2 15:04:05 MST 2006 - // Mon Aug 10 15:44:11 UTC+0100 2015 - // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - if unicode.IsLetter(r) { - p.stateDate = dateAlphaWSAlphaColonAlpha - } else if r == '-' || r == '+' { - p.stateDate = dateAlphaWSAlphaColonOffset - } - case dateAlphaWSAlphaColonAlpha: - // Mon Jan _2 15:04:05 MST 2006 - // Mon Aug 10 15:44:11 UTC+0100 2015 - // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - if r == '+' { - p.stateDate = dateAlphaWSAlphaColonAlphaOffset - } - case dateAlphaWSAlphaColonAlphaOffset: - // Mon Aug 10 15:44:11 UTC+0100 2015 - // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - if unicode.IsLetter(r) { - p.stateDate = dateAlphaWSAlphaColonAlphaOffsetAlpha + i = i - 3 + p.stateDate = dateAlphaWsAlpha + p.yeari = 0 + break iterRunes + } else if r == ' ' { + // must be year format, not 15:04 + p.yearlen = i - p.yeari + u.Infof("yeari=%d yearlen=%d", p.yeari, p.yearlen) + p.setYear() + u.Debugf("format=%s", string(p.format)) + break iterRunes } + + /* + case dateAlphaWSAlphaColon: + // Mon Jan _2 15:04:05 2006 + // Mon Jan 02 15:04:05 -0700 2006 + // Mon Jan _2 15:04:05 MST 2006 + // Mon Aug 10 15:44:11 UTC+0100 2015 + // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) + if unicode.IsLetter(r) { + p.stateDate = dateAlphaWSAlphaColonAlpha + } else if r == '-' || r == '+' { + p.stateDate = dateAlphaWSAlphaColonOffset + } + case dateAlphaWSAlphaColonAlpha: + // Mon Jan _2 15:04:05 MST 2006 + // Mon Aug 10 15:44:11 UTC+0100 2015 + // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) + if r == '+' { + p.stateDate = dateAlphaWSAlphaColonAlphaOffset + } + case dateAlphaWSAlphaColonAlphaOffset: + // Mon Aug 10 15:44:11 UTC+0100 2015 + // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) + if unicode.IsLetter(r) { + p.stateDate = dateAlphaWSAlphaColonAlphaOffsetAlpha + } + */ default: break iterRunes } } - + u.Warnf("%s", string(p.format)) p.coalesceDate(i) - + u.Warnf("%s", string(p.format)) if p.stateTime == timeStart { // increment first one, since the i++ occurs at end of loop i++ @@ -743,7 +822,7 @@ iterRunes: for ; i < len(datestr); i++ { r := rune(datestr[i]) - u.Debugf("i=%d r=%s timeState=%d", i, string(r), p.stateTime) + u.Debugf("i=%d r=%s timeState=%d hi=%d hl=%d", i, string(r), p.stateTime, p.houri, p.hourlen) switch p.stateTime { case timeStart: // 22:43:22 @@ -755,6 +834,7 @@ iterRunes: // 00:12:00 +0000 UTC // 15:04:05 -0700 // 15:04:05 -07:00 + // 15:04:05 2008 // timeOffset // 03:21:51+00:00 // 19:55:00+0100 @@ -815,6 +895,7 @@ iterRunes: if p.mini == 0 { p.mini = i + 1 p.hourlen = i - p.houri + u.Infof("hour i=%v l=%d", p.houri, p.hourlen) } else if p.seci == 0 { p.seci = i + 1 p.minlen = i - p.mini @@ -830,8 +911,10 @@ iterRunes: p.stateTime = timeOffsetColon } case timeWs: - // timeAlpha + // timeWsAlpha // 06:20:00 UTC + // 15:44:11 UTC+0100 2015 + // 18:04:07 GMT+0100 (GMT Daylight Time) // timeWsAMPMMaybe // 05:24:37 PM // timeWsOffset @@ -842,11 +925,15 @@ iterRunes: // 00:12:00 +00:00 UTC // timeWsOffsetAlpha // 00:12:00 +0000 UTC + // timeWsYear + // 00:12:00 2008 // timeZ // 15:04:05.99Z + u.Debugf("timeWs") switch r { case 'A', 'P': // Could be AM/PM or could be PST or similar + p.tzi = i p.stateTime = timeWsAMPMMaybe case '+', '-': p.offseti = i @@ -854,17 +941,81 @@ iterRunes: default: if unicode.IsLetter(r) { // 06:20:00 UTC + // 15:44:11 UTC+0100 2015 + p.tzi = i p.stateTime = timeWsAlpha - break iterTimeRunes + //break iterTimeRunes + } else if unicode.IsDigit(r) { + // 00:12:00 2008 + p.stateTime = timeWsYear + p.yeari = i } } - + case timeWsAlpha: + // 06:20:00 UTC + // timeWsAlphaZoneOffset + // timeWsAlphaZoneOffsetWs + // timeWsAlphaZoneOffsetWsExtra + // 18:04:07 GMT+0100 (GMT Daylight Time) + // timeWsAlphaZoneOffsetWsYear + // 15:44:11 UTC+0100 2015 + u.Debugf("timeWsAlpha") + switch r { + case '+', '-': + p.tzlen = i - p.tzi + u.Infof("tzi=%d tzlen=%d", p.tzi, p.tzlen) + if p.tzlen == 4 { + p.set(p.tzi, "MST ") + } else if p.tzlen == 3 { + p.set(p.tzi, "MST") + } + p.stateTime = timeWsAlphaZoneOffset + p.offseti = i + } + case timeWsAlphaZoneOffset: + // timeWsAlphaZoneOffset + // timeWsAlphaZoneOffsetWs + // timeWsAlphaZoneOffsetWsExtra + // 18:04:07 GMT+0100 (GMT Daylight Time) + // timeWsAlphaZoneOffsetWsYear + // 15:44:11 UTC+0100 2015 + u.Debugf("timeWsAlphaZoneOffset") + switch r { + case ' ': + p.set(p.offseti, "-0700") + p.yeari = i + 1 + p.stateTime = timeWsAlphaZoneOffsetWs + } + case timeWsAlphaZoneOffsetWs: + // timeWsAlphaZoneOffsetWs + // timeWsAlphaZoneOffsetWsExtra + // 18:04:07 GMT+0100 (GMT Daylight Time) + // timeWsAlphaZoneOffsetWsYear + // 15:44:11 UTC+0100 2015 + u.Debugf("timeWsAlphaZoneOffsetWs") + if unicode.IsDigit(r) { + p.stateTime = timeWsAlphaZoneOffsetWsYear + } else { + p.extra = i - 1 + p.stateTime = timeWsAlphaZoneOffsetWsExtra + u.Warnf("going to chop some stuff off %d", p.extra) + } + case timeWsAlphaZoneOffsetWsYear: + // 15:44:11 UTC+0100 2015 + if unicode.IsDigit(r) { + p.yearlen = i - p.yeari + 1 + if p.yearlen == 4 { + p.setYear() + } + } + u.Debugf("timeWsAlphaZoneOffsetWsYear yeari=%d yearlen=%d", p.yeari, p.yearlen) case timeWsAMPMMaybe: // timeWsAMPMMaybe // timeWsAMPM // 05:24:37 PM // timeWsAlpha // 00:12:00 PST + // 15:44:11 UTC+0100 2015 if r == 'M' { //return parse("2006-01-02 03:04:05 PM", datestr, loc) p.stateTime = timeWsAMPM @@ -1035,11 +1186,26 @@ iterRunes: } } + u.Debugf("timestate = %d", p.stateTime) switch p.stateTime { + case timeWsAlpha: + u.Warnf("timeWsAlpha?") + case timeWsYear: + p.yearlen = i - p.yeari + u.Infof("got WsYear yeari=%d yearlen=%d", p.yeari, p.yearlen) + p.setYear() + case timeWsAlphaZoneOffsetWsExtra: + u.Warnf("%s", string(p.format)) + p.trimExtra() + u.Warnf("%s", string(p.format)) case timePeriod: p.mslen = i - p.msi case timeOffset: // 19:55:00+0100 + u.Warnf("offset?") + p.set(p.offseti, "-0700") + case timeWsOffset: + u.Warnf("timeWsOffset?") p.set(p.offseti, "-0700") case timeOffsetColon: // 15:04:05+07:00 @@ -1062,8 +1228,9 @@ iterRunes: default: //u.Warnf("un-handled statetime: %d for %v", p.stateTime, p.datestr) } - + u.Warnf("%s", string(p.format)) p.coalesceTime(i) + u.Warnf("parse: %q AS %q", p.datestr, string(p.format)) } //u.Infof("%60s %q\n\t%+v", datestr, string(p.format), p) @@ -1143,12 +1310,12 @@ iterRunes: // 03.31.1981 // 3.2.1981 // 3.2.81 + p.setYear() p.yearlen = i - p.yeari return p.parse() case dateDigitWs: - // 18 January 2018 - // 8 January 2018 + u.Warnf("what? %s", datestr) return p.parse() case dateDigitWsMoYear: @@ -1158,247 +1325,120 @@ iterRunes: // 12 Feb 2006, 19:17 // 12 Feb 2006, 19:17:22 return p.parse() + case dateDigitWsMolong: + // 18 January 2018 + // 8 January 2018 + if p.daylen == 2 { + return parse("02 January 2006", datestr, loc) + } + return parse("2 January 2006", datestr, loc) - case dateAlphaWSDigitCommaWsYear: + case dateAlphaWsDigitCommaWsYear: // May 8, 2009 5:57:51 PM - for _, layout := range []string{ - "Jan 2, 2006 3:04:05 PM", - "Jan 2, 2006 3:4:05 PM", - "Jan 2, 2006 3:4:5 PM", - "Jan 2, 2006 3:04:5 PM", - "Jan 02, 2006 3:04:05 PM", - "Jan 02, 2006 3:4:05 PM", - "Jan 02, 2006 3:4:5 PM", - "Jan 02, 2006 3:04:5 PM", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - case dateAlphaWSAlphaColon: - // Mon Jan _2 15:04:05 2006 - return parse(time.ANSIC, datestr, loc) + return p.parse() - case dateAlphaWSAlphaColonOffset: - // Mon Jan 02 15:04:05 -0700 2006 - return parse(time.RubyDate, datestr, loc) + case dateAlphaWsAlpha: + return p.parse() - case dateAlphaWSAlphaColonAlpha: - // Mon Jan _2 15:04:05 MST 2006 - return parse(time.UnixDate, datestr, loc) + case dateAlphaWsAlphaYearmaybe: + return p.parse() + case dateAlphaWsDigitCommaWs: + return p.parse() - case dateAlphaWSAlphaColonAlphaOffset: - // Mon Aug 10 15:44:11 UTC+0100 2015 - return parse("Mon Jan 02 15:04:05 MST-0700 2006", datestr, loc) + /* + case dateAlphaWsAlphaWs: + case dateAlphaWSAlphaColon: + // Mon Jan _2 15:04:05 2006 + return parse(time.ANSIC, datestr, loc) - case dateAlphaWSAlphaColonAlphaOffsetAlpha: - // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - if len(datestr) > len("Mon Jan 02 2006 15:04:05 MST-0700") { - // What effing time stamp is this? - // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - dateTmp := datestr[:33] - return parse("Mon Jan 02 2006 15:04:05 MST-0700", dateTmp, loc) - } + case dateAlphaWSAlphaColonOffset: + // Mon Jan 02 15:04:05 -0700 2006 + return parse(time.RubyDate, datestr, loc) + + case dateAlphaWSAlphaColonAlpha: + // Mon Jan _2 15:04:05 MST 2006 + return parse(time.UnixDate, datestr, loc) + + case dateAlphaWSAlphaColonAlphaOffset: + // Mon Aug 10 15:44:11 UTC+0100 2015 + return parse("Mon Jan 02 15:04:05 MST-0700 2006", datestr, loc) + + case dateAlphaWSAlphaColonAlphaOffsetAlpha: + // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) + if len(datestr) > len("Mon Jan 02 2006 15:04:05 MST-0700") { + // What effing time stamp is this? + // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) + dateTmp := datestr[:33] + return parse("Mon Jan 02 2006 15:04:05 MST-0700", dateTmp, loc) + } + */ case dateDigitSlash: // starts digit then slash 02/ (but nothing else) // 3/1/2014 // 10/13/2014 // 01/02/2006 // 2014/10/13 - if p.part1Len == 4 { - if len(datestr) == len("2006/01/02") { - return parse("2006/01/02", datestr, loc) - } - return parse("2006/1/2", datestr, loc) - } - for _, parseFormat := range shortDates { - if t, err := parse(parseFormat, datestr, loc); err == nil { - return t, nil - } - } + return p.parse() - case dateDigitSlashWSColon: // starts digit then slash 02/ more digits/slashes then whitespace + case dateDigitSlashWsColon: // 4/8/2014 22:05 // 04/08/2014 22:05 // 2014/4/8 22:05 // 2014/04/08 22:05 - if p.part1Len == 4 { - for _, layout := range []string{"2006/01/02 15:04", "2006/1/2 15:04", "2006/01/2 15:04", "2006/1/02 15:04", "2006/01/02 15:4", "2006/1/2 15:4", "2006/01/2 15:4", "2006/1/02 15:4"} { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - } else { - for _, layout := range []string{"01/02/2006 15:4", "01/2/2006 15:4", "1/02/2006 15:4", "1/2/2006 15:4", "1/2/06 15:4", "01/02/06 15:4", "01/02/2006 15:04", "01/2/2006 15:04", "1/02/2006 15:04", "1/2/2006 15:04", "1/2/06 15:04", "01/02/06 15:04"} { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - } + return p.parse() - case dateDigitSlashWSColonAMPM: // starts digit then slash 02/ more digits/slashes then whitespace + case dateDigitSlashWsColonAMPM: // 4/8/2014 22:05 PM // 04/08/2014 22:05 PM // 04/08/2014 1:05 PM // 2014/4/8 22:05 PM // 2014/04/08 22:05 PM + return p.parse() - if p.part1Len == 4 { - for _, layout := range []string{"2006/01/02 03:04 PM", "2006/01/2 03:04 PM", "2006/1/02 03:04 PM", "2006/1/2 03:04 PM", - "2006/01/02 3:04 PM", "2006/01/2 3:04 PM", "2006/1/02 3:04 PM", "2006/1/2 3:04 PM", "2006/01/02 3:4 PM", "2006/01/2 3:4 PM", "2006/1/02 3:4 PM", "2006/1/2 3:4 PM", - "2006/01/02 3:4 PM", "2006/01/2 3:4 PM", "2006/1/02 3:4 PM", "2006/1/2 3:4 PM"} { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - } else { - for _, layout := range []string{"01/02/2006 03:04 PM", "01/2/2006 03:04 PM", "1/02/2006 03:04 PM", "1/2/2006 03:04 PM", - "01/02/2006 03:4 PM", "01/2/2006 03:4 PM", "1/02/2006 03:4 PM", "1/2/2006 03:4 PM", - "01/02/2006 3:04 PM", "01/2/2006 3:04 PM", "1/02/2006 3:04 PM", "1/2/2006 3:04 PM", - "01/02/2006 3:04 PM", "01/2/2006 3:04 PM", "1/02/2006 3:04 PM", "1/2/2006 3:04 PM", - "01/02/2006 3:4 PM", "01/2/2006 3:4 PM", "1/02/2006 3:4 PM", "1/2/2006 3:4 PM", - "01/02/2006 3:4 PM", "01/2/2006 3:4 PM", "1/02/2006 3:4 PM", "1/2/2006 3:4 PM"} { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - - } - } - - case dateDigitSlashWSColonColon: // starts digit then slash 02/ more digits/slashes then whitespace double colons + case dateDigitSlashWsColonColon: // 2014/07/10 06:55:38.156283 // 03/19/2012 10:11:59 // 3/1/2012 10:11:59 // 03/1/2012 10:11:59 // 3/01/2012 10:11:59 // 4/8/14 22:05 - if p.part1Len == 4 { - for _, layout := range []string{"2006/01/02 15:04:05", "2006/1/02 15:04:05", "2006/01/2 15:04:05", "2006/1/2 15:04:05", - "2006/01/02 15:04:5", "2006/1/02 15:04:5", "2006/01/2 15:04:5", "2006/1/2 15:04:5", - "2006/01/02 15:4:05", "2006/1/02 15:4:05", "2006/01/2 15:4:05", "2006/1/2 15:4:05", - "2006/01/02 15:4:5", "2006/1/02 15:4:5", "2006/01/2 15:4:5", "2006/1/2 15:4:5"} { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - } else { - for _, layout := range []string{"01/02/2006 15:04:05", "1/02/2006 15:04:05", "01/2/2006 15:04:05", "1/2/2006 15:04:05", - "01/02/2006 15:4:5", "1/02/2006 15:4:5", "01/2/2006 15:4:5", "1/2/2006 15:4:5", - "01/02/2006 15:4:05", "1/02/2006 15:4:05", "01/2/2006 15:4:05", "1/2/2006 15:4:05", - "01/02/2006 15:04:5", "1/02/2006 15:04:5", "01/2/2006 15:04:5", "1/2/2006 15:04:5"} { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - } + return p.parse() - case dateDigitSlashWSColonColonAMPM: // starts digit then slash 02/ more digits/slashes then whitespace double colons + case dateDigitSlashWsColonColonAMPM: // 2014/07/10 06:55:38.156283 PM // 03/19/2012 10:11:59 PM // 3/1/2012 10:11:59 PM // 03/1/2012 10:11:59 PM // 3/01/2012 10:11:59 PM + return p.parse() - if p.part1Len == 4 { - for _, layout := range []string{"2006/01/02 03:04:05 PM", "2006/1/02 03:04:05 PM", "2006/01/2 03:04:05 PM", "2006/1/2 03:04:05 PM", - "2006/01/02 03:4:5 PM", "2006/1/02 03:4:5 PM", "2006/01/2 03:4:5 PM", "2006/1/2 03:4:5 PM", - "2006/01/02 03:4:05 PM", "2006/1/02 03:4:05 PM", "2006/01/2 03:4:05 PM", "2006/1/2 03:4:05 PM", - "2006/01/02 03:04:5 PM", "2006/1/02 03:04:5 PM", "2006/01/2 03:04:5 PM", "2006/1/2 03:04:5 PM", - - "2006/01/02 3:4:5 PM", "2006/1/02 3:4:5 PM", "2006/01/2 3:4:5 PM", "2006/1/2 3:4:5 PM", - "2006/01/02 3:4:05 PM", "2006/1/02 3:4:05 PM", "2006/01/2 3:4:05 PM", "2006/1/2 3:4:05 PM", - "2006/01/02 3:04:5 PM", "2006/1/02 3:04:5 PM", "2006/01/2 3:04:5 PM", "2006/1/2 3:04:5 PM"} { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - } else { - for _, layout := range []string{"01/02/2006 03:04:05 PM", "1/02/2006 03:04:05 PM", "01/2/2006 03:04:05 PM", "1/2/2006 03:04:05 PM", - "01/02/2006 03:4:05 PM", "1/02/2006 03:4:05 PM", "01/2/2006 03:4:05 PM", "1/2/2006 03:4:05 PM", - "01/02/2006 03:04:5 PM", "1/02/2006 03:04:5 PM", "01/2/2006 03:04:5 PM", "1/2/2006 03:04:5 PM", - "01/02/2006 03:4:5 PM", "1/02/2006 03:4:5 PM", "01/2/2006 03:4:5 PM", "1/2/2006 03:4:5 PM", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - } case dateDigitChineseYear: // dateDigitChineseYear // 2014年04月08日 return parse("2006年01月02日", datestr, loc) + case dateDigitChineseYearWs: return parse("2006年01月02日 15:04:05", datestr, loc) - case dateWeekdayCommaOffset: + + case dateWeekdayComma: // Monday, 02 Jan 2006 15:04:05 -0700 // Monday, 02 Jan 2006 15:04:05 +0100 - for _, layout := range []string{ - "Monday, _2 Jan 2006 15:04:05 -0700", - "Monday, _2 Jan 2006 15:04:5 -0700", - "Monday, _2 Jan 2006 15:4:05 -0700", - "Monday, _2 Jan 2006 15:4:5 -0700", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } - - case dateWeekdayCommaDash: // Monday, 02-Jan-06 15:04:05 MST - for _, layout := range []string{ - "Monday, 02-Jan-06 15:04:05 MST", - "Monday, 02-Jan-06 15:4:05 MST", - "Monday, 02-Jan-06 15:04:5 MST", - "Monday, 02-Jan-06 15:4:5 MST", - "Monday, 2-Jan-06 15:04:05 MST", - "Monday, 2-Jan-06 15:4:05 MST", - "Monday, 2-Jan-06 15:4:5 MST", - "Monday, 2-Jan-06 15:04:5 MST", - "Monday, 2-Jan-6 15:04:05 MST", - "Monday, 2-Jan-6 15:4:05 MST", - "Monday, 2-Jan-6 15:4:5 MST", - "Monday, 2-Jan-6 15:04:5 MST", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + return p.parse() + + // case dateWeekdayCommaDash: + // + // return p.parse() case dateWeekdayAbbrevComma: // Starts alpha then comma // Mon, 02-Jan-06 15:04:05 MST // Mon, 02 Jan 2006 15:04:05 MST - for _, layout := range []string{ - "Mon, _2 Jan 2006 15:04:05 MST", - "Mon, _2 Jan 2006 15:04:5 MST", - "Mon, _2 Jan 2006 15:4:5 MST", - "Mon, _2 Jan 2006 15:4:05 MST", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + return p.parse() case dateWeekdayAbbrevCommaDash: // Mon, 02-Jan-06 15:04:05 MST // Mon, 2-Jan-06 15:04:05 MST - for _, layout := range []string{ - "Mon, 02-Jan-06 15:04:05 MST", - "Mon, 02-Jan-06 15:4:05 MST", - "Mon, 02-Jan-06 15:04:5 MST", - "Mon, 02-Jan-06 15:4:5 MST", - "Mon, 2-Jan-06 15:04:05 MST", - "Mon, 2-Jan-06 15:4:05 MST", - "Mon, 2-Jan-06 15:4:5 MST", - "Mon, 2-Jan-06 15:04:5 MST", - "Mon, 2-Jan-6 15:04:05 MST", - "Mon, 2-Jan-6 15:4:05 MST", - "Mon, 2-Jan-6 15:4:5 MST", - "Mon, 2-Jan-6 15:04:5 MST", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + return p.parse() case dateWeekdayAbbrevCommaOffset: // Mon, 02 Jan 2006 15:04:05 -0700 @@ -1406,23 +1446,11 @@ iterRunes: // RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone // // Thu, 4 Jan 2018 17:53:36 +0000 - for _, layout := range []string{ - "Mon, _2 Jan 2006 15:04:05 -0700", - "Mon, _2 Jan 2006 15:4:05 -0700", - "Mon, _2 Jan 2006 15:4:5 -0700", - "Mon, _2 Jan 2006 15:04:5 -0700", - } { - if t, err := parse(layout, datestr, loc); err == nil { - return t, nil - } - } + return p.parse() case dateWeekdayAbbrevCommaOffsetZone: // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) - if p.part3Len == 1 { - return parse("Mon, 2 Jan 2006 15:04:05 -0700 (MST)", datestr, loc) - } - return parse("Mon, _2 Jan 2006 15:04:05 -0700 (MST)", datestr, loc) + return p.parse() } //u.Warnf("no format for %d %d %s", p.stateDate, p.stateTime, p.datestr) diff --git a/parseany_test.go b/parseany_test.go index 5733e0f..692fe56 100644 --- a/parseany_test.go +++ b/parseany_test.go @@ -51,6 +51,13 @@ func TestInLocation(t *testing.T) { assert.Equal(t, "MST", zone, "Should have found zone = MST %v", zone) assert.Equal(t, "2013-02-01 07:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) + ts, err = ParseIn("18 January 2018", denverLoc) + assert.Equal(t, nil, err) + zone, offset = ts.Zone() + assert.Equal(t, -25200, offset, "Should have found offset = 0 %v", offset) + assert.Equal(t, "MST", zone, "Should have found zone = UTC %v", zone) + assert.Equal(t, "2018-01-18 07:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) + // Now we are going to use ParseLocal() and see that it gives same // answer as ParseIn when we have time.Local set to a location time.Local = denverLoc @@ -109,12 +116,18 @@ func TestInLocation(t *testing.T) { func TestOne(t *testing.T) { time.Local = time.UTC var ts time.Time - ts = MustParse("03 February 2013") - assert.Equal(t, "2013-02-03 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - + ts = MustParse("Mon Aug 10 15:44:11 PST-0700 2015") + assert.Equal(t, "2015-08-10 22:44:11 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) } func TestParse(t *testing.T) { + /* + TODO: + - move to testharness + - replace all the default go dates 2006 with others + - more tests on AM/PM zones, offsets for each variation + */ + // Lets ensure we are operating on UTC time.Local = time.UTC @@ -149,8 +162,10 @@ func TestParse(t *testing.T) { assert.Equal(t, "2006-01-02 15:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) // RFC850 = "Monday, 02-Jan-06 15:04:05 MST" - ts = MustParse("Monday, 02-Jan-06 15:04:05 MST") - assert.Equal(t, "2006-01-02 15:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) + ts = MustParse("Wednesday, 07-May-09 08:00:43 MST") + assert.Equal(t, "2009-05-07 08:00:43 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) + ts = MustParse("Wednesday, 28-Feb-18 09:01:00 MST") + assert.Equal(t, "2018-02-28 09:01:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) // ST_WEEKDAYCOMMADELTA // Monday, 02 Jan 2006 15:04:05 -0700 @@ -165,10 +180,12 @@ func TestParse(t *testing.T) { assert.Equal(t, "2006-01-02 16:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) // Another weird one, year on the end after UTC? - ts = MustParse("Mon Aug 10 15:44:11 UTC+0100 2015") + ts = MustParse("Mon Aug 10 15:44:11 UTC+0000 2015") assert.Equal(t, "2015-08-10 15:44:11 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) + ts = MustParse("Mon Aug 10 15:44:11 PST-0700 2015") + assert.Equal(t, "2015-08-10 22:44:11 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - // Easily the worst Date format i have ever seen + // Easily the worst Date format I have ever seen // "Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)" ts = MustParse("Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)") assert.Equal(t, "2015-07-03 17:04:07 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) From 163a0a8ac85344ac87ac9995c7cb8e0669afcb12 Mon Sep 17 00:00:00 2001 From: Aaron Raddon Date: Sat, 10 Mar 2018 11:50:19 -0800 Subject: [PATCH 05/10] Complete refactor for re-usable time parse --- parseany.go | 401 ++++++++++++----------------------------------- parseany_test.go | 9 +- 2 files changed, 106 insertions(+), 304 deletions(-) diff --git a/parseany.go b/parseany.go index 51914c7..3d505df 100644 --- a/parseany.go +++ b/parseany.go @@ -9,15 +9,8 @@ import ( "time" "unicode" "unicode/utf8" - - u "github.com/araddon/gou" ) -func init() { - u.SetupLogging("debug") - u.SetColorOutput() -} - type dateState uint8 type timeState uint8 @@ -32,11 +25,6 @@ const ( dateDigitDot dateDigitDotDot dateDigitSlash - dateDigitSlashWs - dateDigitSlashWsColon - dateDigitSlashWsColonAMPM - dateDigitSlashWsColonColon - dateDigitSlashWsColonColonAMPM dateDigitChineseYear dateDigitChineseYearWs dateDigitWs @@ -51,12 +39,8 @@ const ( dateAlphaWsAlpha dateAlphaWsAlphaYearmaybe dateWeekdayComma - dateWeekdayCommaDash - dateWeekdayCommaOffset dateWeekdayAbbrevComma dateWeekdayAbbrevCommaDash - dateWeekdayAbbrevCommaOffset - dateWeekdayAbbrevCommaOffsetZone ) const ( // Time state @@ -155,8 +139,6 @@ type parser struct { skip int extra int part1Len int - part2Len int - part3Len int yeari int yearlen int moi int @@ -179,10 +161,11 @@ type parser struct { func newParser(dateStr string, loc *time.Location) *parser { p := parser{ - stateDate: dateStart, - stateTime: timeIgnore, - datestr: dateStr, - loc: loc, + stateDate: dateStart, + stateTime: timeIgnore, + datestr: dateStr, + loc: loc, + preferMonthFirst: true, } p.format = []byte(dateStr) return &p @@ -192,8 +175,6 @@ func (p *parser) set(start int, val string) { return } if len(p.format) < start+len(val) { - u.Warnf("not enough space %d %d", len(p.format), start+len(val)) - u.Warnf("%s", p.format[start:]) return } for i, r := range val { @@ -238,7 +219,12 @@ func (p *parser) coalesceDate(end int) { p.setDay() } } - +func (p *parser) ts() string { + return fmt.Sprintf("h:(%d:%d) m:(%d:%d) s:(%d:%d)", p.houri, p.hourlen, p.mini, p.minlen, p.seci, p.seclen) +} +func (p *parser) ds() string { + return fmt.Sprintf("%s d:(%d:%d) m:(%d:%d) y:(%d:%d)", p.datestr, p.dayi, p.daylen, p.moi, p.molen, p.yeari, p.yearlen) +} func (p *parser) coalesceTime(end int) { // 03:04:05 // 15:04:05 @@ -249,7 +235,6 @@ func (p *parser) coalesceTime(end int) { if p.hourlen == 2 { p.set(p.houri, "15") } else if p.hourlen == 1 { - u.Warnf("WTF houri is wrong? houri=%d hourlen=%d", p.houri, p.hourlen) p.set(p.houri, "3") } } @@ -266,7 +251,6 @@ func (p *parser) coalesceTime(end int) { if p.seci > 0 { if p.seclen == 0 { p.seclen = end - p.seci - //u.Infof("fixing seconds p.seci=%d seclen=%d end=%d", p.seci, p.seclen, end) } if p.seclen == 2 { p.set(p.seci, "05") @@ -276,36 +260,24 @@ func (p *parser) coalesceTime(end int) { } if p.msi > 0 { - // if p.mslen == 0 { - // p.mslen = end - p.msi - // //u.Warnf("set mslen??? %v", p.datestr) - // } for i := 0; i < p.mslen; i++ { p.format[p.msi+i] = '0' } } - //u.Debugf("coalesce %+v", p) } func (p *parser) trimExtra() { if p.extra > 0 && len(p.format) > p.extra { - u.Debugf("trim extra %d", p.extra) p.format = p.format[0:p.extra] p.datestr = p.datestr[0:p.extra] } } -// func (p parser) fill(start, ct int, b byte) { -// for i := start; i < start+ct; i++ { -// p.format[i] = b -// } -// } func (p *parser) parse() (time.Time, error) { if p.skip > 0 && len(p.format) > p.skip { p.format = p.format[p.skip:] p.datestr = p.datestr[p.skip:] } - u.Debugf("parse() loc=%v %50s AS %q", p.loc.String(), p.datestr, p.format) if p.loc == nil { return time.Parse(string(p.format), p.datestr) } @@ -351,7 +323,23 @@ iterRunes: p.set(0, "2006") } case '/': + // 03/31/2005 + // 2014/02/24 p.stateDate = dateDigitSlash + if i == 4 { + p.yearlen = i + p.moi = i + 1 + p.setYear() + } else { + if p.preferMonthFirst { + if p.molen == 0 { + p.molen = i + p.setMonth() + p.dayi = i + 1 + } + } + } + case '.': // 3.31.2014 p.moi = 0 @@ -464,51 +452,36 @@ iterRunes: switch r { case ' ': - p.stateDate = dateDigitSlashWs + p.stateTime = timeStart + if p.yearlen == 0 { + p.yearlen = i - p.yeari + p.setYear() + } else if p.daylen == 0 { + p.daylen = i - p.dayi + p.setDay() + } + break iterRunes case '/': - continue + if p.yearlen > 0 { + // 2014/07/10 06:55:38.156283 + if p.molen == 0 { + p.molen = i - p.moi + p.setMonth() + p.dayi = i + 1 + } + } else if p.preferMonthFirst { + if p.daylen == 0 { + p.daylen = i - p.dayi + p.setDay() + p.yeari = i + 1 + } + } + default: // if unicode.IsDigit(r) || r == '/' { // continue // } } - case dateDigitSlashWs: - // 2014/07/10 06:55:38.156283 - // 03/19/2012 10:11:59 - // 04/2/2014 03:00:37 - // 3/1/2012 10:11:59 - // 4/8/2014 22:05 - // 4/8/14 22:05 - switch r { - case ':': - p.stateDate = dateDigitSlashWsColon - } - case dateDigitSlashWsColon: - // 2014/07/10 06:55:38.156283 - // 03/19/2012 10:11:59 - // 04/2/2014 03:00:37 - // 3/1/2012 10:11:59 - // 4/8/2014 22:05 - // 4/8/14 22:05 - // 3/1/2012 10:11:59 AM - switch r { - case ':': - p.stateDate = dateDigitSlashWsColonColon - case 'A', 'P': - p.stateDate = dateDigitSlashWsColonAMPM - } - case dateDigitSlashWsColonColon: - // 2014/07/10 06:55:38.156283 - // 03/19/2012 10:11:59 - // 04/2/2014 03:00:37 - // 3/1/2012 10:11:59 - // 4/8/2014 22:05 - // 4/8/14 22:05 - // 3/1/2012 10:11:59 AM - switch r { - case 'A', 'P': - p.stateDate = dateDigitSlashWsColonColonAMPM - } case dateDigitWs: // 18 January 2018 @@ -582,26 +555,23 @@ iterRunes: // // dateWeekdayComma // Monday, 02 Jan 2006 15:04:05 MST - // dateWeekdayCommaDash - // Monday, 02-Jan-06 15:04:05 MST - // dateWeekdayCommaOffset - // Monday, 02 Jan 2006 15:04:05 -0700 - // Monday, 02 Jan 2006 15:04:05 +0100 + // Monday, 02-Jan-06 15:04:05 MST + // Monday, 02 Jan 2006 15:04:05 -0700 + // Monday, 02 Jan 2006 15:04:05 +0100 // dateWeekdayAbbrevComma // Mon, 02 Jan 2006 15:04:05 MST + // Mon, 02 Jan 2006 15:04:05 -0700 + // Thu, 13 Jul 2017 08:58:40 +0100 + // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) // dateWeekdayAbbrevCommaDash // Mon, 02-Jan-06 15:04:05 MST - // dateWeekdayAbbrevCommaOffset - // Mon, 02 Jan 2006 15:04:05 -0700 - // Thu, 13 Jul 2017 08:58:40 +0100 - // dateWeekdayAbbrevCommaOffsetZone - // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) + switch { case r == ' ': p.stateDate = dateAlphaWs case r == ',': - p.moi = 0 - p.molen = i + // p.moi = 0 + // p.molen = i if i == 3 { p.stateDate = dateWeekdayAbbrevComma p.set(0, "Mon") @@ -609,23 +579,21 @@ iterRunes: p.stateDate = dateWeekdayComma //p.set(0, "Monday") p.skip = i + 2 + i++ // TODO: lets just make this "skip" as we don't need // the mon, monday, they are all superfelous and not needed // just lay down the skip, no need to fill and then skip } - i++ + } - case dateWeekdayComma: // Starts alpha then comma + case dateWeekdayComma: // Monday, 02 Jan 2006 15:04:05 MST - // dateWeekdayCommaDash - // Monday, 02-Jan-06 15:04:05 MST - // dateWeekdayCommaOffset - // Monday, 02 Jan 2006 15:04:05 -0700 - // Monday, 02 Jan 2006 15:04:05 +0100 + // Monday, 02 Jan 2006 15:04:05 -0700 + // Monday, 02 Jan 2006 15:04:05 +0100 + // Monday, 02-Jan-06 15:04:05 MST if p.dayi == 0 { p.dayi = i } - u.Debugf("weekday %d %q", i, string(r)) switch r { case ' ', '-': if p.moi == 0 { @@ -637,45 +605,42 @@ iterRunes: p.yeari = i + 1 p.molen = i - p.moi p.set(p.moi, "Jan") - u.Warnf("set month? %v %v", p.moi, p.molen) } else { p.stateTime = timeStart break iterRunes } //case r == '-': } - case dateWeekdayAbbrevComma: // Starts alpha then comma + case dateWeekdayAbbrevComma: // Mon, 02 Jan 2006 15:04:05 MST + // Mon, 02 Jan 2006 15:04:05 -0700 + // Thu, 13 Jul 2017 08:58:40 +0100 + // Thu, 4 Jan 2018 17:53:36 +0000 + // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) // dateWeekdayAbbrevCommaDash // Mon, 02-Jan-06 15:04:05 MST - // dateWeekdayAbbrevCommaOffset - // Mon, 02 Jan 2006 15:04:05 -0700 - // Thu, 13 Jul 2017 08:58:40 +0100 - // Thu, 4 Jan 2018 17:53:36 +0000 - // dateWeekdayAbbrevCommaOffsetZone - // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) switch { - case r == ' ' && p.part3Len == 0: - p.part3Len = i - p.part1Len - 2 - case r == '-': - if i < 15 { - p.stateDate = dateWeekdayAbbrevCommaDash + case r == ' ': + if p.dayi == 0 { + p.dayi = i + 1 + } else if p.moi == 0 { + p.daylen = i - p.dayi + p.setDay() + p.moi = i + 1 + } else if p.yeari == 0 { + p.molen = i - p.moi + p.set(p.moi, "Jan") + p.yeari = i + 1 + } else { + p.yearlen = i - p.yeari + p.setYear() + p.stateTime = timeStart break iterRunes } - p.stateDate = dateWeekdayAbbrevCommaOffset - case r == '+': - p.stateDate = dateWeekdayAbbrevCommaOffset - } - case dateWeekdayAbbrevCommaOffset: - // dateWeekdayAbbrevCommaOffset - // Mon, 02 Jan 2006 15:04:05 -0700 - // Thu, 13 Jul 2017 08:58:40 +0100 - // Thu, 4 Jan 2018 17:53:36 +0000 - // dateWeekdayAbbrevCommaOffsetZone - // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) - if r == '(' { - p.stateDate = dateWeekdayAbbrevCommaOffsetZone + case r == '-': + p.stateDate = dateWeekdayAbbrevCommaDash + break iterRunes } case dateAlphaWs: @@ -687,10 +652,7 @@ iterRunes: // Mon Aug 10 15:44:11 UTC+0100 2015 // dateAlphaWsDigit // May 8, 2009 5:57:51 PM - u.Infof("dateAlphaWs %s %d", string(r), i) switch { - // case r == ' ': - // p.part2Len = i - p.part1Len case unicode.IsLetter(r): p.set(0, "Mon") p.stateDate = dateAlphaWsAlpha @@ -700,32 +662,22 @@ iterRunes: p.set(0, "Jan") p.stateDate = dateAlphaWsDigit p.dayi = i - default: - u.Warnf("what is this? case r=%s", string(r)) } case dateAlphaWsDigit: // dateAlphaWsDigit // May 8, 2009 5:57:51 PM - switch { - case r == ',': + if r == ',' { p.daylen = i - p.dayi p.setDay() p.stateDate = dateAlphaWsDigitComma - case unicode.IsDigit(r): - p.stateDate = dateAlphaWsDigit - u.Warnf("wtf not possible? %s", datestr) } case dateAlphaWsDigitComma: // x // May 8, 2009 5:57:51 PM - switch { - case r == ' ': + if r == ' ' { p.stateDate = dateAlphaWsDigitCommaWs p.yeari = i + 1 - default: - u.Warnf("hm, can we drop a case here? %v", string(r)) - return time.Time{}, fmt.Errorf("could not find format for %v expected white-space after comma", datestr) } case dateAlphaWsDigitCommaWs: // x @@ -735,7 +687,6 @@ iterRunes: p.yearlen = i - p.yeari p.setYear() p.stateTime = timeStart - u.Debugf("here we are %s i=%d yeari=%d yearlen=%d", string(r), i, p.yeari, p.yearlen) break iterRunes } @@ -745,13 +696,10 @@ iterRunes: // Mon Jan _2 15:04:05 MST 2006 // Mon Aug 10 15:44:11 UTC+0100 2015 // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - u.Debugf("dateAlphaWsAlpha i=%d r=%s", i, string(r)) if r == ' ' { if p.dayi > 0 { p.daylen = i - p.dayi - u.Warnf("%s dayi=%v daylen=%v format=%s", datestr, p.dayi, p.daylen, string(p.format)) p.setDay() - u.Warnf("%s", string(p.format)) p.yeari = i + 1 p.stateDate = dateAlphaWsAlphaYearmaybe p.stateTime = timeStart @@ -759,14 +707,12 @@ iterRunes: } else if unicode.IsDigit(r) { if p.dayi == 0 { p.dayi = i - u.Warnf("found dayi %d", p.dayi) } } case dateAlphaWsAlphaYearmaybe: // x // Mon Jan _2 15:04:05 2006 // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - u.Warnf("%s i=%d yeari+3=%d", string(r), i, p.yeari+4) if r == ':' { i = i - 3 p.stateDate = dateAlphaWsAlpha @@ -775,45 +721,15 @@ iterRunes: } else if r == ' ' { // must be year format, not 15:04 p.yearlen = i - p.yeari - u.Infof("yeari=%d yearlen=%d", p.yeari, p.yearlen) p.setYear() - u.Debugf("format=%s", string(p.format)) break iterRunes } - /* - case dateAlphaWSAlphaColon: - // Mon Jan _2 15:04:05 2006 - // Mon Jan 02 15:04:05 -0700 2006 - // Mon Jan _2 15:04:05 MST 2006 - // Mon Aug 10 15:44:11 UTC+0100 2015 - // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - if unicode.IsLetter(r) { - p.stateDate = dateAlphaWSAlphaColonAlpha - } else if r == '-' || r == '+' { - p.stateDate = dateAlphaWSAlphaColonOffset - } - case dateAlphaWSAlphaColonAlpha: - // Mon Jan _2 15:04:05 MST 2006 - // Mon Aug 10 15:44:11 UTC+0100 2015 - // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - if r == '+' { - p.stateDate = dateAlphaWSAlphaColonAlphaOffset - } - case dateAlphaWSAlphaColonAlphaOffset: - // Mon Aug 10 15:44:11 UTC+0100 2015 - // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - if unicode.IsLetter(r) { - p.stateDate = dateAlphaWSAlphaColonAlphaOffsetAlpha - } - */ default: break iterRunes } } - u.Warnf("%s", string(p.format)) p.coalesceDate(i) - u.Warnf("%s", string(p.format)) if p.stateTime == timeStart { // increment first one, since the i++ occurs at end of loop i++ @@ -822,10 +738,10 @@ iterRunes: for ; i < len(datestr); i++ { r := rune(datestr[i]) - u.Debugf("i=%d r=%s timeState=%d hi=%d hl=%d", i, string(r), p.stateTime, p.houri, p.hourlen) switch p.stateTime { case timeStart: // 22:43:22 + // 22:43 // timeComma // 08:20:13,787 // timeWs @@ -889,13 +805,12 @@ iterRunes: p.seclen = i - p.seci } case ' ': + p.coalesceTime(i) p.stateTime = timeWs - p.seclen = i - p.seci case ':': if p.mini == 0 { p.mini = i + 1 p.hourlen = i - p.houri - u.Infof("hour i=%v l=%d", p.houri, p.hourlen) } else if p.seci == 0 { p.seci = i + 1 p.minlen = i - p.mini @@ -929,7 +844,6 @@ iterRunes: // 00:12:00 2008 // timeZ // 15:04:05.99Z - u.Debugf("timeWs") switch r { case 'A', 'P': // Could be AM/PM or could be PST or similar @@ -959,13 +873,11 @@ iterRunes: // 18:04:07 GMT+0100 (GMT Daylight Time) // timeWsAlphaZoneOffsetWsYear // 15:44:11 UTC+0100 2015 - u.Debugf("timeWsAlpha") switch r { case '+', '-': p.tzlen = i - p.tzi - u.Infof("tzi=%d tzlen=%d", p.tzi, p.tzlen) if p.tzlen == 4 { - p.set(p.tzi, "MST ") + p.set(p.tzi, " MST") } else if p.tzlen == 3 { p.set(p.tzi, "MST") } @@ -979,7 +891,6 @@ iterRunes: // 18:04:07 GMT+0100 (GMT Daylight Time) // timeWsAlphaZoneOffsetWsYear // 15:44:11 UTC+0100 2015 - u.Debugf("timeWsAlphaZoneOffset") switch r { case ' ': p.set(p.offseti, "-0700") @@ -992,13 +903,11 @@ iterRunes: // 18:04:07 GMT+0100 (GMT Daylight Time) // timeWsAlphaZoneOffsetWsYear // 15:44:11 UTC+0100 2015 - u.Debugf("timeWsAlphaZoneOffsetWs") if unicode.IsDigit(r) { p.stateTime = timeWsAlphaZoneOffsetWsYear } else { p.extra = i - 1 p.stateTime = timeWsAlphaZoneOffsetWsExtra - u.Warnf("going to chop some stuff off %d", p.extra) } case timeWsAlphaZoneOffsetWsYear: // 15:44:11 UTC+0100 2015 @@ -1008,7 +917,6 @@ iterRunes: p.setYear() } } - u.Debugf("timeWsAlphaZoneOffsetWsYear yeari=%d yearlen=%d", p.yeari, p.yearlen) case timeWsAMPMMaybe: // timeWsAMPMMaybe // timeWsAMPM @@ -1020,6 +928,11 @@ iterRunes: //return parse("2006-01-02 03:04:05 PM", datestr, loc) p.stateTime = timeWsAMPM p.set(i-1, "PM") + if p.hourlen == 2 { + p.set(p.houri, "03") + } else if p.hourlen == 1 { + p.set(p.houri, "3") + } } else { p.stateTime = timeWsAlpha } @@ -1104,16 +1017,8 @@ iterRunes: // 19:55:00.799+0100 // timePeriodOffsetColon // 15:04:05.999-07:00 - switch r { - case ':': + if r == ':' { p.stateTime = timePeriodOffsetColon - default: - if unicode.IsLetter(r) { - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - p.stateTime = timePeriodWsOffsetWsAlpha - break iterTimeRunes - } } case timePeriodOffsetColon: // timePeriodOffset @@ -1160,8 +1065,6 @@ iterRunes: switch r { case ' ': p.set(p.offseti, "-0700") - case ':': - u.Errorf("timePeriodWsOffset UNHANDLED COLON") default: if unicode.IsLetter(r) { // 00:07:31.945167 +0000 UTC @@ -1186,34 +1089,23 @@ iterRunes: } } - u.Debugf("timestate = %d", p.stateTime) switch p.stateTime { case timeWsAlpha: - u.Warnf("timeWsAlpha?") case timeWsYear: p.yearlen = i - p.yeari - u.Infof("got WsYear yeari=%d yearlen=%d", p.yeari, p.yearlen) p.setYear() case timeWsAlphaZoneOffsetWsExtra: - u.Warnf("%s", string(p.format)) p.trimExtra() - u.Warnf("%s", string(p.format)) case timePeriod: p.mslen = i - p.msi case timeOffset: // 19:55:00+0100 - u.Warnf("offset?") p.set(p.offseti, "-0700") case timeWsOffset: - u.Warnf("timeWsOffset?") p.set(p.offseti, "-0700") case timeOffsetColon: // 15:04:05+07:00 p.set(p.offseti, "-07:00") - // case timeZ: - // u.Warnf("wtf? timeZ") - // case timeZDigit: - // u.Warnf("got timeZDigit Z00:00") case timePeriodOffset: // 19:55:00.799+0100 p.set(p.offseti, "-0700") @@ -1221,20 +1113,10 @@ iterRunes: p.set(p.offseti, "-07:00") case timePeriodWsOffset: p.set(p.offseti, "-0700") - // case timePeriodWsOffsetWsAlpha: - // u.Warnf("timePeriodWsOffsetAlpha") - // case timeWsOffsetAlpha: - // u.Warnf("timeWsOffsetAlpha offseti=%d", p.offseti) - default: - //u.Warnf("un-handled statetime: %d for %v", p.stateTime, p.datestr) } - u.Warnf("%s", string(p.format)) p.coalesceTime(i) - u.Warnf("parse: %q AS %q", p.datestr, string(p.format)) } - //u.Infof("%60s %q\n\t%+v", datestr, string(p.format), p) - switch p.stateDate { case dateDigit: // unixy timestamps ish @@ -1314,10 +1196,6 @@ iterRunes: p.yearlen = i - p.yeari return p.parse() - case dateDigitWs: - u.Warnf("what? %s", datestr) - return p.parse() - case dateDigitWsMoYear: // 2 Jan 2018 23:59 // 02 Jan 2018 23:59 @@ -1342,36 +1220,7 @@ iterRunes: case dateAlphaWsAlphaYearmaybe: return p.parse() - case dateAlphaWsDigitCommaWs: - return p.parse() - /* - case dateAlphaWsAlphaWs: - case dateAlphaWSAlphaColon: - // Mon Jan _2 15:04:05 2006 - return parse(time.ANSIC, datestr, loc) - - case dateAlphaWSAlphaColonOffset: - // Mon Jan 02 15:04:05 -0700 2006 - return parse(time.RubyDate, datestr, loc) - - case dateAlphaWSAlphaColonAlpha: - // Mon Jan _2 15:04:05 MST 2006 - return parse(time.UnixDate, datestr, loc) - - case dateAlphaWSAlphaColonAlphaOffset: - // Mon Aug 10 15:44:11 UTC+0100 2015 - return parse("Mon Jan 02 15:04:05 MST-0700 2006", datestr, loc) - - case dateAlphaWSAlphaColonAlphaOffsetAlpha: - // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - if len(datestr) > len("Mon Jan 02 2006 15:04:05 MST-0700") { - // What effing time stamp is this? - // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - dateTmp := datestr[:33] - return parse("Mon Jan 02 2006 15:04:05 MST-0700", dateTmp, loc) - } - */ case dateDigitSlash: // starts digit then slash 02/ (but nothing else) // 3/1/2014 // 10/13/2014 @@ -1379,39 +1228,6 @@ iterRunes: // 2014/10/13 return p.parse() - case dateDigitSlashWsColon: - // 4/8/2014 22:05 - // 04/08/2014 22:05 - // 2014/4/8 22:05 - // 2014/04/08 22:05 - - return p.parse() - - case dateDigitSlashWsColonAMPM: - // 4/8/2014 22:05 PM - // 04/08/2014 22:05 PM - // 04/08/2014 1:05 PM - // 2014/4/8 22:05 PM - // 2014/04/08 22:05 PM - return p.parse() - - case dateDigitSlashWsColonColon: - // 2014/07/10 06:55:38.156283 - // 03/19/2012 10:11:59 - // 3/1/2012 10:11:59 - // 03/1/2012 10:11:59 - // 3/01/2012 10:11:59 - // 4/8/14 22:05 - return p.parse() - - case dateDigitSlashWsColonColonAMPM: - // 2014/07/10 06:55:38.156283 PM - // 03/19/2012 10:11:59 PM - // 3/1/2012 10:11:59 PM - // 03/1/2012 10:11:59 PM - // 3/01/2012 10:11:59 PM - return p.parse() - case dateDigitChineseYear: // dateDigitChineseYear // 2014年04月08日 @@ -1426,10 +1242,6 @@ iterRunes: // Monday, 02-Jan-06 15:04:05 MST return p.parse() - // case dateWeekdayCommaDash: - // - // return p.parse() - case dateWeekdayAbbrevComma: // Starts alpha then comma // Mon, 02-Jan-06 15:04:05 MST // Mon, 02 Jan 2006 15:04:05 MST @@ -1440,20 +1252,7 @@ iterRunes: // Mon, 2-Jan-06 15:04:05 MST return p.parse() - case dateWeekdayAbbrevCommaOffset: - // Mon, 02 Jan 2006 15:04:05 -0700 - // Thu, 13 Jul 2017 08:58:40 +0100 - // RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone - // - // Thu, 4 Jan 2018 17:53:36 +0000 - return p.parse() - - case dateWeekdayAbbrevCommaOffsetZone: - // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) - return p.parse() } - //u.Warnf("no format for %d %d %s", p.stateDate, p.stateTime, p.datestr) - return time.Time{}, fmt.Errorf("Could not find date format for %s", datestr) } diff --git a/parseany_test.go b/parseany_test.go index 692fe56..8812d2c 100644 --- a/parseany_test.go +++ b/parseany_test.go @@ -184,14 +184,16 @@ func TestParse(t *testing.T) { assert.Equal(t, "2015-08-10 15:44:11 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) ts = MustParse("Mon Aug 10 15:44:11 PST-0700 2015") assert.Equal(t, "2015-08-10 22:44:11 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) + ts = MustParse("Mon Aug 10 15:44:11 CEST+0200 2015") + assert.Equal(t, "2015-08-10 13:44:11 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) // Easily the worst Date format I have ever seen // "Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)" ts = MustParse("Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)") assert.Equal(t, "2015-07-03 17:04:07 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("Mon, 02 Jan 2006 15:04:05 MST") - assert.Equal(t, "2006-01-02 15:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) + ts = MustParse("Fri, 03 Jul 2015 13:04:07 MST") + assert.Equal(t, "2015-07-03 13:04:07 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) ts = MustParse("Mon, 2 Jan 2006 15:4:05 MST") assert.Equal(t, "2006-01-02 15:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) @@ -672,5 +674,6 @@ func TestPStruct(t *testing.T) { p.set(15, "not") assert.Equal(t, "08.21.71", p.datestr) assert.Equal(t, "08.21.71", string(p.format)) - + assert.True(t, len(p.ds()) > 0) + assert.True(t, len(p.ts()) > 0) } From 62e766bf9e640c42529b89aac2f8744303435c8a Mon Sep 17 00:00:00 2001 From: Aaron Raddon Date: Sat, 10 Mar 2018 13:05:55 -0800 Subject: [PATCH 06/10] easier parsing of comma milliseconds --- parseany.go | 18 +++++++----------- parseany_test.go | 4 ++-- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/parseany.go b/parseany.go index 3d505df..60343a6 100644 --- a/parseany.go +++ b/parseany.go @@ -777,17 +777,12 @@ iterRunes: } switch r { case ',': - if len(datestr) == len("2014-05-11 08:20:13,787") { - // go doesn't seem to parse this one natively? or did i miss it? - t, err := parse("2006-01-02 03:04:05", datestr[:i], loc) - if err == nil { - ms, err := strconv.Atoi(datestr[i+1:]) - if err == nil { - return time.Unix(0, t.UnixNano()+int64(ms)*1e6), nil - } - } - return t, err - } + // hm, lets just swap out comma for period. for some reason go + // won't parse it. + // 2014-05-11 08:20:13,787 + ds := []byte(p.datestr) + ds[i] = '.' + return parseTime(string(ds), loc) case '-', '+': // 03:21:51+00:00 p.stateTime = timeOffset @@ -1203,6 +1198,7 @@ iterRunes: // 12 Feb 2006, 19:17 // 12 Feb 2006, 19:17:22 return p.parse() + case dateDigitWsMolong: // 18 January 2018 // 8 January 2018 diff --git a/parseany_test.go b/parseany_test.go index 8812d2c..7e7ac30 100644 --- a/parseany_test.go +++ b/parseany_test.go @@ -116,8 +116,8 @@ func TestInLocation(t *testing.T) { func TestOne(t *testing.T) { time.Local = time.UTC var ts time.Time - ts = MustParse("Mon Aug 10 15:44:11 PST-0700 2015") - assert.Equal(t, "2015-08-10 22:44:11 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) + ts = MustParse("2014-05-11 08:20:13,787") + assert.Equal(t, "2014-05-11 08:20:13.787 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) } func TestParse(t *testing.T) { From a653ec716571b7a60f72740cb8c1e09a78bd211f Mon Sep 17 00:00:00 2001 From: Aaron Raddon Date: Sat, 10 Mar 2018 13:43:16 -0800 Subject: [PATCH 07/10] More date formats closes #46 --- parseany.go | 45 ++++++++++++++++++++++++++++++++++++--------- parseany_test.go | 18 ++++++++++++++++++ 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/parseany.go b/parseany.go index 60343a6..eaec7d0 100644 --- a/parseany.go +++ b/parseany.go @@ -278,6 +278,7 @@ func (p *parser) parse() (time.Time, error) { p.format = p.format[p.skip:] p.datestr = p.datestr[p.skip:] } + //gou.Debugf("parse %q AS %s", p.datestr, string(p.format)) if p.loc == nil { return time.Parse(string(p.format), p.datestr) } @@ -350,6 +351,7 @@ iterRunes: case ' ': // 18 January 2018 // 8 January 2018 + // 8 jan 2018 // 02 Jan 2018 23:59 // 02 Jan 2018 23:59:34 // 12 Feb 2006, 19:17 @@ -486,6 +488,8 @@ iterRunes: case dateDigitWs: // 18 January 2018 // 8 January 2018 + // 8 jan 2018 + // 1 jan 18 // 02 Jan 2018 23:59 // 02 Jan 2018 23:59:34 // 12 Feb 2006, 19:17 @@ -493,7 +497,7 @@ iterRunes: switch r { case ' ': p.yeari = i + 1 - p.yearlen = 4 + //p.yearlen = 4 p.dayi = 0 p.daylen = p.part1Len p.setDay() @@ -509,6 +513,7 @@ iterRunes: } case dateDigitWsMoYear: + // 8 jan 2018 // 02 Jan 2018 23:59 // 02 Jan 2018 23:59:34 // 12 Feb 2006, 19:17 @@ -552,6 +557,7 @@ iterRunes: // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) // dateAlphaWSDigit // May 8, 2009 5:57:51 PM + // oct 1, 1970 // // dateWeekdayComma // Monday, 02 Jan 2006 15:04:05 MST @@ -652,6 +658,8 @@ iterRunes: // Mon Aug 10 15:44:11 UTC+0100 2015 // dateAlphaWsDigit // May 8, 2009 5:57:51 PM + // oct 1, 1970 + // oct 7, '70 switch { case unicode.IsLetter(r): p.set(0, "Mon") @@ -665,24 +673,33 @@ iterRunes: } case dateAlphaWsDigit: - // dateAlphaWsDigit - // May 8, 2009 5:57:51 PM + // May 8, 2009 5:57:51 PM + // oct 1, 1970 + // oct 7, '70 + //gou.Debugf("%d %s dateAlphaWsDigit %s %s", i, string(r), p.ds(), p.ts()) if r == ',' { p.daylen = i - p.dayi p.setDay() p.stateDate = dateAlphaWsDigitComma } case dateAlphaWsDigitComma: - // x - // May 8, 2009 5:57:51 PM + // x + // May 8, 2009 5:57:51 PM + // oct 1, 1970 + // oct 7, '70 if r == ' ' { p.stateDate = dateAlphaWsDigitCommaWs p.yeari = i + 1 } case dateAlphaWsDigitCommaWs: - // x - // May 8, 2009 5:57:51 PM - if r == ' ' { + // x + // May 8, 2009 5:57:51 PM + // oct 1, 1970 + // oct 7, '70 + switch r { + case '\'': + p.yeari = i + 1 + case ' ': p.stateDate = dateAlphaWsDigitCommaWsYear p.yearlen = i - p.yeari p.setYear() @@ -732,7 +749,9 @@ iterRunes: p.coalesceDate(i) if p.stateTime == timeStart { // increment first one, since the i++ occurs at end of loop - i++ + if i < len(p.datestr) { + i++ + } iterTimeRunes: for ; i < len(datestr); i++ { @@ -1192,6 +1211,8 @@ iterRunes: return p.parse() case dateDigitWsMoYear: + // 2 Jan 2018 + // 2 Jan 18 // 2 Jan 2018 23:59 // 02 Jan 2018 23:59 // 02 Jan 2018 23:59:45 @@ -1207,6 +1228,12 @@ iterRunes: } return parse("2 January 2006", datestr, loc) + case dateAlphaWsDigitCommaWs: + // oct 1, 1970 + p.yearlen = i - p.yeari + p.setYear() + return p.parse() + case dateAlphaWsDigitCommaWsYear: // May 8, 2009 5:57:51 PM return p.parse() diff --git a/parseany_test.go b/parseany_test.go index 7e7ac30..904f649 100644 --- a/parseany_test.go +++ b/parseany_test.go @@ -119,6 +119,18 @@ func TestOne(t *testing.T) { ts = MustParse("2014-05-11 08:20:13,787") assert.Equal(t, "2014-05-11 08:20:13.787 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) } + +type dateTest struct { + in, out string +} + +var testInputs = []dateTest{ + {in: "oct 7, 1970", out: "1970-10-07 00:00:00 +0000 UTC"}, + {in: "oct 7, '70", out: "1970-10-07 00:00:00 +0000 UTC"}, + {in: "7 oct 70", out: "1970-10-07 00:00:00 +0000 UTC"}, + {in: "7 oct 1970", out: "1970-10-07 00:00:00 +0000 UTC"}, +} + func TestParse(t *testing.T) { /* @@ -138,6 +150,12 @@ func TestParse(t *testing.T) { assert.Equal(t, true, testDidPanic("NOT GONNA HAPPEN")) + for _, th := range testInputs { + ts = MustParse(th.in) + got := fmt.Sprintf("%v", ts.In(time.UTC)) + assert.Equal(t, th.out, got, "Expected %q but got %q from %q", th.out, got, th.in) + } + // TODO: Is a utf8 date valid? // ts = MustParse("2014-04\u221226") // assert.Equal(t, "2014-04-26 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) From c0c507ee08aee2f04ae617753590276cf52c3e9a Mon Sep 17 00:00:00 2001 From: Aaron Raddon Date: Sat, 10 Mar 2018 14:24:21 -0800 Subject: [PATCH 08/10] update readme for new formats --- README.md | 10 +++++++++- example/main.go | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a1d4bc6..26cbe7c 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,8 @@ import ( var examples = []string{ "May 8, 2009 5:57:51 PM", + "oct 7, 1970", + "oct 7, '70", "Mon Jan 2 15:04:05 2006", "Mon Jan 2 15:04:05 MST 2006", "Mon Jan 02 15:04:05 -0700 2006", @@ -74,6 +76,8 @@ var examples = []string{ "Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)", "12 Feb 2006, 19:17", "12 Feb 2006 19:17", + "7 oct 70", + "7 oct 1970", "03 February 2013", "2013-Feb-03", // mm/dd/yy @@ -176,6 +180,8 @@ func main() { | Input | Parsed, and Output as %v | +-------------------------------------------------------+----------------------------------------+ | 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, '70 | 1970-10-07 00:00:00 +0000 UTC | | Mon Jan 2 15:04:05 2006 | 2006-01-02 15:04:05 +0000 UTC | | Mon Jan 2 15:04:05 MST 2006 | 2006-01-02 15:04:05 +0000 MST | | Mon Jan 02 15:04:05 -0700 2006 | 2006-01-02 15:04:05 -0700 -0700 | @@ -188,6 +194,8 @@ func main() { | Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) | 2015-07-03 18:04:07 +0100 GMT | | 12 Feb 2006, 19:17 | 2006-02-12 19:17:00 +0000 UTC | | 12 Feb 2006 19:17 | 2006-02-12 19:17:00 +0000 UTC | +| 7 oct 70 | 1970-10-07 00:00:00 +0000 UTC | +| 7 oct 1970 | 1970-10-07 00:00:00 +0000 UTC | | 03 February 2013 | 2013-02-03 00:00:00 +0000 UTC | | 2013-Feb-03 | 2013-02-03 00:00:00 +0000 UTC | | 3/31/2014 | 2014-03-31 00:00:00 +0000 UTC | @@ -228,7 +236,7 @@ func main() { | 2014-12-16 06:20:00 GMT | 2014-12-16 06:20:00 +0000 UTC | | 2014-04-26 05:24:37 PM | 2014-04-26 17:24:37 +0000 UTC | | 2014-04-26 13:13:43 +0800 | 2014-04-26 13:13:43 +0800 +0800 | -| 2014-04-26 13:13:44 +09:00 | 2014-04-26 13:13:44 +0900 +0900 | +| 2014-04-26 13:13:44 +09:00 | 2014-04-26 13:13:44 +0000 UTC | | 2012-08-03 18:31:59.257000000 +0000 UTC | 2012-08-03 18:31:59.257 +0000 UTC | | 2015-09-30 18:48:56.35272715 +0000 UTC | 2015-09-30 18:48:56.35272715 +0000 UTC | | 2015-02-18 00:12:00 +0000 GMT | 2015-02-18 00:12:00 +0000 UTC | diff --git a/example/main.go b/example/main.go index fd70f0a..4e10c9e 100644 --- a/example/main.go +++ b/example/main.go @@ -11,6 +11,8 @@ import ( var examples = []string{ "May 8, 2009 5:57:51 PM", + "oct 7, 1970", + "oct 7, '70", "Mon Jan 2 15:04:05 2006", "Mon Jan 2 15:04:05 MST 2006", "Mon Jan 02 15:04:05 -0700 2006", @@ -23,6 +25,8 @@ var examples = []string{ "Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)", "12 Feb 2006, 19:17", "12 Feb 2006 19:17", + "7 oct 70", + "7 oct 1970", "03 February 2013", "2013-Feb-03", // mm/dd/yy @@ -125,6 +129,8 @@ func main() { | Input | Parsed, and Output as %v | +-------------------------------------------------------+----------------------------------------+ | 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, '70 | 1970-10-07 00:00:00 +0000 UTC | | Mon Jan 2 15:04:05 2006 | 2006-01-02 15:04:05 +0000 UTC | | Mon Jan 2 15:04:05 MST 2006 | 2006-01-02 15:04:05 +0000 MST | | Mon Jan 02 15:04:05 -0700 2006 | 2006-01-02 15:04:05 -0700 -0700 | @@ -137,6 +143,8 @@ func main() { | Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) | 2015-07-03 18:04:07 +0100 GMT | | 12 Feb 2006, 19:17 | 2006-02-12 19:17:00 +0000 UTC | | 12 Feb 2006 19:17 | 2006-02-12 19:17:00 +0000 UTC | +| 7 oct 70 | 1970-10-07 00:00:00 +0000 UTC | +| 7 oct 1970 | 1970-10-07 00:00:00 +0000 UTC | | 03 February 2013 | 2013-02-03 00:00:00 +0000 UTC | | 2013-Feb-03 | 2013-02-03 00:00:00 +0000 UTC | | 3/31/2014 | 2014-03-31 00:00:00 +0000 UTC | @@ -177,7 +185,7 @@ func main() { | 2014-12-16 06:20:00 GMT | 2014-12-16 06:20:00 +0000 UTC | | 2014-04-26 05:24:37 PM | 2014-04-26 17:24:37 +0000 UTC | | 2014-04-26 13:13:43 +0800 | 2014-04-26 13:13:43 +0800 +0800 | -| 2014-04-26 13:13:44 +09:00 | 2014-04-26 13:13:44 +0900 +0900 | +| 2014-04-26 13:13:44 +09:00 | 2014-04-26 13:13:44 +0000 UTC | | 2012-08-03 18:31:59.257000000 +0000 UTC | 2012-08-03 18:31:59.257 +0000 UTC | | 2015-09-30 18:48:56.35272715 +0000 UTC | 2015-09-30 18:48:56.35272715 +0000 UTC | | 2015-02-18 00:12:00 +0000 GMT | 2015-02-18 00:12:00 +0000 UTC | From 7b436dd68b7f2d04703b2fe8beb8ad32e0aea96c Mon Sep 17 00:00:00 2001 From: Aaron Raddon Date: Sun, 11 Mar 2018 14:08:21 -0700 Subject: [PATCH 09/10] test-harness --- parseany.go | 26 ++++++++++++++++++++++++-- parseany_test.go | 47 +++++++++++++++++------------------------------ 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/parseany.go b/parseany.go index eaec7d0..ffa713a 100644 --- a/parseany.go +++ b/parseany.go @@ -9,8 +9,15 @@ import ( "time" "unicode" "unicode/utf8" + + "github.com/araddon/gou" ) +func init() { + gou.SetupLogging("debug") + gou.SetColorOutput() +} + type dateState uint8 type timeState uint8 @@ -55,6 +62,7 @@ const ( timeWsAMPMMaybe timeWsAMPM timeWsOffset + timeWsOffsetWs timeWsOffsetAlpha timeWsOffsetColonAlpha timeWsOffsetColon @@ -278,7 +286,7 @@ func (p *parser) parse() (time.Time, error) { p.format = p.format[p.skip:] p.datestr = p.datestr[p.skip:] } - //gou.Debugf("parse %q AS %s", p.datestr, string(p.format)) + gou.Debugf("parse %q AS %s", p.datestr, string(p.format)) if p.loc == nil { return time.Parse(string(p.format), p.datestr) } @@ -715,11 +723,13 @@ iterRunes: // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) if r == ' ' { if p.dayi > 0 { + p.daylen = i - p.dayi p.setDay() p.yeari = i + 1 p.stateDate = dateAlphaWsAlphaYearmaybe p.stateTime = timeStart + gou.Infof("%d %s dateAlphaWsAlpha %s %s", i, string(r), p.ds(), p.ts()) } } else if unicode.IsDigit(r) { if p.dayi == 0 { @@ -734,6 +744,7 @@ iterRunes: i = i - 3 p.stateDate = dateAlphaWsAlpha p.yeari = 0 + gou.Warnf("hm, not year") break iterRunes } else if r == ' ' { // must be year format, not 15:04 @@ -757,6 +768,8 @@ iterRunes: for ; i < len(datestr); i++ { r := rune(datestr[i]) + gou.Debugf("%d %s iterTimeRunes %s %s", i, string(r), p.ds(), p.ts()) + switch p.stateTime { case timeStart: // 22:43:22 @@ -850,6 +863,7 @@ iterRunes: // 15:04:05 -0700 // timeWsOffsetColon // 15:04:05 -07:00 + // 17:57:51 -0700 2009 // timeWsOffsetColonAlpha // 00:12:00 +00:00 UTC // timeWsOffsetAlpha @@ -858,6 +872,7 @@ iterRunes: // 00:12:00 2008 // timeZ // 15:04:05.99Z + gou.Infof("timeWs") switch r { case 'A', 'P': // Could be AM/PM or could be PST or similar @@ -954,18 +969,22 @@ iterRunes: case timeWsOffset: // timeWsOffset // 15:04:05 -0700 + // timeWsOffsetWs + // 17:57:51 -0700 2009 // timeWsOffsetColon // 15:04:05 -07:00 // timeWsOffsetColonAlpha // 00:12:00 +00:00 UTC // timeWsOffsetAlpha // 00:12:00 +0000 UTC + gou.Infof("timeWsOffset") switch r { case ':': p.stateTime = timeWsOffsetColon case ' ': p.set(p.offseti, "-0700") - p.stateTime = timeWsOffset + gou.Warnf("end of offset?") + p.stateTime = timeWsOffsetWs default: if unicode.IsLetter(r) { // 00:12:00 +0000 UTC @@ -973,6 +992,7 @@ iterRunes: break iterTimeRunes } } + case timeWsOffsetWs: case timeWsOffsetColon: // timeWsOffsetColon @@ -1117,6 +1137,8 @@ iterRunes: p.set(p.offseti, "-0700") case timeWsOffset: p.set(p.offseti, "-0700") + case timeWsOffsetWs: + // 17:57:51 -0700 2009 case timeOffsetColon: // 15:04:05+07:00 p.set(p.offseti, "-07:00") diff --git a/parseany_test.go b/parseany_test.go index 904f649..b104b3f 100644 --- a/parseany_test.go +++ b/parseany_test.go @@ -124,22 +124,32 @@ type dateTest struct { in, out string } +// {in: , out: }, + var testInputs = []dateTest{ {in: "oct 7, 1970", out: "1970-10-07 00:00:00 +0000 UTC"}, {in: "oct 7, '70", out: "1970-10-07 00:00:00 +0000 UTC"}, + {in: "Oct 7, '70", out: "1970-10-07 00:00:00 +0000 UTC"}, + {in: "Feb 8, 2009 5:57:51 AM", out: "2009-02-08 05:57:51 +0000 UTC"}, + {in: "May 8, 2009 5:57:51 PM", out: "2009-05-08 17:57:51 +0000 UTC"}, + {in: "May 8, 2009 5:57:1 PM", out: "2009-05-08 17:57:01 +0000 UTC"}, + {in: "May 8, 2009 5:7:51 PM", out: "2009-05-08 17:07:51 +0000 UTC"}, {in: "7 oct 70", out: "1970-10-07 00:00:00 +0000 UTC"}, {in: "7 oct 1970", out: "1970-10-07 00:00:00 +0000 UTC"}, + // ANSIC = "Mon Jan _2 15:04:05 2006" + {in: "Mon Jan 2 15:04:05 2006", out: "2006-01-02 15:04:05 +0000 UTC"}, + {in: "Thu May 8 17:57:51 2009", out: "2009-05-08 17:57:51 +0000 UTC"}, + {in: "Thu May 8 17:57:51 2009", out: "2009-05-08 17:57:51 +0000 UTC"}, + // RubyDate = "Mon Jan 02 15:04:05 -0700 2006" + {in: "Mon Jan 02 15:04:05 -0700 2006", out: "2006-01-02 22:04:05 +0000 UTC"}, + {in: "Thu May 08 17:57:51 -0700 2009", out: "2009-05-08 17:57:51 +0000 UTC"}, + // UnixDate = "Mon Jan _2 15:04:05 MST 2006" + {in: "Mon Jan 2 15:04:05 MST 2006", out: "2006-01-02 15:04:05 +0000 UTC"}, + {in: "Thu May 8 17:57:51 MST 2009", out: "2009-05-08 17:57:51 +0000 UTC"}, } func TestParse(t *testing.T) { - /* - TODO: - - move to testharness - - replace all the default go dates 2006 with others - - more tests on AM/PM zones, offsets for each variation - */ - // Lets ensure we are operating on UTC time.Local = time.UTC @@ -156,29 +166,6 @@ func TestParse(t *testing.T) { assert.Equal(t, th.out, got, "Expected %q but got %q from %q", th.out, got, th.in) } - // TODO: Is a utf8 date valid? - // ts = MustParse("2014-04\u221226") - // assert.Equal(t, "2014-04-26 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("May 8, 2009 5:57:51 PM") - assert.Equal(t, "2009-05-08 17:57:51 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("May 8, 2009 5:57:1 PM") - assert.Equal(t, "2009-05-08 17:57:01 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("May 8, 2009 5:7:51 PM") - assert.Equal(t, "2009-05-08 17:07:51 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - // ANSIC = "Mon Jan _2 15:04:05 2006" - ts = MustParse("Mon Jan 2 15:04:05 2006") - assert.Equal(t, "2006-01-02 15:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - // RubyDate = "Mon Jan 02 15:04:05 -0700 2006" - ts = MustParse("Mon Jan 02 15:04:05 -0700 2006") - assert.Equal(t, "2006-01-02 22:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC)), "%v") - - // UnixDate = "Mon Jan _2 15:04:05 MST 2006" - ts = MustParse("Mon Jan 2 15:04:05 MST 2006") - assert.Equal(t, "2006-01-02 15:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - // RFC850 = "Monday, 02-Jan-06 15:04:05 MST" ts = MustParse("Wednesday, 07-May-09 08:00:43 MST") assert.Equal(t, "2009-05-07 08:00:43 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) From 942c3ab04f938a73f26aa26b6b271634114583e3 Mon Sep 17 00:00:00 2001 From: Aaron Raddon Date: Sun, 11 Mar 2018 17:39:59 -0700 Subject: [PATCH 10/10] Refactor time/date parsing --- parseany.go | 150 +++++---- parseany_test.go | 777 ++++++++++++++++++----------------------------- 2 files changed, 392 insertions(+), 535 deletions(-) diff --git a/parseany.go b/parseany.go index ffa713a..5bf1fb2 100644 --- a/parseany.go +++ b/parseany.go @@ -9,15 +9,8 @@ import ( "time" "unicode" "unicode/utf8" - - "github.com/araddon/gou" ) -func init() { - gou.SetupLogging("debug") - gou.SetColorOutput() -} - type dateState uint8 type timeState uint8 @@ -47,14 +40,15 @@ const ( dateAlphaWsAlphaYearmaybe dateWeekdayComma dateWeekdayAbbrevComma - dateWeekdayAbbrevCommaDash + //dateWeekdayAbbrevCommaDash ) const ( // Time state timeIgnore timeState = iota timeStart timeWs - timeWsAlpha // 3 + timeWsAlpha + timeWsAlphaWs timeWsAlphaZoneOffset timeWsAlphaZoneOffsetWs timeWsAlphaZoneOffsetWsYear @@ -63,7 +57,6 @@ const ( timeWsAMPM timeWsOffset timeWsOffsetWs - timeWsOffsetAlpha timeWsOffsetColonAlpha timeWsOffsetColon timeWsYear @@ -73,11 +66,14 @@ const ( timePeriod timePeriodOffset timePeriodOffsetColon + timePeriodOffsetColonWs timePeriodWs timePeriodWsAlpha timePeriodWsOffset timePeriodWsOffsetWs timePeriodWsOffsetWsAlpha + timePeriodWsOffsetColon + timePeriodWsOffsetColonAlpha timeZ timeZDigit ) @@ -286,7 +282,7 @@ func (p *parser) parse() (time.Time, error) { p.format = p.format[p.skip:] p.datestr = p.datestr[p.skip:] } - gou.Debugf("parse %q AS %s", p.datestr, string(p.format)) + //gou.Debugf("parse %q AS %s", p.datestr, string(p.format)) if p.loc == nil { return time.Parse(string(p.format), p.datestr) } @@ -528,10 +524,14 @@ iterRunes: // 12 Feb 2006, 19:17:22 switch r { case ',': + p.yearlen = i - p.yeari + p.setYear() i++ break iterRunes - case ' ': - break iterRunes + // case ' ': + // p.yearlen = i - p.yeari + // p.setYear() + // break iterRunes } case dateDigitWsMolong: // 18 January 2018 @@ -577,8 +577,7 @@ iterRunes: // Mon, 02 Jan 2006 15:04:05 -0700 // Thu, 13 Jul 2017 08:58:40 +0100 // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) - // dateWeekdayAbbrevCommaDash - // Mon, 02-Jan-06 15:04:05 MST + // Mon, 02-Jan-06 15:04:05 MST switch { case r == ' ': @@ -623,7 +622,6 @@ iterRunes: p.stateTime = timeStart break iterRunes } - //case r == '-': } case dateWeekdayAbbrevComma: // Mon, 02 Jan 2006 15:04:05 MST @@ -631,10 +629,9 @@ iterRunes: // Thu, 13 Jul 2017 08:58:40 +0100 // Thu, 4 Jan 2018 17:53:36 +0000 // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) - // dateWeekdayAbbrevCommaDash - // Mon, 02-Jan-06 15:04:05 MST - switch { - case r == ' ': + // Mon, 02-Jan-06 15:04:05 MST + switch r { + case ' ', '-': if p.dayi == 0 { p.dayi = i + 1 } else if p.moi == 0 { @@ -651,10 +648,6 @@ iterRunes: p.stateTime = timeStart break iterRunes } - - case r == '-': - p.stateDate = dateWeekdayAbbrevCommaDash - break iterRunes } case dateAlphaWs: @@ -729,7 +722,6 @@ iterRunes: p.yeari = i + 1 p.stateDate = dateAlphaWsAlphaYearmaybe p.stateTime = timeStart - gou.Infof("%d %s dateAlphaWsAlpha %s %s", i, string(r), p.ds(), p.ts()) } } else if unicode.IsDigit(r) { if p.dayi == 0 { @@ -744,7 +736,6 @@ iterRunes: i = i - 3 p.stateDate = dateAlphaWsAlpha p.yeari = 0 - gou.Warnf("hm, not year") break iterRunes } else if r == ' ' { // must be year format, not 15:04 @@ -763,12 +754,19 @@ iterRunes: if i < len(p.datestr) { i++ } + // ensure we skip any whitespace prefix + for ; i < len(datestr); i++ { + r := rune(datestr[i]) + if r != ' ' { + break + } + } iterTimeRunes: for ; i < len(datestr); i++ { r := rune(datestr[i]) - gou.Debugf("%d %s iterTimeRunes %s %s", i, string(r), p.ds(), p.ts()) + //gou.Debugf("%d %s iterTimeRunes %s %s", i, string(r), p.ds(), p.ts()) switch p.stateTime { case timeStart: @@ -857,22 +855,21 @@ iterRunes: // 06:20:00 UTC // 15:44:11 UTC+0100 2015 // 18:04:07 GMT+0100 (GMT Daylight Time) + // 17:57:51 MST 2009 // timeWsAMPMMaybe // 05:24:37 PM // timeWsOffset // 15:04:05 -0700 + // 00:12:00 +0000 UTC // timeWsOffsetColon // 15:04:05 -07:00 // 17:57:51 -0700 2009 // timeWsOffsetColonAlpha // 00:12:00 +00:00 UTC - // timeWsOffsetAlpha - // 00:12:00 +0000 UTC // timeWsYear // 00:12:00 2008 // timeZ // 15:04:05.99Z - gou.Infof("timeWs") switch r { case 'A', 'P': // Could be AM/PM or could be PST or similar @@ -885,6 +882,7 @@ iterRunes: if unicode.IsLetter(r) { // 06:20:00 UTC // 15:44:11 UTC+0100 2015 + // 17:57:51 MST 2009 p.tzi = i p.stateTime = timeWsAlpha //break iterTimeRunes @@ -896,6 +894,8 @@ iterRunes: } case timeWsAlpha: // 06:20:00 UTC + // timeWsAlphaWs + // 17:57:51 MST 2009 // timeWsAlphaZoneOffset // timeWsAlphaZoneOffsetWs // timeWsAlphaZoneOffsetWsExtra @@ -912,7 +912,14 @@ iterRunes: } p.stateTime = timeWsAlphaZoneOffset p.offseti = i + case ' ': + // 17:57:51 MST 2009 + p.stateTime = timeWsAlphaWs + p.yeari = i + 1 } + case timeWsAlphaWs: + // 17:57:51 MST 2009 + case timeWsAlphaZoneOffset: // timeWsAlphaZoneOffset // timeWsAlphaZoneOffsetWs @@ -971,29 +978,32 @@ iterRunes: // 15:04:05 -0700 // timeWsOffsetWs // 17:57:51 -0700 2009 + // 00:12:00 +0000 UTC // timeWsOffsetColon // 15:04:05 -07:00 // timeWsOffsetColonAlpha // 00:12:00 +00:00 UTC - // timeWsOffsetAlpha - // 00:12:00 +0000 UTC - gou.Infof("timeWsOffset") switch r { case ':': p.stateTime = timeWsOffsetColon case ' ': p.set(p.offseti, "-0700") - gou.Warnf("end of offset?") + p.yeari = i + 1 p.stateTime = timeWsOffsetWs - default: - if unicode.IsLetter(r) { - // 00:12:00 +0000 UTC - p.stateTime = timeWsOffsetAlpha - break iterTimeRunes - } } case timeWsOffsetWs: - + // 17:57:51 -0700 2009 + // 00:12:00 +0000 UTC + if unicode.IsDigit(r) { + p.yearlen = i - p.yeari + 1 + if p.yearlen == 4 { + p.setYear() + } + } else if unicode.IsLetter(r) { + if p.tzi == 0 { + p.tzi = i + } + } case timeWsOffsetColon: // timeWsOffsetColon // 15:04:05 -07:00 @@ -1051,6 +1061,7 @@ iterRunes: // 19:55:00.799+0100 // timePeriodOffsetColon // 15:04:05.999-07:00 + // 13:31:51.999-07:00 MST if r == ':' { p.stateTime = timePeriodOffsetColon } @@ -1058,7 +1069,15 @@ iterRunes: // timePeriodOffset // timePeriodOffsetColon // 15:04:05.999-07:00 - + // 13:31:51.999 -07:00 MST + switch r { + case ' ': + p.set(p.offseti, "-07:00") + p.stateTime = timePeriodOffsetColonWs + p.tzi = i + 1 + } + case timePeriodOffsetColonWs: + // continue case timePeriodWs: // timePeriodWs // timePeriodWsOffset @@ -1067,6 +1086,8 @@ iterRunes: // timePeriodWsOffsetAlpha // 00:07:31.945167 +0000 UTC // 00:00:00.000 +0000 UTC + // timePeriodWsOffsetColon + // 13:31:51.999 -07:00 MST // timePeriodWsAlpha // 06:20:00.000 UTC if p.offseti == 0 { @@ -1094,9 +1115,13 @@ iterRunes: // timePeriodWsOffsetAlpha // 00:07:31.945167 +0000 UTC // 00:00:00.000 +0000 UTC + // timePeriodWsOffsetColon + // 13:31:51.999 -07:00 MST // timePeriodWsAlpha // 06:20:00.000 UTC switch r { + case ':': + p.stateTime = timePeriodWsOffsetColon case ' ': p.set(p.offseti, "-0700") default: @@ -1108,6 +1133,20 @@ iterRunes: } } + case timePeriodWsOffsetColon: + // 13:31:51.999 -07:00 MST + switch r { + case ' ': + p.set(p.offseti, "-07:00") + default: + if unicode.IsLetter(r) { + // 13:31:51.999 -07:00 MST + p.tzi = i + p.stateTime = timePeriodWsOffsetColonAlpha + } + } + case timePeriodWsOffsetColonAlpha: + // continue case timeZ: // timeZ // 15:04:05.99Z @@ -1124,7 +1163,9 @@ iterRunes: } switch p.stateTime { - case timeWsAlpha: + case timeWsAlphaWs: + p.yearlen = i - p.yeari + p.setYear() case timeWsYear: p.yearlen = i - p.yeari p.setYear() @@ -1138,7 +1179,11 @@ iterRunes: case timeWsOffset: p.set(p.offseti, "-0700") case timeWsOffsetWs: - // 17:57:51 -0700 2009 + // 17:57:51 -0700 2009 + // 00:12:00 +0000 UTC + case timeWsOffsetColon: + // 17:57:51 -07:00 + p.set(p.offseti, "-07:00") case timeOffsetColon: // 15:04:05+07:00 p.set(p.offseti, "-07:00") @@ -1147,6 +1192,14 @@ iterRunes: p.set(p.offseti, "-0700") case timePeriodOffsetColon: p.set(p.offseti, "-07:00") + case timePeriodWsOffsetColonAlpha: + p.tzlen = i - p.tzi + switch p.tzlen { + case 3: + p.set(p.tzi, "MST") + case 4: + p.set(p.tzi, "MST ") + } case timePeriodWsOffset: p.set(p.offseti, "-0700") } @@ -1266,7 +1319,7 @@ iterRunes: case dateAlphaWsAlphaYearmaybe: return p.parse() - case dateDigitSlash: // starts digit then slash 02/ (but nothing else) + case dateDigitSlash: // 3/1/2014 // 10/13/2014 // 01/02/2006 @@ -1287,16 +1340,11 @@ iterRunes: // Monday, 02-Jan-06 15:04:05 MST return p.parse() - case dateWeekdayAbbrevComma: // Starts alpha then comma + case dateWeekdayAbbrevComma: // Mon, 02-Jan-06 15:04:05 MST // Mon, 02 Jan 2006 15:04:05 MST return p.parse() - case dateWeekdayAbbrevCommaDash: - // Mon, 02-Jan-06 15:04:05 MST - // Mon, 2-Jan-06 15:04:05 MST - return p.parse() - } return time.Time{}, fmt.Errorf("Could not find date format for %s", datestr) diff --git a/parseany_test.go b/parseany_test.go index b104b3f..f97490b 100644 --- a/parseany_test.go +++ b/parseany_test.go @@ -121,7 +121,7 @@ func TestOne(t *testing.T) { } type dateTest struct { - in, out string + in, out, loc string } // {in: , out: }, @@ -142,12 +142,275 @@ var testInputs = []dateTest{ {in: "Thu May 8 17:57:51 2009", out: "2009-05-08 17:57:51 +0000 UTC"}, // RubyDate = "Mon Jan 02 15:04:05 -0700 2006" {in: "Mon Jan 02 15:04:05 -0700 2006", out: "2006-01-02 22:04:05 +0000 UTC"}, - {in: "Thu May 08 17:57:51 -0700 2009", out: "2009-05-08 17:57:51 +0000 UTC"}, + {in: "Thu May 08 11:57:51 -0700 2009", out: "2009-05-08 18:57:51 +0000 UTC"}, // UnixDate = "Mon Jan _2 15:04:05 MST 2006" {in: "Mon Jan 2 15:04:05 MST 2006", out: "2006-01-02 15:04:05 +0000 UTC"}, {in: "Thu May 8 17:57:51 MST 2009", out: "2009-05-08 17:57:51 +0000 UTC"}, + {in: "Thu May 8 17:57:51 PST 2009", out: "2009-05-08 17:57:51 +0000 UTC"}, + {in: "Thu May 08 17:57:51 PST 2009", out: "2009-05-08 17:57:51 +0000 UTC"}, + {in: "Thu May 08 05:05:07 PST 2009", out: "2009-05-08 05:05:07 +0000 UTC"}, + {in: "Thu May 08 5:5:7 PST 2009", out: "2009-05-08 05:05:07 +0000 UTC"}, + // ?? + {in: "Mon Aug 10 15:44:11 UTC+0000 2015", out: "2015-08-10 15:44:11 +0000 UTC"}, + {in: "Mon Aug 10 15:44:11 PST-0700 2015", out: "2015-08-10 22:44:11 +0000 UTC"}, + {in: "Mon Aug 10 15:44:11 CEST+0200 2015", out: "2015-08-10 13:44:11 +0000 UTC"}, + {in: "Mon Aug 1 15:44:11 CEST+0200 2015", out: "2015-08-01 13:44:11 +0000 UTC"}, + {in: "Mon Aug 1 5:44:11 CEST+0200 2015", out: "2015-08-01 03:44:11 +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 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 PST-0700 (Pacific Daylight Time)", out: "2015-07-03 13:04:07 +0000 UTC"}, + // ? + {in: "Fri, 03 Jul 2015 08:08:08 MST", out: "2015-07-03 08:08:08 +0000 UTC"}, + {in: "Fri, 03 Jul 2015 08:08:08 PST", out: "2015-07-03 15:08:08 +0000 UTC", loc: "America/Los_Angeles"}, + {in: "Fri, 03 Jul 2015 08:08:08 PST", out: "2015-07-03 08:08:08 +0000 UTC"}, + {in: "Fri, 3 Jul 2015 08:08:08 MST", out: "2015-07-03 08:08:08 +0000 UTC"}, + {in: "Fri, 03 Jul 2015 8:08:08 MST", out: "2015-07-03 08:08:08 +0000 UTC"}, + {in: "Fri, 03 Jul 2015 8:8:8 MST", out: "2015-07-03 08:08:08 +0000 UTC"}, + // ? + {in: "Thu, 03 Jul 2017 08:08:04 +0100", out: "2017-07-03 07:08:04 +0000 UTC"}, + {in: "Thu, 03 Jul 2017 08:08:04 -0100", out: "2017-07-03 09:08:04 +0000 UTC"}, + {in: "Thu, 3 Jul 2017 08:08:04 +0100", out: "2017-07-03 07:08:04 +0000 UTC"}, + {in: "Thu, 03 Jul 2017 8:08:04 +0100", out: "2017-07-03 07:08:04 +0000 UTC"}, + {in: "Thu, 03 Jul 2017 8:8:4 +0100", out: "2017-07-03 07:08:04 +0000 UTC"}, + // + {in: "Tue, 11 Jul 2017 04:08:03 +0200 (CEST)", out: "2017-07-11 02:08:03 +0000 UTC"}, + {in: "Tue, 5 Jul 2017 04:08:03 -0700 (CEST)", out: "2017-07-05 11:08:03 +0000 UTC"}, + {in: "Tue, 11 Jul 2017 04:08:03 +0200 (CEST)", out: "2017-07-11 02:08:03 +0000 UTC", loc: "Europe/Berlin"}, + // day, dd-Mon-yy hh:mm:zz TZ + {in: "Fri, 03-Jul-15 08:08:08 MST", out: "2015-07-03 08:08:08 +0000 UTC"}, + {in: "Fri, 03-Jul-15 08:08:08 PST", out: "2015-07-03 15:08:08 +0000 UTC", loc: "America/Los_Angeles"}, + {in: "Fri, 03-Jul 2015 08:08:08 PST", out: "2015-07-03 08:08:08 +0000 UTC"}, + {in: "Fri, 3-Jul-15 08:08:08 MST", out: "2015-07-03 08:08:08 +0000 UTC"}, + {in: "Fri, 03-Jul-15 8:08:08 MST", out: "2015-07-03 08:08:08 +0000 UTC"}, + {in: "Fri, 03-Jul-15 8:8:8 MST", out: "2015-07-03 08:08:08 +0000 UTC"}, + // RFC850 = "Monday, 02-Jan-06 15:04:05 MST" + {in: "Wednesday, 07-May-09 08:00:43 MST", out: "2009-05-07 08:00:43 +0000 UTC"}, + {in: "Wednesday, 28-Feb-18 09:01:00 MST", out: "2018-02-28 09:01:00 +0000 UTC"}, + {in: "Wednesday, 28-Feb-18 09:01:00 MST", out: "2018-02-28 16:01:00 +0000 UTC", loc: "America/Denver"}, + // with offset then with variations on non-zero filled stuff + {in: "Monday, 02 Jan 2006 15:04:05 +0100", out: "2006-01-02 14:04:05 +0000 UTC"}, + {in: "Wednesday, 28 Feb 2018 09:01:00 -0300", out: "2018-02-28 12:01:00 +0000 UTC"}, + {in: "Wednesday, 2 Feb 2018 09:01:00 -0300", out: "2018-02-02 12:01:00 +0000 UTC"}, + {in: "Wednesday, 2 Feb 2018 9:01:00 -0300", out: "2018-02-02 12:01:00 +0000 UTC"}, + {in: "Wednesday, 2 Feb 2018 09:1:00 -0300", out: "2018-02-02 12:01:00 +0000 UTC"}, + // 12 Feb 2006, 19:17:08 + {in: "07 Feb 2004, 09:07", out: "2004-02-07 09:07:00 +0000 UTC"}, + {in: "07 Feb 2004, 09:07:07", out: "2004-02-07 09:07:07 +0000 UTC"}, + {in: "7 Feb 2004, 09:07:07", out: "2004-02-07 09:07:07 +0000 UTC"}, + {in: "07 Feb 2004, 9:7:7", out: "2004-02-07 09:07:07 +0000 UTC"}, + // 12 Feb 2006, 19:17:08 GMT + {in: "07 Feb 2004, 09:07:07 GMT", out: "2004-02-07 09:07:07 +0000 UTC"}, + // 12 Feb 2006, 19:17:08 +0100 + {in: "07 Feb 2004, 09:07:07 +0100", out: "2004-02-07 08:07:07 +0000 UTC"}, + // 2013-Feb-03 + {in: "2013-Feb-03", out: "2013-02-03 00:00:00 +0000 UTC"}, + // 03 February 2013 + {in: "03 February 2013", out: "2013-02-03 00:00:00 +0000 UTC"}, + {in: "3 February 2013", out: "2013-02-03 00:00:00 +0000 UTC"}, + // Chinese 2014年04月18日 + {in: "2014年04月08日", out: "2014-04-08 00:00:00 +0000 UTC"}, + {in: "2014年04月08日 19:17:22", out: "2014-04-08 19:17:22 +0000 UTC"}, + // mm/dd/yyyy + {in: "03/31/2014", out: "2014-03-31 00:00:00 +0000 UTC"}, + {in: "3/31/2014", out: "2014-03-31 00:00:00 +0000 UTC"}, + {in: "3/5/2014", out: "2014-03-05 00:00:00 +0000 UTC"}, + // mm/dd/yy + {in: "08/08/71", out: "1971-08-08 00:00:00 +0000 UTC"}, + {in: "8/8/71", out: "1971-08-08 00:00:00 +0000 UTC"}, + // mm/dd/yy hh:mm:ss + {in: "04/02/2014 04:08:09", out: "2014-04-02 04:08:09 +0000 UTC"}, + {in: "4/2/2014 04:08:09", out: "2014-04-02 04:08:09 +0000 UTC"}, + {in: "04/02/2014 4:08:09", out: "2014-04-02 04:08:09 +0000 UTC"}, + {in: "04/02/2014 4:8:9", out: "2014-04-02 04:08:09 +0000 UTC"}, + {in: "04/02/2014 04:08", out: "2014-04-02 04:08:00 +0000 UTC"}, + {in: "04/02/2014 4:8", out: "2014-04-02 04:08:00 +0000 UTC"}, + {in: "04/02/2014 04:08:09.123", out: "2014-04-02 04:08:09.123 +0000 UTC"}, + {in: "04/02/2014 04:08:09.12312", out: "2014-04-02 04:08:09.12312 +0000 UTC"}, + {in: "04/02/2014 04:08:09.123123", out: "2014-04-02 04:08:09.123123 +0000 UTC"}, + // mm/dd/yy hh:mm:ss AM + {in: "04/02/2014 04:08:09 AM", out: "2014-04-02 04:08:09 +0000 UTC"}, + {in: "04/02/2014 04:08:09 PM", out: "2014-04-02 16:08:09 +0000 UTC"}, + {in: "04/02/2014 04:08 AM", out: "2014-04-02 04:08:00 +0000 UTC"}, + {in: "04/02/2014 04:08 PM", out: "2014-04-02 16:08:00 +0000 UTC"}, + {in: "04/02/2014 4:8 AM", out: "2014-04-02 04:08:00 +0000 UTC"}, + {in: "04/02/2014 4:8 PM", out: "2014-04-02 16:08:00 +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.123 PM", out: "2014-04-02 16:08:09.123 +0000 UTC"}, + // yyyy/mm/dd + {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/4/2", out: "2014-04-02 00:00:00 +0000 UTC"}, + // yyyy/mm/dd hh:mm:ss AM + {in: "2014/04/02 04:08", out: "2014-04-02 04:08:00 +0000 UTC"}, + {in: "2014/03/31 04:08", out: "2014-03-31 04:08:00 +0000 UTC"}, + {in: "2014/4/2 04:08", out: "2014-04-02 04:08:00 +0000 UTC"}, + {in: "2014/04/02 4:8", out: "2014-04-02 04:08:00 +0000 UTC"}, + {in: "2014/04/02 04:08:09", out: "2014-04-02 04:08:09 +0000 UTC"}, + {in: "2014/03/31 04:08:09", out: "2014-03-31 04:08:09 +0000 UTC"}, + {in: "2014/4/2 04:08:09", out: "2014-04-02 04:08:09 +0000 UTC"}, + {in: "2014/04/02 04:08:09.123", out: "2014-04-02 04:08:09.123 +0000 UTC"}, + {in: "2014/04/02 04:08:09.123123", out: "2014-04-02 04:08:09.123123 +0000 UTC"}, + {in: "2014/04/02 04:08:09 AM", out: "2014-04-02 04:08:09 +0000 UTC"}, + {in: "2014/03/31 04:08:09 AM", out: "2014-03-31 04:08:09 +0000 UTC"}, + {in: "2014/4/2 04:08:09 AM", out: "2014-04-02 04:08:09 +0000 UTC"}, + {in: "2014/04/02 04:08:09.123 AM", out: "2014-04-02 04:08:09.123 +0000 UTC"}, + {in: "2014/04/02 04:08:09.123 PM", out: "2014-04-02 16:08:09.123 +0000 UTC"}, + // yyyy-mm-dd + {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-4-2", out: "2014-04-02 00:00:00 +0000 UTC"}, + // yyyy-mm + {in: "2014-04", out: "2014-04-01 00:00:00 +0000 UTC"}, + // yyyy-mm-dd hh:mm:ss AM + {in: "2014-04-02 04:08", out: "2014-04-02 04:08:00 +0000 UTC"}, + {in: "2014-03-31 04:08", out: "2014-03-31 04:08:00 +0000 UTC"}, + {in: "2014-4-2 04:08", out: "2014-04-02 04:08:00 +0000 UTC"}, + {in: "2014-04-02 4:8", out: "2014-04-02 04:08:00 +0000 UTC"}, + {in: "2014-04-02 04:08:09", out: "2014-04-02 04:08:09 +0000 UTC"}, + {in: "2014-03-31 04:08:09", out: "2014-03-31 04:08:09 +0000 UTC"}, + {in: "2014-4-2 04:08:09", out: "2014-04-02 04:08:09 +0000 UTC"}, + {in: "2014-04-02 04:08:09.123", out: "2014-04-02 04:08:09.123 +0000 UTC"}, + {in: "2014-04-02 04:08:09.123123", out: "2014-04-02 04:08:09.123123 +0000 UTC"}, + {in: "2014-04-02 04:08:09.12312312", out: "2014-04-02 04:08:09.12312312 +0000 UTC"}, + {in: "2014-04-02 04:08:09 AM", out: "2014-04-02 04:08:09 +0000 UTC"}, + {in: "2014-03-31 04:08:09 AM", out: "2014-03-31 04:08:09 +0000 UTC"}, + {in: "2014-04-26 05:24:37 PM", out: "2014-04-26 17:24:37 +0000 UTC"}, + {in: "2014-4-2 04:08:09 AM", out: "2014-04-02 04:08:09 +0000 UTC"}, + {in: "2014-04-02 04:08:09.123 AM", out: "2014-04-02 04:08:09.123 +0000 UTC"}, + {in: "2014-04-02 04:08:09.123 PM", out: "2014-04-02 16:08:09.123 +0000 UTC"}, + // yyyy-mm-dd hh:mm:ss,000 + {in: "2014-05-11 08:20:13,787", out: "2014-05-11 08:20:13.787 +0000 UTC"}, + // yyyy-mm-dd hh:mm:ss +0000 + {in: "2012-08-03 18:31:59 +0000", out: "2012-08-03 18:31:59 +0000 UTC"}, + {in: "2012-08-03 13:31:59 -0600", out: "2012-08-03 19:31:59 +0000 UTC"}, + {in: "2012-08-03 18:31:59.257000000 +0000", out: "2012-08-03 18:31:59.257 +0000 UTC"}, + {in: "2012-08-03 8:1:59.257000000 +0000", out: "2012-08-03 08:01:59.257 +0000 UTC"}, + {in: "2012-8-03 18:31:59.257000000 +0000", out: "2012-08-03 18:31:59.257 +0000 UTC"}, + {in: "2012-8-3 18:31:59.257000000 +0000", out: "2012-08-03 18:31:59.257 +0000 UTC"}, + {in: "2014-04-26 17:24:37.123456 +0000", out: "2014-04-26 17:24:37.123456 +0000 UTC"}, + {in: "2014-04-26 17:24:37.12 +0000", out: "2014-04-26 17:24:37.12 +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"}, + + // 13:31:51.999 -07:00 MST + // yyyy-mm-dd hh:mm:ss +00:00 + {in: "2012-08-03 18:31:59 +00:00", out: "2012-08-03 18:31:59 +0000 UTC"}, + {in: "2014-05-01 08:02:13 +00:00", out: "2014-05-01 08:02:13 +0000 UTC"}, + {in: "2014-5-01 08:02:13 +00:00", out: "2014-05-01 08:02:13 +0000 UTC"}, + {in: "2014-05-1 08:02:13 +00:00", out: "2014-05-01 08:02:13 +0000 UTC"}, + {in: "2012-08-03 13:31:59 -06:00", out: "2012-08-03 19:31:59 +0000 UTC"}, + {in: "2012-08-03 18:31:59.257000000 +00:00", out: "2012-08-03 18:31:59.257 +0000 UTC"}, + {in: "2012-08-03 8:1:59.257000000 +00:00", out: "2012-08-03 08:01:59.257 +0000 UTC"}, + {in: "2012-8-03 18:31:59.257000000 +00:00", out: "2012-08-03 18:31:59.257 +0000 UTC"}, + {in: "2012-8-3 18:31:59.257000000 +00:00", out: "2012-08-03 18:31:59.257 +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.1 +00:00", out: "2014-04-26 17:24:37.1 +0000 UTC"}, + // yyyy-mm-dd hh:mm:ss +0000 TZ + // Golang Native Format + {in: "2012-08-03 18:31:59 +0000 UTC", out: "2012-08-03 18:31:59 +0000 UTC"}, + {in: "2012-08-03 13:31:59 -0600 MST", out: "2012-08-03 19:31:59 +0000 UTC", loc: "America/Denver"}, + {in: "2015-02-18 00:12:00 +0000 UTC", out: "2015-02-18 00:12:00 +0000 UTC"}, + {in: "2015-02-18 00:12:00 +0000 GMT", out: "2015-02-18 00:12:00 +0000 UTC"}, + {in: "2015-02-08 03:02:00 +0200 CEST", out: "2015-02-08 01:02:00 +0000 UTC", loc: "Europe/Berlin"}, + {in: "2015-02-08 03:02:00 +0300 MSK", out: "2015-02-08 00:02:00 +0000 UTC"}, + {in: "2015-2-08 03:02:00 +0300 MSK", out: "2015-02-08 00:02:00 +0000 UTC"}, + {in: "2015-02-8 03:02:00 +0300 MSK", out: "2015-02-08 00:02:00 +0000 UTC"}, + {in: "2015-2-8 03:02:00 +0300 MSK", out: "2015-02-08 00:02:00 +0000 UTC"}, + {in: "2012-08-03 18:31:59.257000000 +0000 UTC", out: "2012-08-03 18:31:59.257 +0000 UTC"}, + {in: "2012-08-03 8:1:59.257000000 +0000 UTC", out: "2012-08-03 08:01:59.257 +0000 UTC"}, + {in: "2012-8-03 18:31:59.257000000 +0000 UTC", out: "2012-08-03 18:31:59.257 +0000 UTC"}, + {in: "2012-8-3 18:31:59.257000000 +0000 UTC", out: "2012-08-03 18:31:59.257 +0000 UTC"}, + {in: "2014-04-26 17:24:37.123456 +0000 UTC", out: "2014-04-26 17:24:37.123456 +0000 UTC"}, + {in: "2014-04-26 17:24:37.12 +0000 UTC", out: "2014-04-26 17:24:37.12 +0000 UTC"}, + {in: "2014-04-26 17:24:37.1 +0000 UTC", out: "2014-04-26 17:24:37.1 +0000 UTC"}, + // yyyy-mm-dd hh:mm:ss TZ + {in: "2012-08-03 18:31:59 UTC", out: "2012-08-03 18:31:59 +0000 UTC"}, + {in: "2014-12-16 06:20:00 GMT", out: "2014-12-16 06:20:00 +0000 UTC"}, + {in: "2012-08-03 13:31:59 MST", out: "2012-08-03 20:31:59 +0000 UTC", loc: "America/Denver"}, + {in: "2012-08-03 18:31:59.257000000 UTC", out: "2012-08-03 18:31:59.257 +0000 UTC"}, + {in: "2012-08-03 8:1:59.257000000 UTC", out: "2012-08-03 08:01:59.257 +0000 UTC"}, + {in: "2012-8-03 18:31:59.257000000 UTC", out: "2012-08-03 18:31:59.257 +0000 UTC"}, + {in: "2012-8-3 18:31:59.257000000 UTC", out: "2012-08-03 18:31:59.257 +0000 UTC"}, + {in: "2014-04-26 17:24:37.123456 UTC", out: "2014-04-26 17:24:37.123456 +0000 UTC"}, + {in: "2014-04-26 17:24:37.12 UTC", out: "2014-04-26 17:24:37.12 +0000 UTC"}, + {in: "2014-04-26 17:24:37.1 UTC", out: "2014-04-26 17:24:37.1 +0000 UTC"}, + // This one is pretty special, it is TIMEZONE based but starts with P to emulate collions with PM + {in: "2014-04-26 05:24:37 PST", out: "2014-04-26 05:24:37 +0000 UTC"}, + {in: "2014-04-26 05:24:37 PST", out: "2014-04-26 12:24:37 +0000 UTC", loc: "America/Los_Angeles"}, + // yyyy-mm-dd hh:mm:ss+00:00 + {in: "2012-08-03 18:31:59+00:00", out: "2012-08-03 18:31:59 +0000 UTC"}, + {in: "2017-07-19 03:21:51+00:00", out: "2017-07-19 03:21:51 +0000 UTC"}, + // yyyy-mm-dd hh:mm:ss.000+00:00 PST + {in: "2012-08-03 18:31:59.000+00:00 PST", out: "2012-08-03 18:31:59 +0000 UTC", loc: "America/Los_Angeles"}, + // yyyy-mm-dd hh:mm:ss +00:00 TZ + {in: "2012-08-03 18:31:59 +00:00 UTC", out: "2012-08-03 18:31:59 +0000 UTC"}, + {in: "2012-08-03 13:31:51 -07:00 MST", out: "2012-08-03 20:31:51 +0000 UTC", loc: "America/Denver"}, + {in: "2012-08-03 18:31:59.257000000 +00:00 UTC", out: "2012-08-03 18:31:59.257 +0000 UTC"}, + {in: "2012-08-03 13:31:51.123 -08:00 PST", out: "2012-08-03 21:31:51.123 +0000 UTC", loc: "America/Los_Angeles"}, + {in: "2012-08-03 13:31:51.123 +02:00 CEST", out: "2012-08-03 11:31:51.123 +0000 UTC", loc: "Europe/Berlin"}, + {in: "2012-08-03 8:1:59.257000000 +00:00 UTC", out: "2012-08-03 08:01:59.257 +0000 UTC"}, + {in: "2012-8-03 18:31:59.257000000 +00:00 UTC", out: "2012-08-03 18:31:59.257 +0000 UTC"}, + {in: "2012-8-3 18:31:59.257000000 +00:00 UTC", out: "2012-08-03 18:31:59.257 +0000 UTC"}, + {in: "2014-04-26 17:24:37.123456 +00:00 UTC", out: "2014-04-26 17:24:37.123456 +0000 UTC"}, + {in: "2014-04-26 17:24:37.12 +00:00 UTC", out: "2014-04-26 17:24:37.12 +0000 UTC"}, + {in: "2014-04-26 17:24:37.1 +00:00 UTC", out: "2014-04-26 17:24:37.1 +0000 UTC"}, + // yyyy-mm-ddThh:mm:ss + {in: "2009-08-12T22:15:09", out: "2009-08-12 22:15:09 +0000 UTC"}, + {in: "2009-08-08T02:08:08", out: "2009-08-08 02:08:08 +0000 UTC"}, + {in: "2009-08-08T2:8:8", out: "2009-08-08 02:08:08 +0000 UTC"}, + {in: "2009-08-12T22:15:09.123", out: "2009-08-12 22:15:09.123 +0000 UTC"}, + {in: "2009-08-12T22:15:09.123456", out: "2009-08-12 22:15:09.123456 +0000 UTC"}, + {in: "2009-08-12T22:15:09.12", out: "2009-08-12 22:15:09.12 +0000 UTC"}, + {in: "2009-08-12T22:15:09.1", out: "2009-08-12 22:15:09.1 +0000 UTC"}, + {in: "2014-04-26 17:24:37.3186369", out: "2014-04-26 17:24:37.3186369 +0000 UTC"}, + // yyyy-mm-ddThh:mm:ss-07:00 + {in: "2009-08-12T22:15:09-07:00", out: "2009-08-13 05:15:09 +0000 UTC"}, + {in: "2009-08-12T22:15:09-03:00", out: "2009-08-13 01:15:09 +0000 UTC"}, + {in: "2009-08-12T22:15:9-07:00", out: "2009-08-13 05:15:09 +0000 UTC"}, + {in: "2009-08-12T22:15:09.123-07:00", out: "2009-08-13 05:15:09.123 +0000 UTC"}, + {in: "2016-06-21T19:55:00+01:00", out: "2016-06-21 18:55:00 +0000 UTC"}, + {in: "2016-06-21T19:55:00.799+01:00", out: "2016-06-21 18:55:00.799 +0000 UTC"}, + // yyyy-mm-ddThh:mm:ss-0700 + {in: "2009-08-12T22:15:09-0700", out: "2009-08-13 05:15:09 +0000 UTC"}, + {in: "2009-08-12T22:15:09-0300", out: "2009-08-13 01:15:09 +0000 UTC"}, + {in: "2009-08-12T22:15:9-0700", out: "2009-08-13 05:15:09 +0000 UTC"}, + {in: "2009-08-12T22:15:09.123-0700", out: "2009-08-13 05:15:09.123 +0000 UTC"}, + {in: "2016-06-21T19:55:00+0100", out: "2016-06-21 18:55:00 +0000 UTC"}, + {in: "2016-06-21T19:55:00.799+0100", out: "2016-06-21 18:55:00.799 +0000 UTC"}, + {in: "2016-06-21T19:55:00+0100", out: "2016-06-21 18:55:00 +0000 UTC"}, + {in: "2016-06-21T19:55:00-0700", out: "2016-06-22 02:55:00 +0000 UTC"}, + {in: "2016-06-21T19:55:00.799+0100", out: "2016-06-21 18:55:00.799 +0000 UTC"}, + // yyyy-mm-ddThh:mm:ssZ + {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:09.99Z", out: "2009-08-12 22:15:09.99 +0000 UTC"}, + {in: "2009-08-12T22:15:09.9999Z", out: "2009-08-12 22:15:09.9999 +0000 UTC"}, + {in: "2009-08-12T22:15:09.99999999Z", out: "2009-08-12 22:15:09.99999999 +0000 UTC"}, + {in: "2009-08-12T22:15:9.99999999Z", out: "2009-08-12 22:15:09.99999999 +0000 UTC"}, + // mm.dd.yyyy + {in: "3.31.2014", out: "2014-03-31 00:00:00 +0000 UTC"}, + {in: "3.3.2014", out: "2014-03-03 00:00:00 +0000 UTC"}, + {in: "03.31.2014", out: "2014-03-31 00:00:00 +0000 UTC"}, + // mm.dd.yy + {in: "08.21.71", out: "1971-08-21 00:00:00 +0000 UTC"}, + // yyyymmdd and similar + {in: "2014", out: "2014-01-01 00:00:00 +0000 UTC"}, + {in: "20140601", out: "2014-06-01 00:00:00 +0000 UTC"}, + // all digits: unix secs, ms etc + {in: "1332151919", out: "2012-03-19 10:11:59 +0000 UTC"}, + {in: "1332151919", out: "2012-03-19 10:11:59 +0000 UTC", loc: "America/Denver"}, + {in: "1384216367111", out: "2013-11-12 00:32:47.111 +0000 UTC"}, + {in: "1384216367111222", out: "2013-11-12 00:32:47.111222 +0000 UTC"}, + {in: "1384216367111222333", out: "2013-11-12 00:32:47.111222333 +0000 UTC"}, } +/* + {in: , out: }, + + +*/ func TestParse(t *testing.T) { // Lets ensure we are operating on UTC @@ -159,499 +422,45 @@ func TestParse(t *testing.T) { assert.NotEqual(t, nil, err) assert.Equal(t, true, testDidPanic("NOT GONNA HAPPEN")) - - for _, th := range testInputs { - ts = MustParse(th.in) - got := fmt.Sprintf("%v", ts.In(time.UTC)) - assert.Equal(t, th.out, got, "Expected %q but got %q from %q", th.out, got, th.in) - } - - // RFC850 = "Monday, 02-Jan-06 15:04:05 MST" - ts = MustParse("Wednesday, 07-May-09 08:00:43 MST") - assert.Equal(t, "2009-05-07 08:00:43 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("Wednesday, 28-Feb-18 09:01:00 MST") - assert.Equal(t, "2018-02-28 09:01:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - // ST_WEEKDAYCOMMADELTA - // Monday, 02 Jan 2006 15:04:05 -0700 - // Monday, 02 Jan 2006 15:04:05 +0100 - ts = MustParse("Monday, 02 Jan 2006 15:04:05 +0100") - assert.Equal(t, "2006-01-02 14:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("Monday, 02 Jan 2006 15:04:5 +0100") - assert.Equal(t, "2006-01-02 14:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("Monday, 02 Jan 2006 15:4:05 +0100") - assert.Equal(t, "2006-01-02 14:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("Monday, 02 Jan 2006 15:04:05 -0100") - assert.Equal(t, "2006-01-02 16:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - // Another weird one, year on the end after UTC? - ts = MustParse("Mon Aug 10 15:44:11 UTC+0000 2015") - assert.Equal(t, "2015-08-10 15:44:11 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("Mon Aug 10 15:44:11 PST-0700 2015") - assert.Equal(t, "2015-08-10 22:44:11 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("Mon Aug 10 15:44:11 CEST+0200 2015") - assert.Equal(t, "2015-08-10 13:44:11 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - // Easily the worst Date format I have ever seen - // "Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)" - ts = MustParse("Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)") - assert.Equal(t, "2015-07-03 17:04:07 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("Fri, 03 Jul 2015 13:04:07 MST") - assert.Equal(t, "2015-07-03 13:04:07 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("Mon, 2 Jan 2006 15:4:05 MST") - assert.Equal(t, "2006-01-02 15:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("Mon, 2 Jan 2006 15:4:5 MST") - assert.Equal(t, "2006-01-02 15:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("Mon, 02-Jan-06 15:04:05 MST") - assert.Equal(t, "2006-01-02 15:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("Tue, 11 Jul 2017 16:28:13 +0200 (CEST)") - assert.Equal(t, "2017-07-11 14:28:13 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("Tue, 5 Jul 2017 16:28:13 -0700 (CEST)") - assert.Equal(t, "2017-07-05 23:28:13 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("Thu, 13 Jul 2017 08:58:40 +0100") - assert.Equal(t, "2017-07-13 07:58:40 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("Mon, 02 Jan 2006 15:04:05 -0700") - assert.Equal(t, "2006-01-02 22:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("Mon, 02 Jan 2006 15:4:05 -0700") - assert.Equal(t, "2006-01-02 22:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("Mon, 02 Jan 2006 15:4:5 -0700") - assert.Equal(t, "2006-01-02 22:04:05 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("Thu, 4 Jan 2018 17:53:36 +0000") - assert.Equal(t, "2018-01-04 17:53:36 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - // not sure if this is anything close to a standard, never seen it before - ts = MustParse("12 Feb 2006, 19:17") - assert.Equal(t, "2006-02-12 19:17:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2 Feb 2006, 19:17") - assert.Equal(t, "2006-02-02 19:17:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("12 Feb 2006, 19:17:22") - assert.Equal(t, "2006-02-12 19:17:22 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2 Feb 2006, 19:17:22") - assert.Equal(t, "2006-02-02 19:17:22 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("12 Feb 2006 19:17") - assert.Equal(t, "2006-02-12 19:17:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2 Feb 2006 19:17") - assert.Equal(t, "2006-02-02 19:17:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("12 Feb 2006 19:17:22") - assert.Equal(t, "2006-02-12 19:17:22 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2 Feb 2006 19:17:22") - assert.Equal(t, "2006-02-02 19:17:22 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - // // 19 Dec 2013 12:15:23 GMT - // ts = MustParse("12 Feb 2006 19:17:22 GMT") - // assert.Equal(t, "2006-02-12 19:17:22 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - // ts = MustParse("2 Feb 2006 19:17:22 GMT") - // assert.Equal(t, "2006-02-02 19:17:22 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - // // 28 Mar 2010 15:45:30 +1100 - // ts = MustParse("12 Feb 2006 19:17:22") - // assert.Equal(t, "2006-02-12 19:17:22 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2013-Feb-03") - assert.Equal(t, "2013-02-03 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("03 February 2013") - assert.Equal(t, "2013-02-03 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("3 February 2013") - assert.Equal(t, "2013-02-03 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - //--------------------------------------------- - // Chinese 2014年04月18日 - - ts = MustParse("2014年04月08日") - assert.Equal(t, "2014-04-08 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014年04月08日 19:17:22") - assert.Equal(t, "2014-04-08 19:17:22 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - //--------------------------------------------- - // mm/dd/yyyy ? - - ts = MustParse("3/31/2014") - assert.Equal(t, "2014-03-31 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("03/31/2014") - assert.Equal(t, "2014-03-31 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - // what type of date is this? 08/21/71 - ts = MustParse("08/21/71") - assert.Equal(t, "1971-08-21 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - // m/d/yy - ts = MustParse("8/1/71") - assert.Equal(t, "1971-08-01 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("4/8/2014 22:05") - assert.Equal(t, "2014-04-08 22:05:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("4/18/2014 22:05") - assert.Equal(t, "2014-04-18 22:05:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("04/08/2014 22:05") - assert.Equal(t, "2014-04-08 22:05:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("4/8/14 22:05") - assert.Equal(t, "2014-04-08 22:05:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("4/18/14 22:05") - assert.Equal(t, "2014-04-18 22:05:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("10/18/14 22:05") - assert.Equal(t, "2014-10-18 22:05:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("04/2/2014 4:00:51") - assert.Equal(t, "2014-04-02 04:00:51 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("8/8/1965 01:00:01 PM") - assert.Equal(t, "1965-08-08 13:00:01 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("8/8/1965 12:00:01 AM") - assert.Equal(t, "1965-08-08 00:00:01 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("8/8/1965 01:00 PM") - assert.Equal(t, "1965-08-08 13:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("08/8/1965 01:00 PM") - assert.Equal(t, "1965-08-08 13:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("8/08/1965 1:00 PM") - assert.Equal(t, "1965-08-08 13:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("8/8/1965 12:00 AM") - assert.Equal(t, "1965-08-08 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("8/13/1965 01:00 PM") - assert.Equal(t, "1965-08-13 13:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("4/02/2014 03:00:51") - assert.Equal(t, "2014-04-02 03:00:51 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("03/19/2012 10:11:59") - assert.Equal(t, "2012-03-19 10:11:59 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("03/19/2012 10:11:59.3186369") - assert.Equal(t, "2012-03-19 10:11:59.3186369 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - //--------------------------------------------- - // yyyy/mm/dd ? - - ts = MustParse("2014/3/31") - assert.Equal(t, "2014-03-31 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014/03/31") - assert.Equal(t, "2014-03-31 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014/4/8 22:05") - assert.Equal(t, "2014-04-08 22:05:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014/4/8 2:05") - assert.Equal(t, "2014-04-08 02:05:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014/04/08 22:05") - assert.Equal(t, "2014-04-08 22:05:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014/04/2 03:00:51") - assert.Equal(t, "2014-04-02 03:00:51 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014/4/02 03:00:51") - assert.Equal(t, "2014-04-02 03:00:51 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2012/03/19 10:11:59") - assert.Equal(t, "2012-03-19 10:11:59 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2012/03/19 10:11:59.318") - assert.Equal(t, "2012-03-19 10:11:59.318 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2012/03/19 10:11:59.3186369") - assert.Equal(t, "2012-03-19 10:11:59.3186369 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2012/03/19 10:11:59.318636945") - assert.Equal(t, "2012-03-19 10:11:59.318636945 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2012/03/19 10:11 PM") - assert.Equal(t, "2012-03-19 22:11:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2012/03/19 1:11 PM") - assert.Equal(t, "2012-03-19 13:11:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2012/3/19 10:11 PM") - assert.Equal(t, "2012-03-19 22:11:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2012/3/3 10:11 PM") - assert.Equal(t, "2012-03-03 22:11:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2012/03/19 10:11:59 PM") - assert.Equal(t, "2012-03-19 22:11:59 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2012/3/3 10:11:59 PM") - assert.Equal(t, "2012-03-03 22:11:59 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2012/03/03 10:11:59.345 PM") - assert.Equal(t, "2012-03-03 22:11:59.345 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - //--------------------------------------------- - // yyyy-mm-dd ? - ts = MustParse("2009-08-12T22:15:09-07:00") - assert.Equal(t, "2009-08-13 05:15:09 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2009-08-12T22:15:9-07:00") - assert.Equal(t, "2009-08-13 05:15:09 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2009-08-12T22:15:09.123-07:00") - assert.Equal(t, "2009-08-13 05:15:09.123 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC)), "%v", ts.In(time.UTC)) - - ts = MustParse("2009-08-12T22:15Z") - assert.Equal(t, "2009-08-12 22:15:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC)), "%v", ts.In(time.UTC)) - - ts = MustParse("2009-08-12T22:15:09Z") - assert.Equal(t, "2009-08-12 22:15:09 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC)), "%v", ts.In(time.UTC)) - - ts = MustParse("2009-08-12T22:15:09.99Z") - assert.Equal(t, "2009-08-12 22:15:09.99 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2009-08-12T22:15:09.9999Z") - assert.Equal(t, "2009-08-12 22:15:09.9999 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2009-08-12T22:15:09.99999999Z") - assert.Equal(t, "2009-08-12 22:15:09.99999999 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2009-08-12T22:15:9.99999999Z") - assert.Equal(t, "2009-08-12 22:15:09.99999999 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - // https://github.com/golang/go/issues/5294 _, err = ParseAny(time.RFC3339) assert.NotEqual(t, nil, err) - ts = MustParse("2009-08-12T22:15:09.123") - assert.Equal(t, "2009-08-12 22:15:09.123 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) + for _, th := range testInputs { + if len(th.loc) > 0 { + loc, err := time.LoadLocation(th.loc) + if err != nil { + t.Fatalf("Expected to load location %q but got %v", th.loc, err) + } + ts, err = ParseIn(th.in, loc) + if err != nil { + t.Fatalf("expected to parse %q but got %v", th.in, err) + } + got := fmt.Sprintf("%v", ts.In(time.UTC)) + assert.Equal(t, th.out, got, "Expected %q but got %q from %q", th.out, got, th.in) + if th.out != got { + panic("whoops") + } + } else { + ts = MustParse(th.in) + got := fmt.Sprintf("%v", ts.In(time.UTC)) + assert.Equal(t, th.out, got, "Expected %q but got %q from %q", th.out, got, th.in) + if th.out != got { + panic("whoops") + } + } + } - ts = MustParse("2009-08-12T22:15:09.123456") - assert.Equal(t, "2009-08-12 22:15:09.123456 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2009-08-12T22:15:09.12") - assert.Equal(t, "2009-08-12 22:15:09.12 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2009-08-12T22:15:09.1") - assert.Equal(t, "2009-08-12 22:15:09.1 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2009-08-12T22:15:09") - assert.Equal(t, "2009-08-12 22:15:09 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-04-26 17:24:37.3186369") - assert.Equal(t, "2014-04-26 17:24:37.3186369 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - // 2015-06-25 01:25:37.115208593 +0000 UTC - ts = MustParse("2012-08-03 18:31:59.257000000 +0000 UTC") - assert.Equal(t, "2012-08-03 18:31:59.257 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2012-08-03 8:1:59.257000000 +0000 UTC") - assert.Equal(t, "2012-08-03 08:01:59.257 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2012-8-03 18:31:59.257000000 +0000 UTC") - assert.Equal(t, "2012-08-03 18:31:59.257 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2012-8-3 18:31:59.257000000 +0000 UTC") - assert.Equal(t, "2012-08-03 18:31:59.257 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2015-09-30 18:48:56.35272715 +0000 UTC") - assert.Equal(t, "2015-09-30 18:48:56.35272715 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2017-01-27 00:07:31.945167") - assert.Equal(t, "2017-01-27 00:07:31.945167 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2012-08-03 18:31:59.257000000") - assert.Equal(t, "2012-08-03 18:31:59.257 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2013-04-01 22:43") - assert.Equal(t, "2013-04-01 22:43:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2013-04-01 22:43:22") - assert.Equal(t, "2013-04-01 22:43:22 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-04-26 17:24:37.123") - assert.Equal(t, "2014-04-26 17:24:37.123 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-04-26 17:24:37.123456 +0000 UTC") - assert.Equal(t, "2014-04-26 17:24:37.123456 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-04-26 17:24:37.123456 UTC") - assert.Equal(t, "2014-04-26 17:24:37.123456 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-04-26 17:24:37.123 UTC") - assert.Equal(t, "2014-04-26 17:24:37.123 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-04-26 17:24:37.12 UTC") - assert.Equal(t, "2014-04-26 17:24:37.12 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-04-26 17:24:37.1 UTC") - assert.Equal(t, "2014-04-26 17:24:37.1 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-04-26 09:04:37.123 +0800") - assert.Equal(t, "2014-04-26 01:04:37.123 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("2014-04-26 9:04:37.123 +0800") - assert.Equal(t, "2014-04-26 01:04:37.123 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("2014-04-26 09:4:37.123 +0800") - assert.Equal(t, "2014-04-26 01:04:37.123 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("2014-04-26 9:4:37.123 +0800") - assert.Equal(t, "2014-04-26 01:04:37.123 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-04-26 17:24:37.123 -0800") - assert.Equal(t, "2014-04-27 01:24:37.123 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-04-26 17:24:37.123456 +0800") - assert.Equal(t, "2014-04-26 09:24:37.123456 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-04-26 17:24:37.123456 -0800") - assert.Equal(t, "2014-04-27 01:24:37.123456 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2017-07-19 03:21:51+00:00") - assert.Equal(t, "2017-07-19 03:21:51 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2017-07-09 03:01:51 +00:00 UTC") - assert.Equal(t, "2017-07-09 03:01:51 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("2017-7-09 03:01:51 +00:00 UTC") - assert.Equal(t, "2017-07-09 03:01:51 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("2017-07-9 03:01:51 +00:00 UTC") - assert.Equal(t, "2017-07-09 03:01:51 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("2017-7-9 03:01:51 +00:00 UTC") - assert.Equal(t, "2017-07-09 03:01:51 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2017-07-19 03:01:51 +00:00 UTC") - assert.Equal(t, "2017-07-19 03:01:51 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("2017-07-19 3:01:51 +00:00 UTC") - assert.Equal(t, "2017-07-19 03:01:51 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("2017-07-19 03:1:51 +00:00 UTC") - assert.Equal(t, "2017-07-19 03:01:51 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("2017-07-19 3:1:51 +00:00 UTC") - assert.Equal(t, "2017-07-19 03:01:51 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - // Golang Native Format - ts = MustParse("2015-02-18 00:12:00 +0000 UTC") - assert.Equal(t, "2015-02-18 00:12:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2015-02-18 00:12:00 +0000 GMT") - assert.Equal(t, "2015-02-18 00:12:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2015-02-08 03:02:00 +0300 MSK") - assert.Equal(t, "2015-02-08 00:02:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("2015-2-08 03:02:00 +0300 MSK") - assert.Equal(t, "2015-02-08 00:02:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("2015-02-8 03:02:00 +0300 MSK") - assert.Equal(t, "2015-02-08 00:02:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("2015-2-8 03:02:00 +0300 MSK") - assert.Equal(t, "2015-02-08 00:02:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-12-16 06:20:00 UTC") - assert.Equal(t, "2014-12-16 06:20:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-12-16 06:20:00 GMT") - assert.Equal(t, "2014-12-16 06:20:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-12-16 06:20:00 +0000 UTC") - assert.Equal(t, "2014-12-16 06:20:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-04-26 05:24:37 PM") - assert.Equal(t, "2014-04-26 17:24:37 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - // This one is pretty special, it is TIMEZONE based but starts with P to emulate collions with PM - ts = MustParse("2014-04-26 05:24:37 PST") - assert.Equal(t, "2014-04-26 05:24:37 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-04-26") - assert.Equal(t, "2014-04-26 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-04") - assert.Equal(t, "2014-04-01 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-05-11 08:20:13,787") - assert.Equal(t, "2014-05-11 08:20:13.787 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - _, err = ParseAny("2014-13-13 08:20:13,787") // month 13 doesn't exist so error - assert.NotEqual(t, nil, err) - - ts = MustParse("2014-05-01 08:02:13 +00:00") - assert.Equal(t, "2014-05-01 08:02:13 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("2014-5-01 08:02:13 +00:00") - assert.Equal(t, "2014-05-01 08:02:13 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("2014-05-1 08:02:13 +00:00") - assert.Equal(t, "2014-05-01 08:02:13 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - ts = MustParse("2014-5-1 08:02:13 +00:00") - assert.Equal(t, "2014-05-01 08:02:13 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2014-05-11 08:20:13 +0000") - assert.Equal(t, "2014-05-11 08:20:13 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2016-06-21T19:55:00+01:00") - assert.Equal(t, "2016-06-21 18:55:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2016-06-21T19:55:00.799+01:00") - assert.Equal(t, "2016-06-21 18:55:00.799 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2016-06-21T19:55:00+0100") - assert.Equal(t, "2016-06-21 18:55:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2016-06-21T19:55:00-0700") - assert.Equal(t, "2016-06-22 02:55:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("2016-06-21T19:55:00.799+0100") - assert.Equal(t, "2016-06-21 18:55:00.799 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - //--------------------------------------------- - // mm.dd.yyyy - // mm.dd.yy - - ts = MustParse("3.31.2014") - assert.Equal(t, "2014-03-31 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("03.31.2014") - assert.Equal(t, "2014-03-31 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("08.21.71") - assert.Equal(t, "1971-08-21 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - //--------------------------------------------- - - // yyyymmdd and similar - ts = MustParse("2014") - assert.Equal(t, "2014-01-01 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("20140601") - assert.Equal(t, "2014-06-01 00:00:00 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("1332151919") - assert.Equal(t, "2012-03-19 10:11:59 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("1384216367111") - assert.Equal(t, "2013-11-12 00:32:47.111 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts, _ = ParseIn("1384216367111", time.UTC) - assert.Equal(t, "2013-11-12 00:32:47.111 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - ts = MustParse("1384216367111222") - assert.Equal(t, "2013-11-12 00:32:47.111222 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) - - // Nanoseconds - ts = MustParse("1384216367111222333") - assert.Equal(t, "2013-11-12 00:32:47.111222333 +0000 UTC", fmt.Sprintf("%v", ts.In(time.UTC))) + // some errors _, err = ParseAny("138421636711122233311111") // too many digits assert.NotEqual(t, nil, err) _, err = ParseAny("-1314") assert.NotEqual(t, nil, err) + + _, err = ParseAny("2014-13-13 08:20:13,787") // month 13 doesn't exist so error + assert.NotEqual(t, nil, err) } func testDidPanic(datestr string) (paniced bool) {