diff --git a/parseany.go b/parseany.go index 717c35a..5fabaca 100644 --- a/parseany.go +++ b/parseany.go @@ -59,26 +59,28 @@ const ( dateYearDash dateYearDashAlphaDash dateYearDashDash - dateYearDashDashWs // 5 + dateYearDashDashWs // 6 dateYearDashDashT dateYearDashDashOffset dateDigitDash dateDigitDashAlpha - dateDigitDashAlphaDash // 10 + dateDigitDashAlphaDash // 11 + dateDigitDashDigit + dateDigitDashDigitDash dateDigitDot dateDigitDotDot dateDigitSlash dateDigitYearSlash - dateDigitSlashAlpha // 15 + dateDigitSlashAlpha // 18 dateDigitColon dateDigitChineseYear dateDigitChineseYearWs dateDigitWs - dateDigitWsMoYear // 20 + dateDigitWsMoYear // 23 dateAlpha dateAlphaWs dateAlphaWsDigit - dateAlphaWsDigitMore // 24 + dateAlphaWsDigitMore // 27 dateAlphaWsDigitMoreWs dateAlphaWsDigitMoreWsYear dateAlphaWsMonth @@ -88,7 +90,7 @@ const ( dateAlphaWsMore dateAlphaWsAtTime dateAlphaWsAlpha - dateAlphaWsAlphaYearmaybe // 34 + dateAlphaWsAlphaYearmaybe // 37 dateAlphaPeriodWsDigit dateAlphaSlash dateAlphaSlashDigit @@ -545,6 +547,9 @@ iterRunes: if unicode.IsLetter(r) { p.stateDate = dateDigitDashAlpha p.moi = i + } else if unicode.IsDigit(r) { + p.stateDate = dateDigitDashDigit + p.moi = i } else { return p, unknownErr(datestr) } @@ -560,10 +565,29 @@ iterRunes: p.stateDate = dateDigitDashAlphaDash } - case dateDigitDashAlphaDash: - // 13-Feb-03 ambiguous - // 28-Feb-03 ambiguous - // 29-Jun-2016 dd-month(alpha)-yyyy + case dateDigitDashDigit: + // 29-06-2026 + switch r { + case '-': + // X + // 29-06-2026 + p.molen = i - p.moi + if p.molen == 2 { + p.set(p.moi, "01") + p.yeari = i + 1 + p.stateDate = dateDigitDashDigitDash + } else { + return p, unknownErr(datestr) + } + } + + case dateDigitDashAlphaDash, dateDigitDashDigitDash: + // dateDigitDashAlphaDash: + // 13-Feb-03 ambiguous + // 28-Feb-03 ambiguous + // 29-Jun-2016 dd-month(alpha)-yyyy + // dateDigitDashDigitDash: + // 29-06-2026 switch r { case ' ': // we need to find if this was 4 digits, aka year @@ -581,8 +605,11 @@ iterRunes: } else if length == 2 { // We have no idea if this is // yy-mon-dd OR dd-mon-yy + // (or for dateDigitDashDigitDash, yy-mm-dd OR dd-mm-yy) // - // We are going to ASSUME (bad, bad) that it is dd-mon-yy which is a horible assumption + // We are going to ASSUME (bad, bad) that it is dd-mon-yy (dd-mm-yy), + // which is a horrible assumption, but seems to be the convention for + // dates that are formatted in this way. p.ambiguousMD = true p.yearlen = 2 p.set(p.yeari, "06") @@ -592,6 +619,8 @@ iterRunes: if !p.setDay() { return p, unknownErr(datestr) } + } else { + return p, unknownErr(datestr) } p.stateTime = timeStart break iterRunes @@ -2055,32 +2084,43 @@ iterRunes: case dateYearDashDashT: return p, nil - case dateDigitDashAlphaDash: - // 13-Feb-03 ambiguous - // 28-Feb-03 ambiguous - // 29-Jun-2016 - length := len(p.datestr) - (p.moi + p.molen + 1) - if length == 4 { - p.yearlen = 4 - p.set(p.yeari, "2006") - // We now also know that part1 was the day - p.dayi = 0 - p.daylen = p.part1Len - if !p.setDay() { - return p, unknownErr(datestr) - } - } else if length == 2 { - // We have no idea if this is - // yy-mon-dd OR dd-mon-yy - // - // We are going to ASSUME (bad, bad) that it is dd-mon-yy which is a horible assumption - p.ambiguousMD = true - p.yearlen = 2 - p.set(p.yeari, "06") - // We now also know that part1 was the day - p.dayi = 0 - p.daylen = p.part1Len - if !p.setDay() { + case dateDigitDashAlphaDash, dateDigitDashDigitDash: + // This has already been done if we parsed the time already + if p.stateTime == timeIgnore { + // dateDigitDashAlphaDash: + // 13-Feb-03 ambiguous + // 28-Feb-03 ambiguous + // 29-Jun-2016 + // dateDigitDashDigitDash: + // 29-06-2026 + length := len(p.datestr) - (p.moi + p.molen + 1) + if length == 4 { + p.yearlen = 4 + p.set(p.yeari, "2006") + // We now also know that part1 was the day + p.dayi = 0 + p.daylen = p.part1Len + if !p.setDay() { + return p, unknownErr(datestr) + } + } else if length == 2 { + // We have no idea if this is + // yy-mon-dd OR dd-mon-yy + // (or for dateDigitDashDigitDash, yy-mm-dd OR dd-mm-yy) + // + // We are going to ASSUME (bad, bad) that it is dd-mon-yy (dd-mm-yy), + // which is a horrible assumption, but seems to be the convention for + // dates that are formatted in this way. + p.ambiguousMD = true + p.yearlen = 2 + p.set(p.yeari, "06") + // We now also know that part1 was the day + p.dayi = 0 + p.daylen = p.part1Len + if !p.setDay() { + return p, unknownErr(datestr) + } + } else { return p, unknownErr(datestr) } } diff --git a/parseany_test.go b/parseany_test.go index 0a545f9..fc1938a 100644 --- a/parseany_test.go +++ b/parseany_test.go @@ -324,10 +324,20 @@ var testInputs = []dateTest{ // yyyy-mm-dd-07:00 {in: "2020-07-20+08:00", out: "2020-07-19 16:00:00 +0000 UTC"}, {in: "2020-07-20+0800", out: "2020-07-19 16:00:00 +0000 UTC"}, - // dd-mmm-yy + // dd-mmm-yy (alpha month) {in: "28-Feb-02", out: "2002-02-28 00:00:00 +0000 UTC"}, {in: "15-Jan-18", out: "2018-01-15 00:00:00 +0000 UTC"}, {in: "15-Jan-2017", out: "2017-01-15 00:00:00 +0000 UTC"}, + {in: "28-Feb-02 15:16:17", out: "2002-02-28 15:16:17 +0000 UTC"}, + {in: "15-Jan-18 15:16:17", out: "2018-01-15 15:16:17 +0000 UTC"}, + {in: "15-Jan-2017 15:16:17", out: "2017-01-15 15:16:17 +0000 UTC"}, + // dd-mm-yy (digit month - potentially ambiguous) - https://github.com/araddon/dateparse/issues/139 + {in: "28-02-02", out: "2002-02-28 00:00:00 +0000 UTC"}, + {in: "15-01-18", out: "2018-01-15 00:00:00 +0000 UTC"}, + {in: "15-01-2017", out: "2017-01-15 00:00:00 +0000 UTC"}, + {in: "28-02-02 15:16:17", out: "2002-02-28 15:16:17 +0000 UTC"}, + {in: "15-01-18 15:16:17", out: "2018-01-15 15:16:17 +0000 UTC"}, + {in: "15-01-2017 15:16:17", out: "2017-01-15 15:16:17 +0000 UTC"}, // yyyy-mm {in: "2014-04", out: "2014-04-01 00:00:00 +0000 UTC"}, // yyyy-mm-dd hh:mm:ss AM @@ -733,7 +743,6 @@ var testParseErrors = []dateTest{ {in: "oct.-7-1970", err: true}, {in: "septe. 7, 1970", err: true}, {in: "SeptemberRR 7th, 1970", err: true}, - {in: "29-06-2016", err: true}, // this is just testing the empty space up front {in: " 2018-01-02 17:08:09 -07:00", err: true}, // a semantic version number should not be interpreted as a date