From c62ed15d7357bfe5d6a592087b107c5c95a9aa56 Mon Sep 17 00:00:00 2001 From: Klondike Dragon Date: Tue, 12 Dec 2023 17:42:09 -0700 Subject: [PATCH] Support PMDT and AMT time zones Also disallow PM and AM from being specified twice in the string. Fixes https://github.com/araddon/dateparse/issues/149 --- parseany.go | 28 +++++++++++++++++++++------- parseany_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/parseany.go b/parseany.go index 52f5e1a..1dc0443 100644 --- a/parseany.go +++ b/parseany.go @@ -114,7 +114,6 @@ const ( timeOffset timeOffsetColon timeOffsetColonAlpha - timeAlpha timePeriod timePeriodAMPM timeZ @@ -1350,17 +1349,22 @@ iterRunes: } else { // Could be AM/PM isLower := r == 'a' || r == 'p' + isTwoLetterWord := ((i+2) == len(p.datestr) || p.nextIs(i+1, ' ')) switch { - case isLower && p.nextIs(i, 'm'): + case isLower && p.nextIs(i, 'm') && isTwoLetterWord && !p.parsedAMPM: p.coalesceTime(i) p.set(i, "pm") + p.parsedAMPM = true // skip 'm' i++ - case !isLower && p.nextIs(i, 'M'): + case !isLower && p.nextIs(i, 'M') && isTwoLetterWord && !p.parsedAMPM: p.coalesceTime(i) p.set(i, "PM") + p.parsedAMPM = true // skip 'M' i++ + default: + return p, unexpectedTail(p.datestr[i:]) } } case ' ': @@ -1526,8 +1530,11 @@ iterRunes: // timeWsAlpha // 00:12:00 PST // 15:44:11 UTC+0100 2015 - if r == 'm' || r == 'M' { - //return parse("2006-01-02 03:04:05 PM", p.datestr, loc) + isTwoLetterWord := ((i+1) == len(p.datestr) || p.nextIs(i, ' ')) + if (r == 'm' || r == 'M') && isTwoLetterWord { + if p.parsedAMPM { + return p, unexpectedTail(p.datestr[i:]) + } // This isn't a time zone after all... p.tzi = 0 p.stateTime = timeWsAMPM @@ -1536,6 +1543,7 @@ iterRunes: } else { p.set(i-1, "PM") } + p.parsedAMPM = true if p.hourlen == 2 { p.set(p.houri, "03") } else if p.hourlen == 1 { @@ -1668,21 +1676,26 @@ iterRunes: case 'a', 'A', 'p', 'P': // Could be AM/PM isLower := r == 'a' || r == 'p' + isTwoLetterWord := ((i+2) == len(p.datestr) || p.nextIs(i+1, ' ')) switch { - case isLower && p.nextIs(i, 'm'): + case isLower && p.nextIs(i, 'm') && isTwoLetterWord && !p.parsedAMPM: p.mslen = i - p.msi p.coalesceTime(i) p.set(i, "pm") + p.parsedAMPM = true // skip 'm' i++ p.stateTime = timePeriodAMPM - case !isLower && p.nextIs(i, 'M'): + case !isLower && p.nextIs(i, 'M') && isTwoLetterWord && !p.parsedAMPM: p.mslen = i - p.msi p.coalesceTime(i) p.set(i, "PM") + p.parsedAMPM = true // skip 'M' i++ p.stateTime = timePeriodAMPM + default: + return p, unexpectedTail(p.datestr[i:]) } default: if !unicode.IsDigit(r) { @@ -2053,6 +2066,7 @@ type parser struct { formatSetLen int datestr string fullMonth string + parsedAMPM bool skip int extra int part1Len int diff --git a/parseany_test.go b/parseany_test.go index c45a5ee..1a6398f 100644 --- a/parseany_test.go +++ b/parseany_test.go @@ -373,6 +373,19 @@ var testInputs = []dateTest{ {in: "1 October 2022 23:59", out: "2022-10-01 23:59:00 +0000 UTC"}, {in: "1 November 2022 23:59", out: "2022-11-01 23:59:00 +0000 UTC"}, {in: "1 December 2022 23:59", out: "2022-12-01 23:59:00 +0000 UTC"}, + // https://github.com/araddon/dateparse/issues/149 + {in: "2018-09-30 21:09:13 PMDT", out: "2018-09-30 21:09:13 +0000 UTC", zname: "PMDT"}, + {in: "2018-09-30 08:09:13 PM PMDT", out: "2018-09-30 20:09:13 +0000 UTC", zname: "PMDT"}, + {in: "2018-09-30 08:09:13pm PMDT", out: "2018-09-30 20:09:13 +0000 UTC", zname: "PMDT"}, + {in: "2018-09-30 21:09:13.123 PMDT", out: "2018-09-30 21:09:13.123 +0000 UTC", zname: "PMDT"}, + {in: "2018-09-30 08:09:13.123 PM PMDT", out: "2018-09-30 20:09:13.123 +0000 UTC", zname: "PMDT"}, + {in: "2018-09-30 08:09:13.123pm PMDT", out: "2018-09-30 20:09:13.123 +0000 UTC", zname: "PMDT"}, + {in: "2018-09-30 21:09:13 AMT", out: "2018-09-30 21:09:13 +0000 UTC", zname: "AMT"}, + {in: "2018-09-30 08:09:13 AM AMT", out: "2018-09-30 08:09:13 +0000 UTC", zname: "AMT"}, + {in: "2018-09-30 08:09:13am AMT", out: "2018-09-30 08:09:13 +0000 UTC", zname: "AMT"}, + {in: "2018-09-30 21:09:13.123 AMT", out: "2018-09-30 21:09:13.123 +0000 UTC", zname: "AMT"}, + {in: "2018-09-30 08:09:13.123 am AMT", out: "2018-09-30 08:09:13.123 +0000 UTC", zname: "AMT"}, + {in: "2018-09-30 08:09:13.123am AMT", out: "2018-09-30 08:09:13.123 +0000 UTC", zname: "AMT"}, // yyyy-mm-dd hh:mm:ss,000 {in: "2014-05-11 08:20:13,787", out: "2014-05-11 08:20:13.787 +0000 UTC"}, // yyyy-mm-dd hh:mm:ss +0000 @@ -713,6 +726,23 @@ var testParseErrors = []dateTest{ {in: "Wayne, Bruce", err: true}, {in: "Miami, Florida", err: true}, {in: "Doe, John", err: true}, + // https://github.com/araddon/dateparse/issues/149 + {in: "2018-09-30 21:09:13PMDT", err: true}, + {in: "2018-09-30 08:09:13pm PM", err: true}, + {in: "2018-09-30 08:09:13 PM PM", err: true}, + {in: "2018-09-30 08:09:13 PMDT PM", err: true}, + {in: "2018-09-30 21:09:13.123PMDT", err: true}, + {in: "2018-09-30 08:09:13.123PM pm", err: true}, + {in: "2018-09-30 08:09:13.123 pm PM", err: true}, + {in: "2018-09-30 08:09:13.123 PMDT pm", err: true}, + {in: "2018-09-30 21:09:13AMT", err: true}, + {in: "2018-09-30 08:09:13am AM", err: true}, + {in: "2018-09-30 08:09:13 AM AM", err: true}, + {in: "2018-09-30 08:09:13 AMT AM", err: true}, + {in: "2018-09-30 21:09:13.123AMT", err: true}, + {in: "2018-09-30 08:09:13.123AM am", err: true}, + {in: "2018-09-30 08:09:13.123 am AM", err: true}, + {in: "2018-09-30 08:09:13.123 AMDT am", err: true}, } func TestParseErrors(t *testing.T) {