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/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 f33719b..60343a6 100644 --- a/parseany.go +++ b/parseany.go @@ -11,62 +11,67 @@ import ( "unicode/utf8" ) -type dateState int +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 + dateDigitDashDash + dateDigitDashDashWs + dateDigitDashDashT + dateDigitDashDashAlpha + dateDigitDot + dateDigitDotDot + dateDigitSlash + dateDigitChineseYear + dateDigitChineseYearWs + dateDigitWs + dateDigitWsMoYear + dateDigitWsMolong + dateAlpha + dateAlphaWs + dateAlphaWsDigit + dateAlphaWsDigitComma + dateAlphaWsDigitCommaWs + dateAlphaWsDigitCommaWsYear + dateAlphaWsAlpha + dateAlphaWsAlphaYearmaybe + dateWeekdayComma + dateWeekdayAbbrevComma + dateWeekdayAbbrevCommaDash +) +const ( + // Time state + timeIgnore timeState = iota + timeStart + timeWs + timeWsAlpha // 3 + timeWsAlphaZoneOffset + timeWsAlphaZoneOffsetWs + timeWsAlphaZoneOffsetWsYear + timeWsAlphaZoneOffsetWsExtra + timeWsAMPMMaybe + timeWsAMPM + timeWsOffset + timeWsOffsetAlpha + timeWsOffsetColonAlpha + timeWsOffsetColon + timeWsYear + timeOffset + timeOffsetColon + timeAlpha + timePeriod + timePeriodOffset + timePeriodOffsetColon + timePeriodWs + timePeriodWsAlpha + timePeriodWsOffset + timePeriodWsOffsetWs + timePeriodWsOffsetWsAlpha + timeZ + timeZDigit ) var ( @@ -124,64 +129,252 @@ func parse(layout, datestr string, loc *time.Location) (time.Time, error) { return time.ParseInLocation(layout, datestr, loc) } -func parseTime(datestr string, loc *time.Location) (time.Time, error) { - state := stateStart +type parser struct { + loc *time.Location + preferMonthFirst bool + stateDate dateState + stateTime timeState + format []byte + datestr string + skip int + extra int + part1Len 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 + offsetlen int + tzi int + tzlen int +} - part1Len := 0 - part2Len := 0 - part3Len := 0 +func newParser(dateStr string, loc *time.Location) *parser { + p := parser{ + stateDate: dateStart, + stateTime: timeIgnore, + datestr: dateStr, + loc: loc, + preferMonthFirst: true, + } + 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) { + return + } + for i, r := range val { + p.format[start+i] = byte(r) + } +} +func (p *parser) setMonth() { + if p.molen == 2 { + p.set(p.moi, "01") + } else if p.molen == 1 { + p.set(p.moi, "1") + } +} + +func (p *parser) setDay() { + if p.daylen == 2 { + p.set(p.dayi, "02") + } else if p.daylen == 1 { + p.set(p.dayi, "2") + } +} +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 + } + p.setYear() + } + 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) 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 + // 3:04:05 + // 3:4:5 + // 15:04:05.00 + if p.houri > 0 { + if p.hourlen == 2 { + p.set(p.houri, "15") + } else if p.hourlen == 1 { + 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 + } + if p.seclen == 2 { + p.set(p.seci, "05") + } else { + p.set(p.seci, "5") + } + } + + if p.msi > 0 { + for i := 0; i < p.mslen; i++ { + p.format[p.msi+i] = '0' + } + } +} + +func (p *parser) trimExtra() { + if p.extra > 0 && len(p.format) > p.extra { + p.format = p.format[0:p.extra] + p.datestr = p.datestr[0:p.extra] + } +} + +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:] + } + 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) { + + p := newParser(datestr, loc) + i := 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 // 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 p.stateDate { + case dateStart: if unicode.IsDigit(r) { - state = stateDigit + p.stateDate = dateDigit } else if unicode.IsLetter(r) { - state = stateAlpha - } - case stateDigit: // starts digits - if unicode.IsDigit(r) { - continue - } else if unicode.IsLetter(r) { - if r == '年' { - // Chinese Year - state = stateDigitChineseYear - } + p.stateDate = dateAlpha } + case dateDigit: + switch r { case '-', '\u2212': - state = stateDigitDash - 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 '/': - state = stateDigitSlash - part1Len = i - case '.': - state = stateDigitDot - part1Len = i - case ' ': - state = stateDigitWs - part1Len = i - } + // 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 stateDigitDash: // starts digit then dash 02- + case '.': + // 3.31.2014 + p.moi = 0 + p.molen = i + p.setMonth() + 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 + default: + //if unicode.IsDigit(r) { + continue + } + p.part1Len = i + + case dateDigitDash: + // 2006-01 // 2006-01-02 - // stateDigitDashT + // 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 - // stateDigitDashWs + // dateDigitDashDashWs // 2012-08-03 18:31:59.257000000 // 2014-04-26 17:24:37.3186369 // 2017-01-27 00:07:31.945167 @@ -190,197 +383,63 @@ iterRunes: // 2017-07-19 03:21:51+00:00 // 2013-04-01 22:43:22 // 2014-04-26 05:24:37 PM - // stateDigitDashAlpha + // dateDigitDashDashAlpha // 2013-Feb-03 - switch { - case r == '-': - part2Len = i - part1Len - 1 - case r == ' ': - part3Len = i - part1Len - part2Len - 1 - 1 - state = stateDigitDashWs - case r == 'T': - state = stateDigitDashT + // 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) { - state = stateDigitDashAlpha - break iterRunes + if unicode.IsDigit(r) { + //continue + } else if unicode.IsLetter(r) { + p.stateDate = dateDigitDashDashAlpha } } - 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 + 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 ',': - 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 + 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 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 + case dateDigitDashDashAlpha: + // 2013-Feb-03 + // 13-Feb-03 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 '-': + p.molen = i - p.moi + p.set(p.moi, "Jan") + p.dayi = i + 1 } - - 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: // 2014/07/10 06:55:38.156283 // 03/19/2012 10:11:59 // 04/2/2014 03:00:37 @@ -390,271 +449,671 @@ iterRunes: // 10/13/2014 // 01/02/2006 // 1/2/06 - if unicode.IsDigit(r) || r == '/' { - continue - } + switch r { case ' ': - state = stateDigitSlashWS - } - case stateDigitSlashWS: // 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 - // 3/1/2012 10:11:59 - // 4/8/2014 22:05 - // 4/8/14 22:05 - switch r { - case ':': - state = stateDigitSlashWSColon - } - case stateDigitSlashWSColon: // 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 - // 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 ':': - state = stateDigitSlashWSColonColon - case 'A', 'P': - state = stateDigitSlashWSColonAMPM - } - case stateDigitSlashWSColonColon: // 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 - // 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': - state = stateDigitSlashWSColonColonAMPM + 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 '/': + 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 stateDigitWs: + case dateDigitWs: // 18 January 2018 // 8 January 2018 - //stateDigitWsMoShort - // 02 Jan 2018 23:59 - // 02 Jan 2018 23:59:34 - // stateDigitWsMoShortComma - // 12 Feb 2006, 19:17 - // 12 Feb 2006, 19:17:22 - if r == ' ' { - if i <= part1Len+len(" Feb") { - state = stateDigitWsMoShort + // 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.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") + p.stateDate = dateDigitWsMoYear } else { - break iterRunes + p.stateDate = dateDigitWsMolong } } - case stateDigitWsMoShort: - // 18 January 2018 - // 8 January 2018 - // stateDigitWsMoShort - // stateDigitWsMoShortColon - // 02 Jan 2018 23:59 - // stateDigitWsMoShortComma - // 12 Feb 2006, 19:17 - // 12 Feb 2006, 19:17:22 - switch r { - case ':': - state = stateDigitWsMoShortColon - case ',': - state = stateDigitWsMoShortComma - } - case stateDigitWsMoShortColon: - // 02 Jan 2018 23:59 - // stateDigitWsMoShortColonColon - // 02 Jan 2018 23:59:45 + case 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 ':': - state = stateDigitWsMoShortColonColon + case ',': + i++ + break iterRunes + case ' ': break iterRunes } + case dateDigitWsMolong: + // 18 January 2018 + // 8 January 2018 - 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 + p.stateDate = dateDigitChineseYearWs break } - case stateDigitDot: + case dateDigitDot: // 3.31.2014 if r == '.' { - state = stateDigitDotDot - part2Len = i + p.daylen = i - p.dayi + p.yeari = i + 1 + p.setDay() + p.stateDate = dateDigitDotDot } - 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 - // Monday, 02-Jan-06 15:04:05 MST - // stateWeekdayCommaOffset - // Monday, 02 Jan 2006 15:04:05 -0700 - // Monday, 02 Jan 2006 15:04:05 +0100 - // stateWeekdayAbbrevComma - // Mon, 02 Jan 2006 15:04:05 MST - // stateWeekdayAbbrevCommaDash - // Mon, 02-Jan-06 15:04:05 MST - // stateWeekdayAbbrevCommaOffset - // Mon, 02 Jan 2006 15:04:05 -0700 - // Thu, 13 Jul 2017 08:58:40 +0100 - // stateWeekdayAbbrevCommaOffsetZone - // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) - switch { - case unicode.IsLetter(r): - continue - case r == ' ': - state = stateAlphaWS - case r == ',': - part1Len = i - if i == 3 { - state = stateWeekdayAbbrevComma - } else { - state = stateWeekdayComma - } - i++ - } - case stateWeekdayComma: // Starts alpha then comma - // Monday, 02 Jan 2006 15:04:05 MST - // stateWeekdayCommaDash // Monday, 02-Jan-06 15:04:05 MST - // stateWeekdayCommaOffset // 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 + switch { - case r == '-': - if i < 15 { - state = stateWeekdayCommaDash + case r == ' ': + p.stateDate = dateAlphaWs + case r == ',': + // 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.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 + } + + } + case dateWeekdayComma: + // Monday, 02 Jan 2006 15:04:05 MST + // 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 + } + 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") + } else { + p.stateTime = timeStart break iterRunes } - state = stateWeekdayCommaOffset - case r == '+': - state = stateWeekdayCommaOffset + //case r == '-': } - case stateWeekdayAbbrevComma: // Starts alpha then comma + case dateWeekdayAbbrevComma: // Mon, 02 Jan 2006 15:04:05 MST - // stateWeekdayAbbrevCommaDash + // 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 - // stateWeekdayAbbrevCommaOffset - // 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 - // 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 + 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 } - state = stateWeekdayAbbrevCommaOffset - case r == '+': - state = stateWeekdayAbbrevCommaOffset + + case r == '-': + p.stateDate = dateWeekdayAbbrevCommaDash + break iterRunes } - case stateWeekdayAbbrevCommaOffset: - // stateWeekdayAbbrevCommaOffset - // 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 - // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) - if r == '(' { - state = stateWeekdayAbbrevCommaOffsetZone - } - - case stateAlphaWS: // 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 switch { case unicode.IsLetter(r): - state = stateAlphaWSAlpha + p.set(0, "Mon") + p.stateDate = dateAlphaWsAlpha + //p.moi = i + p.set(i, "Jan") case unicode.IsDigit(r): - state = stateAlphaWSDigitComma + p.set(0, "Jan") + p.stateDate = dateAlphaWsDigit + p.dayi = i } - 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: + // dateAlphaWsDigit + // May 8, 2009 5:57:51 PM + if r == ',' { + p.daylen = i - p.dayi + p.setDay() + p.stateDate = dateAlphaWsDigitComma + } + case dateAlphaWsDigitComma: + // x + // May 8, 2009 5:57:51 PM + if r == ' ' { + p.stateDate = dateAlphaWsDigitCommaWs + p.yeari = i + 1 + } + case dateAlphaWsDigitCommaWs: + // x + // May 8, 2009 5:57:51 PM + if r == ' ' { + p.stateDate = dateAlphaWsDigitCommaWsYear + p.yearlen = i - p.yeari + p.setYear() + p.stateTime = timeStart + break iterRunes + } + + 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) + if r == ' ' { + if p.dayi > 0 { + p.daylen = i - p.dayi + p.setDay() + p.yeari = i + 1 + p.stateDate = dateAlphaWsAlphaYearmaybe + p.stateTime = timeStart + } + } else if unicode.IsDigit(r) { + if p.dayi == 0 { + p.dayi = i } } - - case stateAlphaWSAlpha: // Alpha, whitespace, alpha + case dateAlphaWsAlphaYearmaybe: + // x // 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 - } - case stateAlphaWSAlphaColon: // 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 - } else if r == '-' || r == '+' { - state = stateAlphaWSAlphaColonOffset - } - case stateAlphaWSAlphaColonAlpha: // 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 - } - case stateAlphaWSAlphaColonAlphaOffset: // 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 + 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 + p.setYear() + break iterRunes } + default: break iterRunes } } + p.coalesceDate(i) + if p.stateTime == timeStart { + // increment first one, since the i++ occurs at end of loop + i++ - switch state { - case stateDigit: + iterTimeRunes: + for ; i < len(datestr); i++ { + r := rune(datestr[i]) + + switch p.stateTime { + case timeStart: + // 22:43:22 + // 22:43 + // 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 + // 15:04:05 2008 + // 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 + // 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 + if p.houri == 0 { + p.houri = i + } + switch r { + case ',': + // 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 + p.seclen = i - p.seci + p.offseti = i + case '.': + p.stateTime = timePeriod + p.seclen = i - p.seci + p.msi = i + 1 + case 'Z': + p.stateTime = timeZ + if p.seci == 0 { + p.minlen = i - p.mini + } else { + p.seclen = i - p.seci + } + case ' ': + p.coalesceTime(i) + p.stateTime = timeWs + case ':': + 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 timeOffset: + // 19:55:00+0100 + // timeOffsetColon + // 15:04:05+07:00 + // 15:04:05-07:00 + if r == ':' { + p.stateTime = timeOffsetColon + } + case timeWs: + // 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 + // 15:04:05 -0700 + // timeWsOffsetColon + // 15:04:05 -07:00 + // timeWsOffsetColonAlpha + // 00:12:00 +00:00 UTC + // timeWsOffsetAlpha + // 00:12:00 +0000 UTC + // timeWsYear + // 00:12:00 2008 + // timeZ + // 15:04:05.99Z + 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 + p.stateTime = timeWsOffset + default: + if unicode.IsLetter(r) { + // 06:20:00 UTC + // 15:44:11 UTC+0100 2015 + p.tzi = i + p.stateTime = timeWsAlpha + //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 + switch r { + case '+', '-': + p.tzlen = i - p.tzi + 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 + 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 + if unicode.IsDigit(r) { + p.stateTime = timeWsAlphaZoneOffsetWsYear + } else { + p.extra = i - 1 + p.stateTime = timeWsAlphaZoneOffsetWsExtra + } + case timeWsAlphaZoneOffsetWsYear: + // 15:44:11 UTC+0100 2015 + if unicode.IsDigit(r) { + p.yearlen = i - p.yeari + 1 + if p.yearlen == 4 { + p.setYear() + } + } + 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 + 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 + } + + 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 + switch r { + case ':': + p.stateTime = timeWsOffsetColon + 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: + // 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 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 + // 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.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 = timePeriodWsAlpha + } + } + case timePeriodOffset: + // timePeriodOffset + // 19:55:00.799+0100 + // timePeriodOffsetColon + // 15:04:05.999-07:00 + if r == ':' { + p.stateTime = timePeriodOffsetColon + } + case timePeriodOffsetColon: + // timePeriodOffset + // timePeriodOffsetColon + // 15:04:05.999-07:00 + + case timePeriodWs: + // 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: + + // 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") + 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 + // 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 + } + + } + } + + switch p.stateTime { + case timeWsAlpha: + case timeWsYear: + p.yearlen = i - p.yeari + p.setYear() + case timeWsAlphaZoneOffsetWsExtra: + p.trimExtra() + case timePeriod: + p.mslen = i - p.msi + case timeOffset: + // 19:55:00+0100 + p.set(p.offseti, "-0700") + case timeWsOffset: + p.set(p.offseti, "-0700") + case timeOffsetColon: + // 15:04:05+07:00 + p.set(p.offseti, "-07: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") + } + p.coalesceTime(i) + } + + switch p.stateDate { + case dateDigit: // unixy timestamps ish // 1499979655583057426 nanoseconds // 1499979795437000 micro-seconds @@ -698,896 +1157,97 @@ iterRunes: return t.In(loc), nil } - case stateDigitDash: // starts digit then dash 02- + case dateDigitDash: + // 2006-01 + return p.parse() + + 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 stateDigitDashAlpha: + 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 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 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 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 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 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 stateDigitDashWs: // starts digit then dash 02- then whitespace 1 << 2 << 5 + 3 + case dateDigitDashDashWs: // 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 - } - } + return p.parse() - 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 dateDigitDashDashT: + return p.parse() - 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 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 dateDigitDotDot: + // 03.31.1981 + // 3.2.1981 + // 3.2.81 + p.setYear() + p.yearlen = i - p.yeari + return p.parse() - 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: - switch { - case len(datestr) == len("01.02.2006"): - return parse("01.02.2006", datestr, loc) - case len(datestr)-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 - } - } - } - - case stateDigitWs: - // 18 January 2018 - // 8 January 2018 - if part1Len == 1 { - return parse("2 January 2006", datestr, loc) - } - return parse("02 January 2006", datestr, loc) - // 02 Jan 2018 23:59 - case stateDigitWsMoShortColon: + case dateDigitWsMoYear: // 2 Jan 2018 23:59 // 02 Jan 2018 23:59 - if 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 stateDigitWsMoShortColonColon: // 02 Jan 2018 23:59:45 - if 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 stateDigitWsMoShortComma: // 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 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 stateAlphaWSAlphaColon: - // Mon Jan _2 15:04:05 2006 - return parse(time.ANSIC, datestr, loc) + case dateAlphaWsDigitCommaWsYear: + // May 8, 2009 5:57:51 PM + return p.parse() - case stateAlphaWSAlphaColonOffset: - // Mon Jan 02 15:04:05 -0700 2006 - return parse(time.RubyDate, datestr, loc) + case dateAlphaWsAlpha: + return p.parse() - case stateAlphaWSAlphaColonAlpha: - // Mon Jan _2 15:04:05 MST 2006 - return parse(time.UnixDate, datestr, loc) + case dateAlphaWsAlphaYearmaybe: + return p.parse() - case stateAlphaWSAlphaColonAlphaOffset: - // Mon Aug 10 15:44:11 UTC+0100 2015 - return parse("Mon Jan 02 15:04:05 MST-0700 2006", datestr, loc) - - case stateAlphaWSAlphaColonAlphaOffsetAlpha: - // 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 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 // 2014/10/13 - if 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 stateDigitSlashWSColon: // 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 - // 2014/04/08 22:05 - - if 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 - } - } - } - - case stateDigitSlashWSColonAMPM: // 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 - // 2014/4/8 22:05 PM - // 2014/04/08 22:05 PM - - if 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 stateDigitSlashWSColonColon: // 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 - // 03/1/2012 10:11:59 - // 3/01/2012 10:11:59 - // 4/8/14 22:05 - if 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 - } - } - } - - case stateDigitSlashWSColonColonAMPM: // 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 - // 03/1/2012 10:11:59 PM - // 3/01/2012 10:11:59 PM - - if 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 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 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 stateWeekdayCommaDash: // 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 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{ - "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 stateWeekdayAbbrevCommaDash: + 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 stateWeekdayAbbrevCommaOffset: - // 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 - 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 - } - } - - case stateWeekdayAbbrevCommaOffsetZone: - // 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) - } - return parse("Mon, _2 Jan 2006 15:04:05 -0700 (MST)", datestr, loc) } return time.Time{}, fmt.Errorf("Could not find date format for %s", datestr) diff --git a/parseany_test.go b/parseany_test.go index 0aeeade..7e7ac30 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 @@ -106,8 +113,21 @@ 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("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) { + /* + 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 @@ -142,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 @@ -158,16 +180,20 @@ 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))) + 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 + // 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))) @@ -222,6 +248,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))) @@ -622,3 +658,22 @@ 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)) + assert.True(t, len(p.ds()) > 0) + assert.True(t, len(p.ts()) > 0) +}