diff --git a/.gitignore b/.gitignore index 3503c41..386a0f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.pprof *.test dist -vendor \ No newline at end of file +vendor +dateparse/dateparse diff --git a/README.md b/README.md index 7656620..e7a71f6 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Go Date Parser Parse date/time strings without knowing the format in advance. Supports 100+ formats. Validates comprehensively to avoid false positives. Very fast (~single-pass state-machine based approach). See [bench_test.go](https://github.com/itlightning/dateparse/blob/main/bench_test.go) for performance comparison. See the critical note below about timezones. -⚡ Maintained by [IT Lightning](https://itlightning.com/), a cloud-first logging platform that's uniquely powerful, super-easy (schemaless, point-and-shoot ingestion), and affordable. It automatically extracts and classifies structured data out of your unstructured log messages. Enjoy visual pattern-analysis and robust SQL-like search. It's unique architecture means you can log more and pay less. Check it out and give us feedback! ⚡ +⚡ Maintained by [SparkLogs](https://sparklogs.com/), a cloud-first logging platform that's uniquely powerful, super-easy (schemaless, point-and-shoot ingestion), and affordable. It automatically extracts and classifies structured data out of your unstructured log messages. Enjoy visual pattern-analysis and robust SQL-like search. It's unique architecture means you can log more and pay less. Check it out and give us feedback! SparkLogs is developed by [IT Lightning](https://itlightning.com/). ⚡ 🐛💡 Find a bug or have an idea with this package? [Issues](https://github.com/itlightning/dateparse/issues) and pull requests are welcome. @@ -75,6 +75,14 @@ cli tool for testing dateformats [Date Parse CLI](https://github.com/itlightning/dateparse/tree/main/dateparse) +Running the tests +---------------------------------- + +Make sure for your Linux distribution you've installed the relevant package that includes older timezone name links (e.g., `US/Pacific`). For example, on Ubuntu: + +```bash +sudo apt install tzdata-legacy +``` Extended example ------------------- diff --git a/dateparse/README.md b/dateparse/README.md index f6b99a6..128fa11 100644 --- a/dateparse/README.md +++ b/dateparse/README.md @@ -169,4 +169,44 @@ Your Using time.Local set to location=America/New_York EDT | ParseAny | time.Local = time.UTC | 2017-03-03 00:00:00 +0000 UTC | 2017-03-03 00:00:00 +0000 UTC day=5 | +-------------+---------------------------+----------------------------------------------------+----------------------------------------------------+ +# Automatically retry date formats that are ambiguous mm/dd vs dd/mm +$ ./dateparse --retry-ambiguous "28.09.2024" + +Your Current time.Local zone is MDT + +Layout String: dateparse.ParseFormat() => 02.01.2006 + ++-------------+-----------------------+----------------------------------------------------+----------------------------------------------------+ +| method | Zone Source | Parsed | Parsed: t.In(time.UTC) | ++-------------+-----------------------+----------------------------------------------------+----------------------------------------------------+ +| ParseIn | time.Local = nil | 2024-09-28 00:00:00 +0000 UTC | 2024-09-28 00:00:00 +0000 UTC | +| ParseIn | time.Local = time.UTC | 2024-09-28 00:00:00 +0000 UTC | 2024-09-28 00:00:00 +0000 UTC | +| ParseLocal | time.Local = nil | 2024-09-28 00:00:00 +0000 UTC | 2024-09-28 00:00:00 +0000 UTC | +| ParseLocal | time.Local = time.UTC | 2024-09-28 00:00:00 +0000 UTC | 2024-09-28 00:00:00 +0000 UTC | +| ParseStrict | time.Local = nil | this date has ambiguous mm/dd vs dd/mm type format | this date has ambiguous mm/dd vs dd/mm type format | +| ParseStrict | time.Local = time.UTC | this date has ambiguous mm/dd vs dd/mm type format | this date has ambiguous mm/dd vs dd/mm type format | +| ParseAny | time.Local = nil | 2024-09-28 00:00:00 +0000 UTC | 2024-09-28 00:00:00 +0000 UTC day=6 | +| ParseAny | time.Local = time.UTC | 2024-09-28 00:00:00 +0000 UTC | 2024-09-28 00:00:00 +0000 UTC day=6 | ++-------------+-----------------------+----------------------------------------------------+----------------------------------------------------+ + +# Force dates to be interpreted as day-first instead of month-first +$ ./dateparse --prefer-day-first "28.09.2024" + +Your Current time.Local zone is MDT + +Layout String: dateparse.ParseFormat() => 02.01.2006 + ++-------------+-----------------------+----------------------------------------------------+----------------------------------------------------+ +| method | Zone Source | Parsed | Parsed: t.In(time.UTC) | ++-------------+-----------------------+----------------------------------------------------+----------------------------------------------------+ +| ParseAny | time.Local = nil | 2024-09-28 00:00:00 +0000 UTC | 2024-09-28 00:00:00 +0000 UTC day=6 | +| ParseAny | time.Local = time.UTC | 2024-09-28 00:00:00 +0000 UTC | 2024-09-28 00:00:00 +0000 UTC day=6 | +| ParseIn | time.Local = nil | 2024-09-28 00:00:00 +0000 UTC | 2024-09-28 00:00:00 +0000 UTC | +| ParseIn | time.Local = time.UTC | 2024-09-28 00:00:00 +0000 UTC | 2024-09-28 00:00:00 +0000 UTC | +| ParseLocal | time.Local = nil | 2024-09-28 00:00:00 +0000 UTC | 2024-09-28 00:00:00 +0000 UTC | +| ParseLocal | time.Local = time.UTC | 2024-09-28 00:00:00 +0000 UTC | 2024-09-28 00:00:00 +0000 UTC | +| ParseStrict | time.Local = nil | this date has ambiguous mm/dd vs dd/mm type format | this date has ambiguous mm/dd vs dd/mm type format | +| ParseStrict | time.Local = time.UTC | this date has ambiguous mm/dd vs dd/mm type format | this date has ambiguous mm/dd vs dd/mm type format | ++-------------+-----------------------+----------------------------------------------------+----------------------------------------------------+ + ``` \ No newline at end of file diff --git a/dateparse/main.go b/dateparse/main.go index ff532e5..9ec8bcb 100644 --- a/dateparse/main.go +++ b/dateparse/main.go @@ -11,12 +11,27 @@ import ( ) var ( - timezone = "" - datestr = "" + timezone = "" + datestr = "" + retryAmbiguousDateWithSwap = false + preferDayFirst = false + parserOptions = []dateparse.ParserOption{} ) +func buildParserOptions() { + parserOptions = []dateparse.ParserOption{} + if retryAmbiguousDateWithSwap { + parserOptions = append(parserOptions, dateparse.RetryAmbiguousDateWithSwap(true)) + } + if preferDayFirst { + parserOptions = append(parserOptions, dateparse.PreferMonthFirst(false)) + } +} + func main() { flag.StringVar(&timezone, "timezone", "", "Timezone aka `America/Los_Angeles` formatted time-zone") + flag.BoolVar(&retryAmbiguousDateWithSwap, "retry-ambiguous", false, "Retry ambiguous date/time formats (day-first vs month-first)") + flag.BoolVar(&preferDayFirst, "prefer-day-first", false, "Prefer day-first date format") flag.Parse() if len(flag.Args()) == 0 { @@ -25,13 +40,17 @@ func main() { ./dateparse "2009-08-12T22:15:09.99Z" ./dateparse --timezone="America/Denver" "2017-07-19 03:21:51+00:00" + ./dateparse --prefer-day-first "28.09.2024" + ./dateparse --retry-ambiguous "28.09.2024" `) return } + buildParserOptions() + datestr = flag.Args()[0] - layout, err := dateparse.ParseFormat(datestr) + layout, err := dateparse.ParseFormat(datestr, parserOptions...) if err != nil { fatal(err) } @@ -82,7 +101,7 @@ type parser func(datestr string, loc *time.Location, utc bool) string func parseLocal(datestr string, loc *time.Location, utc bool) string { time.Local = loc - t, err := dateparse.ParseLocal(datestr) + t, err := dateparse.ParseLocal(datestr, parserOptions...) if err != nil { return err.Error() } @@ -93,7 +112,7 @@ func parseLocal(datestr string, loc *time.Location, utc bool) string { } func parseIn(datestr string, loc *time.Location, utc bool) string { - t, err := dateparse.ParseIn(datestr, loc) + t, err := dateparse.ParseIn(datestr, loc, parserOptions...) if err != nil { return err.Error() } @@ -104,7 +123,7 @@ func parseIn(datestr string, loc *time.Location, utc bool) string { } func parseAny(datestr string, loc *time.Location, utc bool) string { - t, err := dateparse.ParseAny(datestr) + t, err := dateparse.ParseAny(datestr, parserOptions...) if err != nil { return err.Error() } @@ -115,7 +134,7 @@ func parseAny(datestr string, loc *time.Location, utc bool) string { } func parseStrict(datestr string, loc *time.Location, utc bool) string { - t, err := dateparse.ParseStrict(datestr) + t, err := dateparse.ParseStrict(datestr, parserOptions...) if err != nil { return err.Error() }