diff --git a/parseany.go b/parseany.go index 307402f..87333ba 100644 --- a/parseany.go +++ b/parseany.go @@ -59,7 +59,7 @@ const ( dateDigit dateDigitSt dateYearDash - dateYearDashAlphaDash + dateYearDashAlpha dateYearDashDash dateYearDashDashWs // 6 dateYearDashDashT @@ -77,25 +77,23 @@ const ( dateDigitSlash dateDigitYearSlash dateDigitSlashAlpha // 21 + dateDigitSlashAlphaSlash dateDigitColon dateDigitChineseYear dateDigitChineseYearWs dateDigitWs - dateDigitWsMoYear // 26 + dateDigitWsMoYear // 27 dateAlpha dateAlphaWs dateAlphaWsDigit - dateAlphaWsDigitMore // 30 + dateAlphaWsDigitMore // 31 dateAlphaWsDigitMoreWs dateAlphaWsDigitMoreWsYear - dateAlphaWsMonth - dateAlphaWsDigitYearmaybe - dateAlphaWsMonthMore - dateAlphaWsMonthSuffix - dateAlphaWsMore - dateAlphaWsAtTime + dateAlphaWsDigitYearMaybe + dateVariousDaySuffix + dateAlphaFullMonthWs + dateAlphaFullMonthWsDayWs dateAlphaWsAlpha - dateAlphaWsAlphaYearmaybe // 40 dateAlphaPeriodWsDigit dateAlphaSlash dateAlphaSlashDigit @@ -351,6 +349,8 @@ iterRunes: return p, unknownErr(datestr) } p.dayi = i + 1 + } else { + return p, unknownErr(datestr) } } else { if p.daylen == 0 { @@ -359,9 +359,10 @@ iterRunes: return p, unknownErr(datestr) } p.moi = i + 1 + } else { + return p, unknownErr(datestr) } } - } case ':': @@ -384,6 +385,8 @@ iterRunes: return p, unknownErr(datestr) } p.dayi = i + 1 + } else { + return p, unknownErr(datestr) } } else { if p.daylen == 0 { @@ -392,6 +395,8 @@ iterRunes: return p, unknownErr(datestr) } p.moi = i + 1 + } else { + return p, unknownErr(datestr) } } } @@ -418,6 +423,8 @@ iterRunes: return p, unknownErr(datestr) } p.dayi = i + 1 + } else { + return p, unknownErr(datestr) } } else { if p.daylen == 0 { @@ -426,6 +433,8 @@ iterRunes: return p, unknownErr(datestr) } p.moi = i + 1 + } else { + return p, unknownErr(datestr) } } } @@ -468,10 +477,13 @@ iterRunes: case 's', 'S', 'r', 'R', 't', 'T', 'n', 'N': // 1st January 2018 // 2nd Jan 2018 23:59 - // st, rd, nd, st - p.stateDate = dateAlphaWsMonthSuffix + // st, rd, nd, th + p.stateDate = dateVariousDaySuffix i-- default: + if !unicode.IsDigit(r) { + return p, unknownErr(datestr) + } continue } p.part1Len = i @@ -487,8 +499,9 @@ iterRunes: // 2020-08-17T17:00:00:000+0100 // dateYearDashDashWs // 2013-04-01 22:43:22 - // dateYearDashAlphaDash - // 2013-Feb-03 + // dateYearDashAlpha + // 2013-Feb-03 + // 2013-February-03 switch r { case '-': p.molen = i - p.moi @@ -499,7 +512,9 @@ iterRunes: } default: if unicode.IsLetter(r) { - p.stateDate = dateYearDashAlphaDash + p.stateDate = dateYearDashAlpha + } else if !unicode.IsDigit(r) { + return p, unknownErr(datestr) } } @@ -510,6 +525,7 @@ iterRunes: // 2013-04-01 22:43:22 // dateYearDashDashOffset // 2020-07-20+00:00 + // (these states are also reused after dateYearDashAlpha, like 2020-July-20...) switch r { case '+', '-': p.offseti = i @@ -534,28 +550,58 @@ iterRunes: return p, unknownErr(datestr) } break iterRunes + default: + if !unicode.IsDigit(r) { + return p, unknownErr(datestr) + } } case dateYearDashDashT: // dateYearDashDashT // 2006-01-02T15:04:05Z07:00 // 2020-08-17T17:00:00:000+0100 + // (this state should never be reached, we break out when in this state) + return p, unknownErr(datestr) case dateYearDashDashOffset: // 2020-07-20+00:00 switch r { case ':': p.set(p.offseti, "-07:00") + default: + if !unicode.IsDigit(r) { + return p, unknownErr(datestr) + } } - case dateYearDashAlphaDash: - // 2013-Feb-03 + case dateYearDashAlpha: + // dateYearDashAlpha + // 2013-Feb-03 + // 2013-February-03 switch r { case '-': p.molen = i - p.moi - p.set(p.moi, "Jan") - p.dayi = i + 1 + // Must be a valid short or long month + if p.molen == 3 { + p.set(p.moi, "Jan") + p.dayi = i + 1 + p.stateDate = dateYearDashDash + } else { + possibleFullMonth := strings.ToLower(p.datestr[p.moi:(p.moi + p.molen)]) + if i > 3 && isMonthFull(possibleFullMonth) { + p.fullMonth = possibleFullMonth + p.dayi = i + 1 + p.stateDate = dateYearDashDash + } else { + return p, unknownErr(datestr) + } + } + default: + if !unicode.IsLetter(r) { + return p, unknownErr(datestr) + } } + case dateDigitDash: // 13-Feb-03 // 29-Jun-2016 @@ -578,6 +624,10 @@ iterRunes: p.set(p.moi, "Jan") p.yeari = i + 1 p.stateDate = dateDigitDashAlphaDash + default: + if !unicode.IsLetter(r) { + return p, unknownErr(datestr) + } } case dateDigitDashDigit: @@ -594,6 +644,10 @@ iterRunes: } else { return p, unknownErr(datestr) } + default: + if !unicode.IsDigit(r) { + return p, unknownErr(datestr) + } } case dateDigitDashAlphaDash, dateDigitDashDigitDash: @@ -668,7 +722,7 @@ iterRunes: break iterRunes } default: - if !unicode.IsDigit(r) && !unicode.IsLetter(r) && p.link > 0 { + if !unicode.IsDigit(r) { return p, unknownErr(datestr) } } @@ -697,21 +751,47 @@ iterRunes: } p.dayi = i + 1 } + default: + if !unicode.IsDigit(r) { + return p, unknownErr(datestr) + } } case dateDigitSlashAlpha: // 06/May/2008 + // 06/September/2008 switch r { case '/': // | // 06/May/2008 if p.molen == 0 { - p.set(p.moi, "Jan") - p.yeari = i + 1 + p.molen = i - p.moi + if p.molen == 3 { + p.set(p.moi, "Jan") + p.yeari = i + 1 + p.stateDate = dateDigitSlashAlphaSlash + } else { + possibleFullMonth := strings.ToLower(p.datestr[p.moi:(p.moi + p.molen)]) + if i > 3 && isMonthFull(possibleFullMonth) { + p.fullMonth = possibleFullMonth + p.yeari = i + 1 + p.stateDate = dateDigitSlashAlphaSlash + } else { + return p, unknownErr(datestr) + } + } + } else { + return p, unknownErr(datestr) } - // We aren't breaking because we are going to re-use this case - // to find where the date starts, and possible time begins + default: + if !unicode.IsLetter(r) { + return p, unknownErr(datestr) + } + } + + case dateDigitSlashAlphaSlash: + switch r { case ' ': fallthrough case ':': @@ -723,6 +803,10 @@ iterRunes: } } break iterRunes + default: + if !unicode.IsDigit(r) { + return p, unknownErr(datestr) + } } case dateDigitSlash: @@ -771,6 +855,10 @@ iterRunes: } } break iterRunes + default: + if !unicode.IsDigit(r) { + return p, unknownErr(datestr) + } } case dateDigitColon: @@ -831,6 +919,10 @@ iterRunes: p.yeari = i + 1 } } + default: + if !unicode.IsDigit(r) { + return p, unknownErr(datestr) + } } case dateDigitWs: @@ -876,6 +968,10 @@ iterRunes: p.set(p.moi, "Jan") p.stateDate = dateDigitWsMoYear } + default: + if !unicode.IsLetter(r) { + return p, unknownErr(datestr) + } } case dateDigitWsMoYear: @@ -898,6 +994,10 @@ iterRunes: return p, unknownErr(datestr) } break iterRunes + default: + if !unicode.IsDigit(r) { + return p, unknownErr(datestr) + } } case dateYearWs: @@ -920,6 +1020,8 @@ iterRunes: return p, unknownErr(datestr) } } + } else if !unicode.IsLetter(r) { + return p, unknownErr(datestr) } case dateYearWsMonthWs: // 2013 Jan 06 15:04:05 @@ -936,6 +1038,10 @@ iterRunes: p.setDay() p.stateTime = timeStart break iterRunes + default: + if !unicode.IsDigit(r) { + return p, unknownErr(datestr) + } } case dateDigitChineseYear: @@ -964,13 +1070,18 @@ iterRunes: p.stateDate = dateDigitChineseYearWs p.stateTime = timeStart break iterRunes + default: + if !unicode.IsDigit(r) { + return p, unknownErr(datestr) + } } case dateDigitDot: - // This is the 2nd period // 3.31.2014 // 08.21.71 // 2014.05 // 2018.09.30 + + // This is the 2nd period if r == '.' { if p.moi == 0 { // 3.31.2014 @@ -998,14 +1109,16 @@ iterRunes: } p.stateDate = dateDigitDotDot } + } else if !unicode.IsDigit(r) { + return p, unknownErr(datestr) } case dateDigitDotDot: - // dateYearDashDashT + // dateDigitDotDotT // 2006.01.02T15:04:05Z07:00 - // dateYearDashDashWs + // dateDigitDotDotWs // 2013.04.01 22:43:22 - // dateYearDashDashOffset + // dateDigitDotDotOffset // 2020.07.20+00:00 switch r { case '+', '-': @@ -1031,18 +1144,28 @@ iterRunes: return p, unknownErr(datestr) } break iterRunes + default: + if !unicode.IsDigit(r) { + return p, unknownErr(datestr) + } } case dateDigitDotDotT: - // dateYearDashDashT + // dateDigitDotDotT // 2006-01-02T15:04:05Z07:00 // 2020-08-17T17:00:00:000+0100 + // (should be unreachable, we break in this state) + return p, unknownErr(datestr) case dateDigitDotDotOffset: // 2020-07-20+00:00 switch r { case ':': p.set(p.offseti, "-07:00") + default: + if !unicode.IsDigit(r) { + return p, unknownErr(datestr) + } } case dateAlpha: @@ -1056,10 +1179,9 @@ iterRunes: // dateAlphaWsDigit // May 8, 2009 5:57:51 PM // oct 1, 1970 - // dateAlphaWsMonth - // April 8, 2009 - // dateAlphaWsMore - // dateAlphaWsAtTime + // dateAlphaFullMonthWs + // January 02, 2006 3:04pm + // January 02, 2006 3:04pm MST-07 // January 02, 2006 at 3:04pm MST-07 // // dateAlphaPeriodWsDigit @@ -1085,47 +1207,45 @@ iterRunes: // Mon, 02-Jan-06 15:04:05 MST switch { case r == ' ': + // This could be a weekday or a month, detect and parse both cases. + // skip & return to dateStart + // Tue 05 May 2020, 05:05:05 + // Tuesday 05 May 2020, 05:05:05 + // Mon Jan 2 15:04:05 2006 + // Monday Jan 2 15:04:05 2006 + maybeDayOrMonth := strings.ToLower(p.datestr[0:i]) + if isDay(maybeDayOrMonth) { + // using skip throws off indices used by other code; saner to restart + newDateStr := p.datestr[i+1:] + putBackParser(p) + return parseTime(newDateStr, loc) + } + // X // April 8, 2009 if i > 3 { - // Check to see if the alpha is name of month? or Day? - month := strings.ToLower(p.datestr[0:i]) - if isMonthFull(month) { + // Expecting a full month name at this point + if isMonthFull(maybeDayOrMonth) { p.moi = 0 p.molen = i - p.fullMonth = month - // len(" 31, 2018") = 9 - if len(p.datestr[i:]) < 10 { - // April 8, 2009 - p.stateDate = dateAlphaWsMonth - } else { - p.stateDate = dateAlphaWsMore - } + p.fullMonth = maybeDayOrMonth + p.stateDate = dateAlphaFullMonthWs p.dayi = i + 1 break + } else { + return p, unknownErr(datestr) } - } else { - // This is possibly ambiguous? May will parse as either though. - // So, it could return in-correct format. + } else if i == 3 { // dateAlphaWs // May 05, 2005, 05:05:05 // May 05 2005, 05:05:05 // Jul 05, 2005, 05:05:05 // May 8 17:57:51 2009 // May 8 17:57:51 2009 - // skip & return to dateStart - // Tue 05 May 2020, 05:05:05 - // Mon Jan 2 15:04:05 2006 - - maybeDay := strings.ToLower(p.datestr[0:i]) - if isDay(maybeDay) { - // using skip throws off indices used by other code; saner to restart - newDateStr := p.datestr[i+1:] - putBackParser(p) - return parseTime(newDateStr, loc) - } p.stateDate = dateAlphaWs + } else { + return p, unknownErr(datestr) } case r == ',': @@ -1135,12 +1255,15 @@ iterRunes: p.stateDate = dateWeekdayAbbrevComma p.set(0, "Mon") } else { - p.stateDate = dateWeekdayComma - 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 + maybeDay := strings.ToLower(p.datestr[0:i]) + if isDay(maybeDay) { + p.stateDate = dateWeekdayComma + // Just skip past the weekday, it contains no valuable info + p.skip = i + 2 + i++ + } else { + return p, unknownErr(datestr) + } } case r == '.': // sept. 28, 2017 @@ -1181,6 +1304,10 @@ iterRunes: return p, unknownErr(datestr) } } + default: + if !unicode.IsLetter(r) { + return p, unknownErr(datestr) + } } case dateAlphaWs: @@ -1208,6 +1335,10 @@ iterRunes: p.set(0, "Jan") p.stateDate = dateAlphaWsDigit p.dayi = i + case r == ' ': + // continue + default: + return p, unknownErr(datestr) } case dateAlphaWsDigit: @@ -1231,13 +1362,15 @@ iterRunes: return p, unknownErr(datestr) } p.yeari = i + 1 - p.stateDate = dateAlphaWsDigitYearmaybe + p.stateDate = dateAlphaWsDigitYearMaybe p.stateTime = timeStart } else if unicode.IsLetter(r) { - p.stateDate = dateAlphaWsMonthSuffix + p.stateDate = dateVariousDaySuffix i-- + } else if !unicode.IsDigit(r) { + return p, unknownErr(datestr) } - case dateAlphaWsDigitYearmaybe: + case dateAlphaWsDigitYearMaybe: // x // May 8 2009 5:57:51 PM // May 8 17:57:51 2009 @@ -1257,6 +1390,8 @@ iterRunes: return p, unknownErr(datestr) } break iterRunes + } else if !unicode.IsDigit(r) { + return p, unknownErr(datestr) } case dateAlphaWsDigitMore: // x @@ -1268,6 +1403,8 @@ iterRunes: if r == ' ' { p.yeari = i + 1 p.stateDate = dateAlphaWsDigitMoreWs + } else { + return p, unknownErr(datestr) } case dateAlphaWsDigitMoreWs: // x @@ -1292,35 +1429,105 @@ iterRunes: } p.stateTime = timeStart break iterRunes + default: + if r != '\'' && !unicode.IsDigit(r) { + return p, unknownErr(datestr) + } } - case dateAlphaWsMonth: - // April 8, 2009 - // April 8 2009 + case dateVariousDaySuffix: + // x + // April 8th, 2009 + // April 8th 2009 switch r { - case ' ': - fallthrough - case ',': - // x - // June 8, 2009 - // x - // June 8 2009 - if p.daylen == 0 { + case 't', 'T': + if p.nextIs(i, 'h') || p.nextIs(i, 'H') { + if len(p.datestr) > i+2 { + newDateStr := p.datestr[0:i] + p.datestr[i+2:] + putBackParser(p) + return parseTime(newDateStr, loc, opts...) + } + } + return p, unknownErr(datestr) + case 'n', 'N': + if p.nextIs(i, 'd') || p.nextIs(i, 'D') { + if len(p.datestr) > i+2 { + newDateStr := p.datestr[0:i] + p.datestr[i+2:] + putBackParser(p) + return parseTime(newDateStr, loc, opts...) + } + } + return p, unknownErr(datestr) + case 's', 'S': + if p.nextIs(i, 't') || p.nextIs(i, 'T') { + if len(p.datestr) > i+2 { + newDateStr := p.datestr[0:i] + p.datestr[i+2:] + putBackParser(p) + return parseTime(newDateStr, loc, opts...) + } + } + return p, unknownErr(datestr) + case 'r', 'R': + if p.nextIs(i, 'd') || p.nextIs(i, 'D') { + if len(p.datestr) > i+2 { + newDateStr := p.datestr[0:i] + p.datestr[i+2:] + putBackParser(p) + return parseTime(newDateStr, loc, opts...) + } + } + return p, unknownErr(datestr) + default: + return p, unknownErr(datestr) + } + + case dateAlphaFullMonthWs: + // January 02, 2006, 15:04:05 + // January 02 2006, 15:04:05 + // January 2nd, 2006, 15:04:05 + // January 2nd 2006, 15:04:05 + // September 17, 2012 at 5:00pm UTC-05 + switch { + case r == ',': + // x + // January 02, 2006, 15:04:05 + if p.nextIs(i, ' ') { p.daylen = i - p.dayi if !p.setDay() { return p, unknownErr(datestr) } + p.yeari = i + 2 + p.stateDate = dateAlphaFullMonthWsDayWs + i++ + } else { + return p, unknownErr(datestr) } - case 's', 'S', 'r', 'R', 't', 'T', 'n', 'N': - // st, rd, nd, st + + case r == ' ': + // x + // January 02 2006, 15:04:05 + p.daylen = i - p.dayi + if !p.setDay() { + return p, unknownErr(datestr) + } + p.yeari = i + 1 + p.stateDate = dateAlphaFullMonthWsDayWs + case unicode.IsDigit(r): + // XX + // January 02, 2006, 15:04:05 + continue + case unicode.IsLetter(r): + // X + // January 2nd, 2006, 15:04:05 + p.daylen = i - p.dayi + if !p.setDay() { + return p, unknownErr(datestr) + } + p.stateDate = dateVariousDaySuffix i-- - p.stateDate = dateAlphaWsMonthSuffix default: - if p.daylen > 0 && p.yeari == 0 { - p.yeari = i - } + return p, unknownErr(datestr) } - case dateAlphaWsMonthMore: + case dateAlphaFullMonthWsDayWs: // X // January 02, 2006, 15:04:05 // January 02 2006, 15:04:05 @@ -1342,87 +1549,10 @@ iterRunes: } p.stateTime = timeStart break iterRunes - } - case dateAlphaWsMonthSuffix: - // x - // April 8th, 2009 - // April 8th 2009 - switch r { - case 't', 'T': - if p.nextIs(i, 'h') || p.nextIs(i, 'H') { - if len(p.datestr) > i+2 { - newDateStr := p.datestr[0:i] + p.datestr[i+2:] - putBackParser(p) - return parseTime(newDateStr, loc, opts...) - } - } - case 'n', 'N': - if p.nextIs(i, 'd') || p.nextIs(i, 'D') { - if len(p.datestr) > i+2 { - newDateStr := p.datestr[0:i] + p.datestr[i+2:] - putBackParser(p) - return parseTime(newDateStr, loc, opts...) - } - } - case 's', 'S': - if p.nextIs(i, 't') || p.nextIs(i, 'T') { - if len(p.datestr) > i+2 { - newDateStr := p.datestr[0:i] + p.datestr[i+2:] - putBackParser(p) - return parseTime(newDateStr, loc, opts...) - } - } - case 'r', 'R': - if p.nextIs(i, 'd') || p.nextIs(i, 'D') { - if len(p.datestr) > i+2 { - newDateStr := p.datestr[0:i] + p.datestr[i+2:] - putBackParser(p) - return parseTime(newDateStr, loc, opts...) - } - } - } - case dateAlphaWsMore: - // January 02, 2006, 15:04:05 - // January 02 2006, 15:04:05 - // January 2nd, 2006, 15:04:05 - // January 2nd 2006, 15:04:05 - // September 17, 2012 at 5:00pm UTC-05 - switch { - case r == ',': - // x - // January 02, 2006, 15:04:05 - if p.nextIs(i, ' ') { - p.daylen = i - p.dayi - if !p.setDay() { - return p, unknownErr(datestr) - } - p.yeari = i + 2 - p.stateDate = dateAlphaWsMonthMore - i++ - } - - case r == ' ': - // x - // January 02 2006, 15:04:05 - p.daylen = i - p.dayi - if !p.setDay() { + default: + if !unicode.IsDigit(r) { return p, unknownErr(datestr) } - p.yeari = i + 1 - p.stateDate = dateAlphaWsMonthMore - case unicode.IsDigit(r): - // XX - // January 02, 2006, 15:04:05 - continue - case unicode.IsLetter(r): - // X - // January 2nd, 2006, 15:04:05 - p.daylen = i - p.dayi - if !p.setDay() { - return p, unknownErr(datestr) - } - p.stateDate = dateAlphaWsMonthSuffix - i-- } case dateAlphaPeriodWsDigit: @@ -1504,11 +1634,19 @@ iterRunes: } else if p.yeari == 0 { p.yeari = i + 1 p.molen = i - p.moi - p.set(p.moi, "Jan") + if p.molen == 3 { + p.set(p.moi, "Jan") + } else { + return p, unknownErr(datestr) + } } else { p.stateTime = timeStart break iterRunes } + default: + if !unicode.IsDigit(r) && !unicode.IsLetter(r) { + return p, unknownErr(datestr) + } } case dateWeekdayAbbrevComma: // Mon, 02 Jan 2006 15:04:05 MST @@ -1536,7 +1674,11 @@ iterRunes: p.moi = i + 1 } else if p.yeari == 0 { p.molen = i - p.moi - offset - p.set(p.moi, "Jan") + if p.molen == 3 { + p.set(p.moi, "Jan") + } else { + return p, unknownErr(datestr) + } p.yeari = i + 1 } else { p.yearlen = i - p.yeari - offset @@ -1546,10 +1688,15 @@ iterRunes: p.stateTime = timeStart break iterRunes } + default: + if !unicode.IsDigit(r) && !unicode.IsLetter(r) { + return p, unknownErr(datestr) + } } default: - break iterRunes + // Reaching an unhandled state unexpectedly should always fail parsing + return p, unknownErr(datestr) } } if !p.coalesceDate(i) { @@ -2241,13 +2388,10 @@ iterRunes: } return p, nil - case dateYearDashAlphaDash: + case dateYearDashAlpha: // 2013-Feb-03 // 2013-Feb-3 - p.daylen = i - p.dayi - if !p.setDay() { - return p, unknownErr(datestr) - } + // 2013-February-3 return p, nil case dateYearDashDashWs: @@ -2347,14 +2491,16 @@ iterRunes: // 12 Feb 2006, 19:17 return p, nil - case dateAlphaWsMonth: - p.yearlen = i - p.yeari - if !p.setYear() { - return p, unknownErr(datestr) + case dateAlphaFullMonthWs: + if p.stateTime == timeIgnore && p.yearlen == 0 { + p.yearlen = i - p.yeari + if !p.setYear() { + return p, unknownErr(datestr) + } } return p, nil - case dateAlphaWsMonthMore: + case dateAlphaFullMonthWsDayWs: return p, nil case dateAlphaWsDigitMoreWs: @@ -2376,7 +2522,7 @@ iterRunes: case dateAlphaWsDigit: return p, nil - case dateAlphaWsDigitYearmaybe: + case dateAlphaWsDigitYearMaybe: return p, nil case dateDigitSlash: @@ -2385,7 +2531,7 @@ iterRunes: // 01/02/2006 return p, nil - case dateDigitSlashAlpha: + case dateDigitSlashAlphaSlash: // 03/Jun/2014 return p, nil @@ -2500,11 +2646,11 @@ func putBackParser(p *parser) { } // we'll be reusing the backing memory for the format byte slice, put it back // to maximum capacity - if cap(p.format) == longestPossibleDateStr { - p.format = p.format[:longestPossibleDateStr] + if cap(p.format) == formatBufferCapacity { + p.format = p.format[:formatBufferCapacity] } else { - // the parsing process replaced this, get back a new one with the right cap - p.format = make([]byte, 0, longestPossibleDateStr) + // the parsing improperly process replaced this, get back a new one with the right cap + p.format = make([]byte, 0, formatBufferCapacity) } // clear out pointers so we don't leak memory we don't need any longer p.loc = nil diff --git a/parseany_test.go b/parseany_test.go index e2940e7..e99904e 100644 --- a/parseany_test.go +++ b/parseany_test.go @@ -23,36 +23,68 @@ type dateTest struct { var testInputs = []dateTest{ {in: "oct 7, 1970", out: "1970-10-07 00:00:00 +0000 UTC"}, + {in: "oct 7, 1970 11:15:26pm", out: "1970-10-07 23:15:26 +0000 UTC"}, {in: "oct 7, '70", out: "1970-10-07 00:00:00 +0000 UTC"}, + {in: "oct 7, '70 11:15:26pm", out: "1970-10-07 23:15:26 +0000 UTC"}, {in: "Oct 7, '70", out: "1970-10-07 00:00:00 +0000 UTC"}, + {in: "Oct 7, '70 11:15:26pm", out: "1970-10-07 23:15:26 +0000 UTC"}, {in: "Oct. 7, '70", out: "1970-10-07 00:00:00 +0000 UTC"}, + {in: "Oct. 7, '70 11:15:26pm", out: "1970-10-07 23:15:26 +0000 UTC"}, {in: "oct. 7, '70", out: "1970-10-07 00:00:00 +0000 UTC"}, + {in: "oct. 7, '70 11:15:26pm", out: "1970-10-07 23:15:26 +0000 UTC"}, {in: "oct. 7, 1970", out: "1970-10-07 00:00:00 +0000 UTC"}, + {in: "oct. 7, 1970 11:15:26pm", out: "1970-10-07 23:15:26 +0000 UTC"}, {in: "Sept. 7, '70", out: "1970-09-07 00:00:00 +0000 UTC"}, + {in: "Sept. 7, '70 11:15:26pm", out: "1970-09-07 23:15:26 +0000 UTC"}, {in: "sept. 7, 1970", out: "1970-09-07 00:00:00 +0000 UTC"}, + {in: "sept. 7, 1970 11:15:26pm", out: "1970-09-07 23:15:26 +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: "May 8, 2009, 5:7:51 PM", out: "2009-05-08 17:07:51 +0000 UTC"}, + {in: "June 8 2009", out: "2009-06-08 00:00:00 +0000 UTC"}, + {in: "June 8, 2009", out: "2009-06-08 00:00:00 +0000 UTC"}, + {in: "February 8th 2009", out: "2009-02-08 00:00:00 +0000 UTC"}, + {in: "February 8th, 2009", out: "2009-02-08 00:00:00 +0000 UTC"}, + {in: "September 3rd 2009", out: "2009-09-03 00:00:00 +0000 UTC"}, + {in: "September 3rd, 2009", out: "2009-09-03 00:00:00 +0000 UTC"}, + {in: "June 8 2009 11:15:26pm", out: "2009-06-08 23:15:26 +0000 UTC"}, + {in: "June 8, 2009 11:15:26pm", out: "2009-06-08 23:15:26 +0000 UTC"}, + {in: "February 8th 2009 11:15:26pm", out: "2009-02-08 23:15:26 +0000 UTC"}, + {in: "February 8th, 2009 11:15:26pm", out: "2009-02-08 23:15:26 +0000 UTC"}, + {in: "September 3rd 2009 11:15:26pm", out: "2009-09-03 23:15:26 +0000 UTC"}, + {in: "September 3rd, 2009 11:15:26pm", out: "2009-09-03 23:15:26 +0000 UTC"}, {in: "7 oct 70", out: "1970-10-07 00:00:00 +0000 UTC"}, + {in: "7 oct 70 11:15:26pm", out: "1970-10-07 23:15:26 +0000 UTC"}, {in: "7 oct 1970", out: "1970-10-07 00:00:00 +0000 UTC"}, + {in: "7 oct 1970 11:15:26pm", out: "1970-10-07 23:15:26 +0000 UTC"}, {in: "7 May 1970", out: "1970-05-07 00:00:00 +0000 UTC"}, + {in: "7 May 1970 11:15:26pm", out: "1970-05-07 23:15:26 +0000 UTC"}, {in: "7 Sep 1970", out: "1970-09-07 00:00:00 +0000 UTC"}, + {in: "7 Sep 1970 11:15:26pm", out: "1970-09-07 23:15:26 +0000 UTC"}, {in: "7 June 1970", out: "1970-06-07 00:00:00 +0000 UTC"}, + {in: "7 June 1970 11:15:26pm", out: "1970-06-07 23:15:26 +0000 UTC"}, {in: "7 September 1970", out: "1970-09-07 00:00:00 +0000 UTC"}, + {in: "7 September 1970 11:15:26pm", out: "1970-09-07 23:15:26 +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"}, + {in: "Monday Jan 2 15:04:05 2006", out: "2006-01-02 15:04:05 +0000 UTC"}, + {in: "Thursday May 8 17:57:51 2009", out: "2009-05-08 17:57:51 +0000 UTC"}, + {in: "Thursday May 8 17:57:51 2009", out: "2009-05-08 17:57:51 +0000 UTC"}, // ANSIC_GLIBC = "Mon 02 Jan 2006 03:04:05 PM UTC" {in: "Mon 02 Jan 2006 03:04:05 PM UTC", out: "2006-01-02 15:04:05 +0000 UTC", zname: "UTC"}, {in: "Mon 02 Jan 2006 03:04:05 PM CEST", out: "2006-01-02 15:04:05 +0000 UTC", zname: "CEST"}, {in: "Mon 30 Sep 2018 09:09:09 PM UTC", out: "2018-09-30 21:09:09 +0000 UTC", zname: "UTC"}, {in: "Mon 30 Sep 2018 09:09:09 PM CEST", out: "2018-09-30 21:09:09 +0000 UTC", zname: "CEST"}, + {in: "Mon 02 Jan 2006", out: "2006-01-02 00:00:00 +0000 UTC"}, + {in: "Monday 02 Jan 2006 03:04:05 PM UTC", out: "2006-01-02 15:04:05 +0000 UTC", zname: "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 11:57:51 -0700 2009", out: "2009-05-08 18:57:51 +0000 UTC"}, + {in: "Thursday 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", zname: "MST"}, {in: "Thu May 8 17:57:51 MST 2009", out: "2009-05-08 17:57:51 +0000 UTC", zname: "MST"}, @@ -62,6 +94,7 @@ var testInputs = []dateTest{ {in: "Thu May 08 17:57:51 CEST 2009", out: "2009-05-08 15:57:51 +0000 UTC", loc: "Europe/Berlin", zname: "CEST"}, {in: "Thu May 08 05:05:07 PST 2009", out: "2009-05-08 05:05:07 +0000 UTC", zname: "PST"}, {in: "Thu May 08 5:5:7 PST 2009", out: "2009-05-08 05:05:07 +0000 UTC", zname: "PST"}, + {in: "Thursday May 08 05:05:07 PST 2009", out: "2009-05-08 05:05:07 +0000 UTC", zname: "PST"}, // Day Month dd time {in: "Mon Aug 10 15:44:11 UTC+0000 2015", out: "2015-08-10 15:44:11 +0000 UTC", zname: "UTC"}, {in: "Mon Aug 10 15:44:11 PST-0700 2015", out: "2015-08-10 22:44:11 +0000 UTC", zname: "PST"}, @@ -75,6 +108,8 @@ var testInputs = []dateTest{ {in: "Fri Jul 3 2015 06:04:07 UTC+0100 (GMT Daylight Time)", out: "2015-07-03 05:04:07 +0000 UTC"}, {in: "Fri Jul 3 2015 06:04:07 PST-0700 (Pacific Daylight Time)", out: "2015-07-03 13:04:07 +0000 UTC", zname: "PST"}, {in: "Fri Jul 3 2015 06:04:07 CEST-0700 (Central European Summer Time)", out: "2015-07-03 13:04:07 +0000 UTC", zname: "CEST"}, + {in: "Fri Jul 3 2015", out: "2015-07-03 00:00:00 +0000 UTC"}, + {in: "Fri Jul 3 2015 11:15:26pm", out: "2015-07-03 23:15:26 +0000 UTC"}, // Month dd, yyyy at time {in: "January 17, 2012 at 18:17:16", out: "2012-01-17 18:17:16 +0000 UTC"}, {in: "February 17, 2012 at 18:17:16", out: "2012-02-17 18:17:16 +0000 UTC"}, @@ -88,10 +123,12 @@ var testInputs = []dateTest{ {in: "OCTober 17, 2012 at 18:17:16", out: "2012-10-17 18:17:16 +0000 UTC"}, {in: "noVEMBER 17, 2012 at 18:17:16", out: "2012-11-17 18:17:16 +0000 UTC"}, {in: "December 17, 2012 at 18:17:16", out: "2012-12-17 18:17:16 +0000 UTC"}, + {in: "September 17 2012 at 5:00pm UTC-05", out: "2012-09-17 22:00:00 +0000 UTC", zname: ""}, // empty zone name, special case of UTC+NNNN {in: "September 17, 2012 at 5:00pm UTC-05", out: "2012-09-17 22:00:00 +0000 UTC", zname: ""}, // empty zone name, special case of UTC+NNNN {in: "September 17, 2012 at 10:09am PST-08", out: "2012-09-17 18:09:00 +0000 UTC", zname: "PST"}, {in: "September 17, 2012 at 10:09am CEST+02", out: "2012-09-17 08:09:00 +0000 UTC", zname: "CEST"}, {in: "September 17, 2012, 10:10:09", out: "2012-09-17 10:10:09 +0000 UTC"}, + {in: "May 17 2012 at 10:09am PST-08", out: "2012-05-17 18:09:00 +0000 UTC", zname: "PST"}, {in: "May 17, 2012 at 10:09am PST-08", out: "2012-05-17 18:09:00 +0000 UTC", zname: "PST"}, {in: "May 17, 2012 AT 10:09am PST-08", out: "2012-05-17 18:09:00 +0000 UTC", zname: "PST"}, {in: "May 17, 2012 AT 10:09am CEST+02", out: "2012-05-17 08:09:00 +0000 UTC", zname: "CEST"}, @@ -135,6 +172,21 @@ var testInputs = []dateTest{ {in: "June 2nd 2012", out: "2012-06-02 00:00:00 +0000 UTC"}, {in: "June 22nd, 2012", out: "2012-06-22 00:00:00 +0000 UTC"}, {in: "June 22nd 2012", out: "2012-06-22 00:00:00 +0000 UTC"}, + {in: "September 17th, 2012 11:15:26pm", out: "2012-09-17 23:15:26 +0000 UTC"}, + {in: "September 17th 2012 11:15:26pm", out: "2012-09-17 23:15:26 +0000 UTC"}, + {in: "September 7th, 2012 11:15:26pm", out: "2012-09-07 23:15:26 +0000 UTC"}, + {in: "September 7th 2012 11:15:26pm", out: "2012-09-07 23:15:26 +0000 UTC"}, + {in: "September 7tH 2012 11:15:26pm", out: "2012-09-07 23:15:26 +0000 UTC"}, + {in: "May 1st 2012 11:15:26pm", out: "2012-05-01 23:15:26 +0000 UTC"}, + {in: "May 1st, 2012 11:15:26pm", out: "2012-05-01 23:15:26 +0000 UTC"}, + {in: "May 21st 2012 11:15:26pm", out: "2012-05-21 23:15:26 +0000 UTC"}, + {in: "May 21st, 2012 11:15:26pm", out: "2012-05-21 23:15:26 +0000 UTC"}, + {in: "May 23rd 2012 11:15:26pm", out: "2012-05-23 23:15:26 +0000 UTC"}, + {in: "May 23rd, 2012 11:15:26pm", out: "2012-05-23 23:15:26 +0000 UTC"}, + {in: "June 2nd, 2012 11:15:26pm", out: "2012-06-02 23:15:26 +0000 UTC"}, + {in: "June 2nd 2012 11:15:26pm", out: "2012-06-02 23:15:26 +0000 UTC"}, + {in: "June 22nd, 2012 11:15:26pm", out: "2012-06-22 23:15:26 +0000 UTC"}, + {in: "June 22nd 2012 11:15:26pm", out: "2012-06-22 23:15:26 +0000 UTC"}, // Incorporate PR https://github.com/araddon/dateparse/pull/128 to fix https://github.com/araddon/dateparse/issues/127 // dd[th,nd,st,rd] Month yyyy {in: "1st September 2012", out: "2012-09-01 00:00:00 +0000 UTC"}, @@ -142,7 +194,8 @@ var testInputs = []dateTest{ {in: "3rd September 2012", out: "2012-09-03 00:00:00 +0000 UTC"}, {in: "4th September 2012", out: "2012-09-04 00:00:00 +0000 UTC"}, {in: "2nd January 2018", out: "2018-01-02 00:00:00 +0000 UTC"}, - {in: "3nd Feb 2018 13:58:24", out: "2018-02-03 13:58:24 +0000 UTC"}, + {in: "3rd Feb 2018 13:58:24", out: "2018-02-03 13:58:24 +0000 UTC"}, + {in: "1st February 2018 13:58:24", out: "2018-02-01 13:58:24 +0000 UTC"}, // RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST" {in: "Fri, 03 Jul 2015 08:08:08 MST", out: "2015-07-03 08:08:08 +0000 UTC", zname: "MST"}, {in: "Fri, 03 Jul 2015 08:08:08 CET", out: "2015-07-03 08:08:08 +0000 UTC", zname: "CET"}, @@ -213,9 +266,14 @@ var testInputs = []dateTest{ {in: "07-Feb-04 09:07:07 +0100", out: "2004-02-07 08:07:07 +0000 UTC"}, // yyyy-mon-dd 2013-Feb-03 {in: "2013-Feb-03", out: "2013-02-03 00:00:00 +0000 UTC"}, + {in: "2013-Feb-03 09:07:08pm", out: "2013-02-03 21:07:08 +0000 UTC"}, + {in: "2013-February-03", out: "2013-02-03 00:00:00 +0000 UTC"}, + {in: "2013-February-03 09:07:08pm", out: "2013-02-03 21:07:08 +0000 UTC"}, // 03 February 2013 {in: "03 February 2013", out: "2013-02-03 00:00:00 +0000 UTC"}, + {in: "03 February 2013 09:07:08pm", out: "2013-02-03 21:07:08 +0000 UTC"}, {in: "3 February 2013", out: "2013-02-03 00:00:00 +0000 UTC"}, + {in: "3 February 2013 09:07:08pm", out: "2013-02-03 21:07:08 +0000 UTC"}, // Chinese 2014年04月18日 - https://github.com/araddon/dateparse/pull/132 {in: "2014年04月08日", out: "2014-04-08 00:00:00 +0000 UTC"}, {in: "2014年4月8日", out: "2014-04-08 00:00:00 +0000 UTC"}, @@ -316,9 +374,12 @@ var testInputs = []dateTest{ // 112.195.209.90 - - [20/Feb/2018:12:12:14 +0800] "GET / HTTP/1.1" 200 190 "-" "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Mobile Safari/537.36" "-" {in: "06/May/2008:08:11:17 -0700", out: "2008-05-06 15:11:17 +0000 UTC"}, {in: "30/May/2008:08:11:17 -0700", out: "2008-05-30 15:11:17 +0000 UTC"}, + {in: "30/October/2008:08:11:17 -0700", out: "2008-10-30 15:11:17 +0000 UTC"}, // dd/mon/yyyy hh:mm:ss tz - {in: "06/May/2008:08:11:17 -0700", out: "2008-05-06 15:11:17 +0000 UTC"}, - {in: "30/May/2008:08:11:17 -0700", out: "2008-05-30 15:11:17 +0000 UTC"}, + {in: "06/May/2008", out: "2008-05-06 00:00:00 +0000 UTC"}, + {in: "06/May/2008 08:11:17 -0700", out: "2008-05-06 15:11:17 +0000 UTC"}, + {in: "30/May/2008 08:11:17 -0700", out: "2008-05-30 15:11:17 +0000 UTC"}, + {in: "30/September/2008 08:11:17 -0700", out: "2008-09-30 15:11:17 +0000 UTC"}, // mon/dd/yyyy {in: "Oct/ 7/1970", out: "1970-10-07 00:00:00 +0000 UTC"}, {in: "Oct/31/1970", out: "1970-10-31 00:00:00 +0000 UTC"}, @@ -387,6 +448,7 @@ var testInputs = []dateTest{ {in: "2023-01-04 12:01:59 AM", out: "2023-01-04 00:01:59 +0000 UTC"}, {in: "2023-01-04 12:01:59.765 AM", out: "2023-01-04 00:01:59.765 +0000 UTC"}, // https://github.com/araddon/dateparse/issues/157 + {in: "Thu Jan 28 2021", out: "2021-01-28 00:00:00 +0000 UTC"}, {in: "Thu Jan 28 2021 15:28:21 GMT+0000 (Coordinated Universal Time)", out: "2021-01-28 15:28:21 +0000 UTC"}, {in: "Thu Jan 28 2021 15:28:21 GMT+0100 (Coordinated Universal Time)", out: "2021-01-28 14:28:21 +0000 UTC"}, {in: "Thu Jan 28 2021 15:28:21 UTC+0000 (Coordinated Universal Time)", out: "2021-01-28 15:28:21 +0000 UTC"}, @@ -401,6 +463,7 @@ var testInputs = []dateTest{ {in: "Mon Dec 26 16:15:55.103786 2016", out: "2016-12-26 16:15:55.103786 +0000 UTC"}, // https://github.com/araddon/dateparse/issues/109 {in: "Sun, 07 Jun 2020 00:00:00 +0100", out: "2020-06-06 23:00:00 +0000 UTC"}, + {in: "Sun, 07 Jun 2020", out: "2020-06-07 00:00:00 +0000 UTC"}, // https://github.com/araddon/dateparse/issues/100#issuecomment-1118868154 {in: "1 Apr 2022 23:59", out: "2022-04-01 23:59:00 +0000 UTC"}, {in: "1 JANuary 2022 23:59", out: "2022-01-01 23:59:00 +0000 UTC"}, @@ -868,6 +931,7 @@ func TestParseLayout(t *testing.T) { // {in: "06/May/2008 15:04:05 -0700", out: "02/Jan/2006 15:04:05 -0700"}, {in: "06/May/2008:15:04:05 -0700", out: "02/Jan/2006:15:04:05 -0700"}, + {in: "06/June/2008 15:04:05 -0700", out: "02/January/2006 15:04:05 -0700"}, {in: "14 May 2019 19:11:40.164", out: "02 Jan 2006 15:04:05.000"}, {in: "171113 14:14:20", out: "060102 15:04:05"},