├── .credo.exs ├── .formatter.exs ├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── EXAMPLES.livemd ├── LICENSE.md ├── README.md ├── bench ├── profile.exs ├── rails.rb ├── ruby.rb └── self.exs ├── bin └── release ├── config ├── bench.exs ├── config.exs ├── dev.exs └── test.exs ├── images └── taxjar_sponsor.jpg ├── lib ├── combinators.ex ├── combinators.ex.exs ├── date_time_parser.ex ├── date_time_parser │ ├── timezone_abbreviations.ex │ └── timezone_parser.ex ├── formatters.ex ├── parse_error.ex ├── parser.ex └── parser │ ├── date.ex │ ├── date_time.ex │ ├── date_time_us.ex │ ├── date_us.ex │ ├── epoch.ex │ ├── serial.ex │ ├── time.ex │ └── tokenizer.ex ├── mix.exs ├── mix.lock ├── pages ├── Future-UTC-DateTime.md └── upgrading_0_to_1.md ├── priv └── tzdata2022g │ ├── africa │ ├── antarctica │ ├── asia │ ├── australasia │ ├── backward │ ├── etcetera │ ├── europe │ ├── northamerica │ └── southamerica └── test ├── date_time_parser └── timezone_parser_test.exs ├── date_time_parser_test.exs ├── fixture ├── date_formats_samples.txt ├── playground_header.md └── ruby-dates.csv ├── support ├── macros.ex └── recorder.ex └── test_helper.exs /.credo.exs: -------------------------------------------------------------------------------- 1 | # This file contains the configuration for Credo and you are probably reading 2 | # this after creating it with `mix credo.gen.config`. 3 | # 4 | # If you find anything wrong or unclear in this file, please report an 5 | # issue on GitHub: https://github.com/rrrene/credo/issues 6 | # 7 | %{ 8 | # 9 | # You can have as many configs as you like in the `configs:` field. 10 | configs: [ 11 | %{ 12 | # 13 | # Run any exec using `mix credo -C `. If no exec name is given 14 | # "default" is used. 15 | # 16 | name: "default", 17 | # 18 | # These are the files included in the analysis: 19 | files: %{ 20 | # 21 | # You can give explicit globs or simply directories. 22 | # In the latter case `**/*.{ex,exs}` will be used. 23 | # 24 | included: ["lib/", "src/", "test/", "web/", "apps/"], 25 | excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/"] 26 | }, 27 | # 28 | # Load and configure plugins here: 29 | # 30 | plugins: [], 31 | # 32 | # If you create your own checks, you must specify the source files for 33 | # them here, so they can be loaded by Credo before running the analysis. 34 | # 35 | requires: [], 36 | # 37 | # If you want to enforce a style guide and need a more traditional linting 38 | # experience, you can change `strict` to `true` below: 39 | # 40 | strict: false, 41 | # 42 | # If you want to use uncolored output by default, you can change `color` 43 | # to `false` below: 44 | # 45 | color: true, 46 | # 47 | # You can customize the parameters of any check by adding a second element 48 | # to the tuple. 49 | # 50 | # To disable a check put `false` as second element: 51 | # 52 | # {Credo.Check.Design.DuplicatedCode, false} 53 | # 54 | checks: [ 55 | # 56 | ## Consistency Checks 57 | # 58 | {Credo.Check.Consistency.ExceptionNames, []}, 59 | {Credo.Check.Consistency.LineEndings, []}, 60 | {Credo.Check.Consistency.ParameterPatternMatching, []}, 61 | {Credo.Check.Consistency.SpaceAroundOperators, []}, 62 | {Credo.Check.Consistency.SpaceInParentheses, []}, 63 | {Credo.Check.Consistency.TabsOrSpaces, []}, 64 | 65 | # 66 | ## Design Checks 67 | # 68 | # You can customize the priority of any check 69 | # Priority values are: `low, normal, high, higher` 70 | # 71 | {Credo.Check.Design.AliasUsage, 72 | [priority: :low, if_nested_deeper_than: 2, if_called_more_often_than: 0]}, 73 | # You can also customize the exit_status of each check. 74 | # If you don't want TODO comments to cause `mix credo` to fail, just 75 | # set this value to 0 (zero). 76 | # 77 | {Credo.Check.Design.TagTODO, [exit_status: 2]}, 78 | {Credo.Check.Design.TagFIXME, []}, 79 | 80 | # 81 | ## Readability Checks 82 | # 83 | {Credo.Check.Readability.AliasOrder, []}, 84 | {Credo.Check.Readability.FunctionNames, []}, 85 | {Credo.Check.Readability.LargeNumbers, []}, 86 | {Credo.Check.Readability.MaxLineLength, [priority: :low, max_length: 120]}, 87 | {Credo.Check.Readability.ModuleAttributeNames, []}, 88 | {Credo.Check.Readability.ModuleDoc, []}, 89 | {Credo.Check.Readability.ModuleNames, []}, 90 | {Credo.Check.Readability.ParenthesesInCondition, []}, 91 | {Credo.Check.Readability.ParenthesesOnZeroArityDefs, []}, 92 | {Credo.Check.Readability.PredicateFunctionNames, []}, 93 | {Credo.Check.Readability.PreferImplicitTry, []}, 94 | {Credo.Check.Readability.RedundantBlankLines, []}, 95 | {Credo.Check.Readability.Semicolons, []}, 96 | {Credo.Check.Readability.SpaceAfterCommas, []}, 97 | {Credo.Check.Readability.StringSigils, []}, 98 | {Credo.Check.Readability.TrailingBlankLine, []}, 99 | {Credo.Check.Readability.TrailingWhiteSpace, []}, 100 | # TODO: enable by default in Credo 1.1 101 | {Credo.Check.Readability.UnnecessaryAliasExpansion, false}, 102 | {Credo.Check.Readability.VariableNames, []}, 103 | 104 | # 105 | ## Refactoring Opportunities 106 | # 107 | {Credo.Check.Refactor.CondStatements, []}, 108 | {Credo.Check.Refactor.CyclomaticComplexity, []}, 109 | {Credo.Check.Refactor.FunctionArity, []}, 110 | {Credo.Check.Refactor.LongQuoteBlocks, []}, 111 | {Credo.Check.Refactor.MapInto, false}, 112 | {Credo.Check.Refactor.MatchInCondition, []}, 113 | {Credo.Check.Refactor.NegatedConditionsInUnless, []}, 114 | {Credo.Check.Refactor.NegatedConditionsWithElse, []}, 115 | {Credo.Check.Refactor.Nesting, []}, 116 | {Credo.Check.Refactor.UnlessWithElse, []}, 117 | {Credo.Check.Refactor.WithClauses, []}, 118 | 119 | # 120 | ## Warnings 121 | # 122 | {Credo.Check.Warning.BoolOperationOnSameValues, []}, 123 | {Credo.Check.Warning.ExpensiveEmptyEnumCheck, []}, 124 | {Credo.Check.Warning.IExPry, []}, 125 | {Credo.Check.Warning.IoInspect, []}, 126 | {Credo.Check.Warning.LazyLogging, false}, 127 | {Credo.Check.Warning.OperationOnSameValues, []}, 128 | {Credo.Check.Warning.OperationWithConstantResult, []}, 129 | {Credo.Check.Warning.RaiseInsideRescue, []}, 130 | {Credo.Check.Warning.UnusedEnumOperation, []}, 131 | {Credo.Check.Warning.UnusedFileOperation, []}, 132 | {Credo.Check.Warning.UnusedKeywordOperation, []}, 133 | {Credo.Check.Warning.UnusedListOperation, []}, 134 | {Credo.Check.Warning.UnusedPathOperation, []}, 135 | {Credo.Check.Warning.UnusedRegexOperation, []}, 136 | {Credo.Check.Warning.UnusedStringOperation, []}, 137 | {Credo.Check.Warning.UnusedTupleOperation, []}, 138 | 139 | # 140 | # Controversial and experimental checks (opt-in, just replace `false` with `[]`) 141 | # 142 | {Credo.Check.Consistency.MultiAliasImportRequireUse, false}, 143 | {Credo.Check.Design.DuplicatedCode, false}, 144 | {Credo.Check.Readability.MultiAlias, false}, 145 | {Credo.Check.Readability.Specs, false}, 146 | {Credo.Check.Readability.SinglePipe, false}, 147 | {Credo.Check.Refactor.ABCSize, false}, 148 | {Credo.Check.Refactor.AppendSingleItem, false}, 149 | {Credo.Check.Refactor.DoubleBooleanNegation, false}, 150 | {Credo.Check.Refactor.ModuleDependencies, false}, 151 | {Credo.Check.Refactor.PipeChainStart, false}, 152 | {Credo.Check.Refactor.VariableRebinding, false}, 153 | {Credo.Check.Warning.MapGetUnsafePass, false}, 154 | {Credo.Check.Warning.UnsafeToAtom, false} 155 | 156 | # 157 | # Custom checks can be created using `mix credo.gen.check`. 158 | # 159 | ] 160 | } 161 | ] 162 | } 163 | -------------------------------------------------------------------------------- /.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | lib/combinators.ex binary 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Tests 3 | 4 | on: 5 | pull_request: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | style: 11 | runs-on: ubuntu-latest 12 | name: Check Style 13 | env: 14 | MIX_ENV: test 15 | CI: "true" 16 | steps: 17 | - uses: actions/checkout@v3 18 | - uses: erlef/setup-beam@v1 19 | with: 20 | otp-version: '25' 21 | elixir-version: '1.14.3' 22 | - run: mix deps.get --only dev 23 | - run: mix format --check-formatted 24 | - run: mix credo --strict 25 | 26 | test-oldest: 27 | runs-on: ubuntu-20.04 28 | name: Tests on oldest version 29 | env: 30 | MIX_ENV: test 31 | CI: "true" 32 | steps: 33 | - uses: actions/checkout@v3 34 | - uses: erlef/setup-beam@v1 35 | with: 36 | otp-version: '22' 37 | elixir-version: '1.12.3-otp-22' 38 | - run: rm mix.lock 39 | - run: mix deps.get 40 | - run: mix compile --force --warnings-as-errors 41 | - run: mix test 42 | 43 | test: 44 | runs-on: ubuntu-latest 45 | name: Tests on latest version 46 | env: 47 | MIX_ENV: test 48 | CI: "true" 49 | steps: 50 | - uses: actions/checkout@v3 51 | - uses: erlef/setup-beam@v1 52 | with: 53 | otp-version: '25' 54 | elixir-version: '1.14.3' 55 | - run: rm mix.lock 56 | - run: mix deps.get 57 | - run: mix compile --force --warnings-as-errors 58 | - run: mix test 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where third-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | date_time_parser-*.tar 24 | *.benchee 25 | .tool-versions 26 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## unreleased 4 | 5 | ## 1.2.0 (2023-12-06) 6 | 7 | - Drop Timex as dependency. This required a custom IANA file parser for 8 | determining timezones and rules that was able to parse timezone abbreviations. 9 | Doing so requires the use of Elixir functions introduced in Elixir 1.11. 10 | - Bump NimbleParsec which requires Elixir 1.12. 11 | 12 | ## 1.1.5 (2022-10-07) 13 | 14 | - Switch to pre-compiled combinators so that NimbleParsec is not needed during 15 | compilation-- why have everyone else compile this when we can compile it 16 | before publishing? 17 | - Update examples to be a Livebook 18 | - Fix serial parser matching on time 19 | - Fix `assume_date` option with time results from `parse` function. 20 | - Support Month-Year format; please note that this essentially requires a 21 | fallback `assume_date` since there is not a data structure that only describes 22 | Month and Year in Elixir. 23 | 24 | ## 1.1.4 (2022-10-05) 25 | 26 | - Update GitHub Repo references 27 | 28 | ## 1.1.3 (2021-10-24) 29 | 30 | - Stricter time parsing by requiring time separators. For example, `949` used to 31 | be parsed as `09:49:00`, but is now considered not valid. This is to help 32 | decrease false positives of nonsensical times such as `25:00 am` (incorrectly 33 | parsed as `02:05:00`). Thanks @fcapovilla (PR #46) 34 | 35 | ## 1.1.2 (2021-06-14) 36 | 37 | - Correct handling of 12:xx:xx AM timestamps (12hr). These were incorrectly 38 | parsed as 12:xx:xx (24hr) timestamps when they should have been 00:xx:xx 39 | (24hr) timestamps. 40 | 41 | - Lock Timex to >= 3.2.1 and <= 3.7.2 to avoid timezone conversion issues when 42 | using `to_utc: true`. Looks like there are some breaking changes (for us) 43 | 44 | ## 1.1.1 (2021-02-06) 45 | 46 | - Adjust tokenizer to prefer month/day before of day/month when those are the 47 | only found tokens. Thanks @mwean raising the issue. 48 | 49 | ## 1.1.0 (2020-10-17) 50 | 51 | - Add option `use_1904_date_system` for the serial parser. It defaults to 52 | `false`. Most spreadsheet applications use the 1900 date system, but 53 | Microsoft Excel on Macintosh in particular used the 1904 date system 54 | [@fcapovilla]. 55 | 56 | ## 1.0.0 (2020-02-05) 57 | 58 | ### Breaking 59 | 60 | - Change `parse_datetime` to no longer assume time. Previously, it 61 | would assume `00:00:00` if it could not be parsed. This is replaced with an 62 | opt-in option `assume_time`. See next point. If you relied on this, to 63 | upgrade add the option; eg: `DateTimeParser.parse_datetime(string, 64 | assume_time: true)` 65 | - Change `parse_datetime` to no longer convert DateTime to UTC 66 | timezone. If you relied on this, to upgrade add the option; eg: 67 | `DateTimeParser.parse_datetime(string, to_utc: true)` 68 | - Change `parse_date` to no longer assume a date. Previously, it 69 | would assume the current date, and replace the found information from the 70 | string. This is replaced with an opt-in option of `assume_date`. If you 71 | relied on this, to upgrade add the option; eg: 72 | `DateTimeParser.parse_date(string, assume_date: true)` 73 | 74 | ### Bugs 75 | 76 | - Fix a UTC conversion bug between Daylight/Standard time (#20). If you're 77 | using an earlier version and converting to UTC, please upgrade to >= 78 | 1.0.0-rc2 immediately. 79 | - Fix an epoch subsecond parsing bug (#16) (thanks @tmr08c) 80 | - Updated for compatibility with Elixir 1.10.0 81 | 82 | ### Features 83 | 84 | - Add `parse/2` that will respond with the best match. This function accepts all 85 | options introduced below. 86 | - Change `parse_datetime/1` to `parse_datetime/2` to accept options: 87 | - `assume_time: true | %Time{} | false` with the default of false. 88 | - Change `parse_date/1` to `parse_date/2` to accept options: 89 | - `assume_date: true | %Date{} | false` with the default of false. 90 | - Add support for parsing negative epoch times. (#24) (thanks @tmr08c) 91 | - Add bang variants, `parse!/2`, `parse_datetime!/2`, `parse_time!/2`, and 92 | `parse_date!/2` 93 | - Added `parsers: []` option to add or disable parsers. This is helpful if you 94 | are don't want to consider Serial or Epoch timestamps. 95 | 96 | ## 0.2.0 (2019-09-26) 97 | 98 | - Add Serial time support 99 | 100 | ## 0.1.4 (2019-09-03) 101 | 102 | - Refactor 103 | - Support subsecond parsing to 24 digits (yoctoseconds) [thanks @myfreeweb] 104 | 105 | ## 0.1.3 (2019-08-02) 106 | 107 | - Support parsing Unix epoch times from strings 108 | - Fix microsecond parsing 109 | 110 | ## 0.1.2 (2019-07-11) 111 | 112 | - Validate days of month. Previously it would allow invalid days such as Feb 30. 113 | Now it will check if the year is a leap year and allow up to 29, and otherwise 114 | 28 for Feb. It will also reject day 31 on months without 31 days. 115 | 116 | ## 0.1.1 (2019-07-03) 117 | 118 | - Fix PM 12-hr conversion. The bug would convert 12:34PM to 24:34 which is 119 | not valid 120 | 121 | ## 0.1.0 (2019-06-24) 122 | 123 | - Add DateTimeParser.parse_datetime/2 124 | - Add DateTimeParser.parse_date/1 125 | - Add DateTimeParser.parse_time/1 126 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at david@bernheisel.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Do not submit PRs with compiled nimble parsec artifacts. The maintainer 4 | will generate these accordingly before a release. 5 | 6 | To run tests: 7 | ```shell 8 | $ # This will run tests, regenerate EXAMPLES.livemd, 9 | $ # and run Credo in strict mode. 10 | $ mix tests 11 | ``` 12 | 13 | To build a release: 14 | ```shell 15 | $ # bin/release {old_version} {new_version} 16 | $ bin/release 1.1.3 1.1.4 17 | ``` 18 | 19 | Please review and agree to the [code of conduct](./CODE_OF_CONDUCT.md) before 20 | contributing. 21 | -------------------------------------------------------------------------------- /EXAMPLES.livemd: -------------------------------------------------------------------------------- 1 | # DateTimeParser Playground 2 | 3 | ## Installation 4 | 5 | Install DateTimeParser in your project 6 | 7 | ```elixir 8 | Mix.install([:date_time_parser]) 9 | ``` 10 | 11 | ## Usage 12 | 13 | Use DateTimeParser to parse strings into DateTime, NaiveDateTime, Date, or Time 14 | structs. For example: 15 | 16 | ```elixir 17 | [ 18 | DateTimeParser.parse_datetime("2021-11-09T10:30:00Z"), 19 | DateTimeParser.parse_datetime("2021-11-09T10:30:00"), 20 | DateTimeParser.parse_date("2021-11-09T10:30:00Z"), 21 | DateTimeParser.parse_time("2021-11-09T10:30:00Z"), 22 | DateTimeParser.parse("2021-32-32T10:30:00Z"), 23 | DateTimeParser.parse("2021-11-09T10:30:00Z") 24 | ] 25 | ``` 26 | 27 | or use the bang functions: 28 | 29 | ```elixir 30 | [ 31 | DateTimeParser.parse_datetime!("2021-11-09T10:30:00Z"), 32 | DateTimeParser.parse_datetime!("2021-11-09T10:30:00"), 33 | DateTimeParser.parse_date!("2021-11-09T10:30:00Z"), 34 | DateTimeParser.parse_time!("2021-11-09T10:30:00Z"), 35 | DateTimeParser.parse!("2021-32-32T10:30:00Z"), 36 | DateTimeParser.parse!("2021-11-09T10:30:00Z") 37 | ] 38 | ``` 39 | 40 | Errors sometimes occur when it can't parse the input: 41 | 42 | ```elixir 43 | [ 44 | DateTimeParser.parse("wat"), 45 | DateTimeParser.parse(123), 46 | DateTimeParser.parse([:foo]) 47 | ] 48 | ``` 49 | 50 | ## Options 51 | 52 | You can configure some convenient options as well, for example to automatically 53 | convert to UTC or to assume a time when not present. 54 | 55 | ```elixir 56 | [ 57 | DateTimeParser.parse("12:30PM", assume_date: Date.utc_today()), 58 | DateTimeParser.parse("2022-01-01", assume_time: ~T[12:43:00]), 59 | DateTimeParser.parse("2022-01-01T15:30 EST", to_utc: true), 60 | DateTimeParser.parse("2022-01-01T15:30 EST", to_utc: false), 61 | # Excel time 62 | DateTimeParser.parse("30134"), 63 | # old Mac Excel spreadsheet time 64 | DateTimeParser.parse("30134.4321", use_1904_date_system: true) 65 | ] 66 | ``` 67 | 68 | ## Examples 69 | 70 | |**Input**|**Output (ISO 8601)**|**Method**|**Options**| 71 | |:-------:|:-------------------:|:--------:|:---------:| 72 | |` 01 Feb 2013`|`2013-02-01`|parse| | 73 | |` 03 Jan 2013 10:15:26 -0800`|`2013-01-03T18:15:26Z`|parse|`[to_utc: true]`| 74 | |` 10/1/2018 :: AM`|`2018-10-01`|parse| | 75 | |` 11 Feb 2013`|`2013-02-11`|parse| | 76 | |` 11 Jan 2013 13:26:55 -0800`|`2013-01-11T21:26:55Z`|parse|`[to_utc: true]`| 77 | |` 12/26/2016`|`2016-12-26`|parse| | 78 | |` 24 Sep 2013`|`2013-09-24`|parse| | 79 | |`""=""9/5/2018"""`|`2018-09-05`|parse_date| | 80 | |`""=""9/5/2018"""`|`2018-09-05T00:00:00`|parse_datetime|`[assume_time: true]`| 81 | |`"=""10/1/2018"""`|`2018-10-01`|parse| | 82 | |`"=""9/5/2018"""`|`2018-09-05`|parse| | 83 | |`"Apr 1, 2016 12:02:53 AM PDT"`|`2016-04-01T07:02:53Z`|parse|`[to_utc: true]`| 84 | |`"Apr 1, 2017 2:21:25 AM PDT"`|`2017-04-01T09:21:25Z`|parse|`[to_utc: true]`| 85 | |`"Dec 1, 2018 7:39:53 AM PST"`|`2018-12-01T15:39:53Z`|parse|`[to_utc: true]`| 86 | |`"Jan 1, 2013 06:34:31 PM PST"`|`2013-01-02T02:34:31Z`|parse|`[to_utc: true]`| 87 | |`"Jan 1, 2014 6:44:47 AM PST"`|`2014-01-01T14:44:47Z`|parse|`[to_utc: true]`| 88 | |`"Mar 28, 2014 6:44:47 AM PDT"`|`2014-03-28T13:44:47Z`|parse|`[to_utc: true]`| 89 | |`"Nov 16, 2017 9:41:28 PM PST"`|`2017-11-17T05:41:28Z`|parse|`[to_utc: true]`| 90 | |`"Nov 20, 2016 22:09:23 PM"`|`2016-11-20T22:09:23`|parse| | 91 | |`"Sat, 29 Sep 2018 21:36:28 -0400"`|`2018-09-30T01:36:28Z`|parse|`[to_utc: true]`| 92 | |`"September 28, 2016"`|`2016-09-28`|parse| | 93 | |`"Tuesday, November 29, 2016"`|`2016-11-29`|parse| | 94 | |`-0000000001`|`1969-12-31`|parse_date| | 95 | |`-0000000001`|`1969-12-31T23:59:59Z`|parse_datetime| | 96 | |`-0000000001.0000000001`|`23:59:58.000000`|parse_time| | 97 | |`-0000000001.0000000001`|`1969-12-31T23:59:58.000000Z`|parse_datetime| | 98 | |`-0000000001.000001`|`23:59:58.999999`|parse_time| | 99 | |`-0000000001.00001`|`23:59:58.99999`|parse_time| | 100 | |`-0000000001.0001`|`23:59:58.9999`|parse_time| | 101 | |`-0000000001.001`|`1969-12-31`|parse_date| | 102 | |`-0000000001.001`|`23:59:58.999`|parse_time| | 103 | |`-0000000001.01`|`23:59:58.99`|parse_time| | 104 | |`-0000000001.1`|`23:59:58.9`|parse_time| | 105 | |`-0000000001.111111`|`1969-12-31`|parse_date| | 106 | |`-0386380800`|`1957-10-04T00:00:00Z`|parse_datetime| | 107 | |`-363`|`1899-01-01`|parse_date| | 108 | |`-363.0`|`1899-01-01T00:00:00`|parse_datetime| | 109 | |`-45103.1454398148`|`1776-07-04`|parse_date| | 110 | |`-45103.1454398148`|`20:30:34`|parse_time| | 111 | |`-45103.1454398148`|`1776-07-04T20:30:34`|parse_datetime| | 112 | |`-9999999999`|`1653-02-10T06:13:21Z`|parse_datetime| | 113 | |`-9999999999.009`|`1653-02-10`|parse_date| | 114 | |`-9999999999.9`|`06:13:20.1`|parse_time| | 115 | |`-9999999999.99`|`06:13:20.01`|parse_time| | 116 | |`-9999999999.999`|`1653-02-10`|parse_date| | 117 | |`-9999999999.999`|`06:13:20.001`|parse_time| | 118 | |`-9999999999.9999`|`06:13:20.0001`|parse_time| | 119 | |`-9999999999.99999`|`06:13:20.00001`|parse_time| | 120 | |`-9999999999.999999`|`1653-02-10`|parse_date| | 121 | |`-9999999999.999999`|`06:13:20.000001`|parse_time| | 122 | |`-9999999999.9999999999`|`1653-02-10`|parse_date| | 123 | |`-9999999999.9999999999`|`06:13:20.000001`|parse_time| | 124 | |`-9999999999.9999999999`|`1653-02-10T06:13:20.000001Z`|parse_datetime| | 125 | |`-99999999999`|`-1199-02-15T14:13:21Z`|parse_datetime| | 126 | |`0000000000`|`1970-01-01`|parse_date| | 127 | |`0000000000`|`00:00:00`|parse_time| | 128 | |`0000000000`|`1970-01-01T00:00:00Z`|parse_datetime| | 129 | |`00:00.0`|`00:00:00`|parse_time| | 130 | |`01-01-2018`|`2018-01-01`|parse| | 131 | |`01-Feb-18`|`2018-02-01`|parse| | 132 | |`01-Jul`|`2019-07-01`|parse|`[assume_date: ~D[2019-01-05]]`| 133 | |`01-Jul`|`Could not parse "01-Jul"`|parse| | 134 | |`01-Jul`|`Could not parse "01-Jul"`|parse_datetime| | 135 | |`01-Jul-18`|`2018-07-01`|parse| | 136 | |`01.09.2018`|`2018-09-01`|parse| | 137 | |`01.11.2018`|`2018-11-01`|parse| | 138 | |`01/01/17`|`2017-01-01`|parse| | 139 | |`01/01/2017`|`2017-01-01`|parse| | 140 | |`01/01/2018 - 17:06`|`2018-01-01T17:06:00`|parse| | 141 | |`01/01/2018 01:21PM`|`2018-01-01T13:21:00`|parse| | 142 | |`01/01/2018 14:44`|`2018-01-01T14:44:00`|parse| | 143 | |`01/01/2018 6:22`|`2018-01-01T06:22:00`|parse| | 144 | |`01/02/16`|`2016-01-02`|parse| | 145 | |`01/02/18 01:02 AM`|`2018-01-02T01:02:00`|parse| | 146 | |`01/02/2015`|`2015-01-02`|parse| | 147 | |`01/09/2034`|`2034-01-09`|parse_date| | 148 | |`01/09/2034`|`2034-01-09T00:00:00`|parse_datetime|`[assume_time: true]`| 149 | |`01/Jun./2018`|`2018-06-01`|parse| | 150 | |`02-05-2018`|`2018-05-02`|parse| | 151 | |`02-Oct-17`|`2017-10-02`|parse| | 152 | |`02/01/17`|`2017-02-01`|parse| | 153 | |`02/01/2018`|`2018-02-01`|parse| | 154 | |`02/06/2019`|`2019-02-06`|parse_date| | 155 | |`02/06/2019`|`2019-02-06T00:00:00`|parse_datetime|`[assume_time: true]`| 156 | |`02/21/2018 9:37:42 AM`|`2018-02-21T09:37:42`|parse| | 157 | |`03/5/2018`|`2018-03-05`|parse| | 158 | |`05/01/2018 0:00`|`2018-05-01T00:00:00`|parse| | 159 | |`06/14/2018 09:42:08 PM-0500`|`2018-06-15T02:42:08Z`|parse|`[to_utc: true]`| 160 | |`06/28/18 1:25`|`2018-06-28T01:25:00`|parse| | 161 | |`07:09.3`|`07:09:00`|parse_time| | 162 | |`08:53.0`|`08:53:00`|parse_time| | 163 | |`1-Apr`|`2019-04-01`|parse|`[assume_date: ~D[2019-01-13]]`| 164 | |`1-Apr`|`Could not parse "1-Apr"`|parse| | 165 | |`1//1/17`|`2017-01-01`|parse| | 166 | |`1/1/0117`|`0117-01-01`|parse| | 167 | |`1/1/17 19:12`|`2017-01-01T19:12:00`|parse| | 168 | |`1/1/18 00:01`|`2018-01-01T00:01:00`|parse| | 169 | |`1/1/18 3:24 PM`|`2018-01-01T15:24:00`|parse| | 170 | |`1/1/19 10:39 AM`|`2019-01-01T10:39:00`|parse| | 171 | |`1/1/2013`|`2013-01-01`|parse| | 172 | |`1/10/2018 8:38pM`|`2018-01-10T20:38:00`|parse| | 173 | |`1/13/19`|`2019-01-13`|parse_date| | 174 | |`1/13/19`|`2019-01-13T00:00:00`|parse_datetime|`[assume_time: true]`| 175 | |`1/13/2019`|`2019-01-13`|parse_date| | 176 | |`1/13/2019`|`2019-01-13T00:00:00`|parse_datetime|`[assume_time: true]`| 177 | |`1/15/2019 3:06`|`2019-01-15`|parse_date| | 178 | |`1/15/2019 3:06`|`2019-01-15T03:06:00`|parse_datetime| | 179 | |`1/17/2018 0:00:00`|`2018-01-17T00:00:00`|parse| | 180 | |`1/2/2018 18:06:26`|`2018-01-02T18:06:26`|parse| | 181 | |`1/3/2019 12:00:00 AM`|`2019-01-03T00:00:00`|parse| | 182 | |`1/31/2018 0:00:00 UTC`|`2018-01-31T00:00:00Z`|parse| | 183 | |`1/9/2034`|`2034-01-09`|parse_date| | 184 | |`1/9/2034`|`2034-01-09T00:00:00`|parse_datetime|`[assume_time: true]`| 185 | |`1/9/34`|`2034-01-09`|parse_date| | 186 | |`1/9/34`|`2034-01-09T00:00:00`|parse_datetime|`[assume_time: true]`| 187 | |`10/2/2017 - 23:14`|`2017-10-02T23:14:00`|parse| | 188 | |`10/5/2017 23:52`|`2017-10-05T23:52:00`|parse| | 189 | |`10:13.7`|`10:13:00`|parse_time| | 190 | |`11 July 2017 1:43:46 PM`|`2017-07-11`|parse_date| | 191 | |`11 July 2017 1:43:46 PM`|`2017-07-11T13:43:46`|parse_datetime| | 192 | |`12/5`|`2021-12-05`|parse_date|`[assume_date: ~D[2021-01-01]]`| 193 | |`12:30PM`|`12:30:00`|parse_time|`[assume_date: ~D[2020-01-01]]`| 194 | |`12:30PM`|`2020-01-01T12:30:00`|parse|`[assume_date: ~D[2020-01-01]]`| 195 | |`13/5`|`2021-05-13`|parse_date|`[assume_date: ~D[2021-01-01]]`| 196 | |`18-07-2018 20:38:34 +00:00`|`2018-07-18T20:38:34Z`|parse| | 197 | |`18-12-29`|`2018-12-29`|parse| | 198 | |`19 September 18 2:33:08 PM`|`2018-09-19`|parse_date| | 199 | |`19 September 18 2:33:08 PM`|`2018-09-19T14:33:08`|parse_datetime| | 200 | |`19 September 2018 08:15:22 AM`|`2018-09-19`|parse_date| | 201 | |`19 September 2018 08:15:22 AM`|`2018-09-19T08:15:22`|parse_datetime| | 202 | |`19-Dec-19`|`2019-12-19`|parse| | 203 | |`2`|`1900-01-01`|parse_date| | 204 | |`2.0`|`1900-01-01T00:00:00`|parse_datetime| | 205 | |`2/5`|`2021-02-05`|parse_date|`[assume_date: ~D[2021-01-01]]`| 206 | |`2010/01/01`|`2010-01-01`|parse| | 207 | |`2011-01-01 04:19:20 -0:00`|`2011-01-01T04:19:20Z`|parse| | 208 | |`2012-10-30 09:52:00`|`2012-10-30T09:52:00`|parse| | 209 | |`2012-11-23T22:42:25-05:00`|`2012-11-24T03:42:25Z`|parse|`[to_utc: true]`| 210 | |`2013-04-26 11:25:03 UTC`|`2013-04-26T11:25:03Z`|parse| | 211 | |`2013-09-10 22:14:56.717`|`2013-09-10T22:14:56.717`|parse| | 212 | |`2013-12-31T22:18:50+00:00`|`2013-12-31T22:18:50Z`|parse| | 213 | |`2015-09-28 10:57:11 -0700`|`2015-09-28T17:57:11Z`|parse|`[to_utc: true]`| 214 | |`2015/12/1 1:16`|`2015-12-01T01:16:00`|parse| | 215 | |`2016-02-29`|`2016-02-29`|parse_date| | 216 | |`2016-02-29 00:00:00 UTC`|`2016-02-29T00:00:00Z`|parse_datetime| | 217 | |`2016-04-30`|`2016-04-30`|parse| | 218 | |`2016-05-02T01:10:06+00:00`|`2016-05-02T01:10:06Z`|parse| | 219 | |`2016-06-11 15:50:43`|`2016-06-11T15:50:43`|parse| | 220 | |`2016-06-16 06:06:06`|`2016-06-16T06:06:06`|parse| | 221 | |`2016-07-01 01:51:34+00`|`2016-07-01T01:51:34Z`|parse| | 222 | |`2016-07-31 18:42:46-07:00`|`2016-08-01T01:42:46Z`|parse|`[to_utc: true]`| 223 | |`2016-08-04T07:00:25Z`|`2016-08-04T07:00:25Z`|parse| | 224 | |`2016-08-19 09:34:51.0`|`2016-08-19T09:34:51.0`|parse| | 225 | |`2016-11-17 10:36:34.81`|`2016-11-17T10:36:34.81`|parse| | 226 | |`2016-11-23T16:25:33.971897`|`2016-11-23T16:25:33.971897`|parse| | 227 | |`2016/1/9`|`2016-01-09`|parse| | 228 | |`2017-02-29`|`Could not parse "2017-02-29"`|parse_date| | 229 | |`2017-02-29 00:00:00 UTC`|`Could not parse "2017-02-29 00:00:00 UTC"`|parse_datetime| | 230 | |`2017-03-04 15:20:47 EDT`|`2017-03-04T20:20:47Z`|parse_datetime|`[to_utc: true]`| 231 | |`2017-03-04 15:20:47 EST`|`2017-03-04T20:20:47Z`|parse_datetime|`[to_utc: true]`| 232 | |`2017-04-31`|`Could not parse "2017-04-31"`|parse_date| | 233 | |`2017-04-31 00:00:00 UTC`|`Could not parse "2017-04-31 00:00:00 UTC"`|parse_datetime| | 234 | |`2017-06-31`|`Could not parse "2017-06-31"`|parse_date| | 235 | |`2017-06-31 00:00:00 UTC`|`Could not parse "2017-06-31 00:00:00 UTC"`|parse_datetime| | 236 | |`2017-09-29+00:00`|`2017-09-29T00:00:00`|parse| | 237 | |`2017-09-31`|`Could not parse "2017-09-31"`|parse_date| | 238 | |`2017-09-31 00:00:00 UTC`|`Could not parse "2017-09-31 00:00:00 UTC"`|parse_datetime| | 239 | |`2017-10-06+03:45:16`|`2017-10-06T03:45:16`|parse| | 240 | |`2017-10-24 04:00:10 PDT`|`2017-10-24T11:00:10Z`|parse|`[to_utc: true]`| 241 | |`2017-11-04 15:20:47 EDT`|`2017-11-04`|parse_date| | 242 | |`2017-11-04 15:20:47 EDT`|`2017-11-04T19:20:47Z`|parse_datetime|`[to_utc: true]`| 243 | |`2017-11-04 15:20:47 EST`|`2017-11-04`|parse_date| | 244 | |`2017-11-04 15:20:47 EST`|`2017-11-04T19:20:47Z`|parse_datetime|`[to_utc: true]`| 245 | |`2017-11-04 15:20:47 UTC`|`2017-11-04`|parse_date| | 246 | |`2017-11-04 15:20:47 UTC`|`2017-11-04T15:20:47Z`|parse_datetime| | 247 | |`2017-11-04 15:20:47+0000`|`2017-11-04`|parse_date| | 248 | |`2017-11-04 15:20:47+0000`|`2017-11-04T15:20:47Z`|parse_datetime| | 249 | |`2017-11-04 15:20:47+0500`|`2017-11-04`|parse_date| | 250 | |`2017-11-04 15:20:47+0500`|`2017-11-04T10:20:47Z`|parse_datetime|`[to_utc: true]`| 251 | |`2017-11-04 15:20:47-0500`|`2017-11-04`|parse_date| | 252 | |`2017-11-04 15:20:47-0500`|`2017-11-04T20:20:47Z`|parse_datetime|`[to_utc: true]`| 253 | |`2017-11-31`|`Could not parse "2017-11-31"`|parse_date| | 254 | |`2017-11-31 00:00:00 UTC`|`Could not parse "2017-11-31 00:00:00 UTC"`|parse_datetime| | 255 | |`2017-12-01 03:52`|`2017-12-01T03:52:00`|parse| | 256 | |`2017/08/08`|`2017-08-08`|parse| | 257 | |`2019-05-16+04:00`|`2019-05-16`|parse_date| | 258 | |`2019-05-16+04:00`|`2019-05-16T04:00:00`|parse_datetime|`[assume_time: true]`| 259 | |`2019-05-20 10:00:00PST`|`2019-05-20`|parse_date| | 260 | |`2019-05-20 10:00:00PST`|`2019-05-20T17:00:00Z`|parse_datetime|`[to_utc: true]`| 261 | |`2019/01/31 0:01`|`2019-01-31T00:01:00`|parse| | 262 | |`2021-03-27 00:00 am`|`2021-03-27T00:00:00`|parse_datetime| | 263 | |`2021-03-27 00:00 pm`|`2021-03-27T00:00:00`|parse_datetime| | 264 | |`2021-03-27 12:00 am`|`2021-03-27T00:00:00`|parse_datetime| | 265 | |`2021-03-27 12:00 pm`|`2021-03-27T12:00:00`|parse_datetime| | 266 | |`2034-01-13`|`2034-01-13`|parse_date| | 267 | |`2034-01-13`|`2034-01-13T00:00:00`|parse_datetime|`[assume_time: true]`| 268 | |`2034-1-9`|`2034-01-09`|parse_date| | 269 | |`2034-1-9`|`2034-01-09T00:00:00`|parse_datetime|`[assume_time: true]`| 270 | |`20340109`|`2034-01-09T00:00:00`|parse_datetime|`[assume_time: true]`| 271 | |`23-05-2019 @ 10:01`|`2019-05-23`|parse_date| | 272 | |`23-05-2019 @ 10:01`|`2019-05-23T10:01:00`|parse_datetime|`[assume_time: true]`| 273 | |`24:00`|`Could not parse "24:00"`|parse_time| | 274 | |`29/Aug./2018`|`2018-08-29`|parse| | 275 | |`29/Sep./2018`|`2018-09-29`|parse| | 276 | |`30134`|`1982-07-02`|parse| | 277 | |`30134.0`|`1982-07-02T00:00:00`|parse| | 278 | |`34-1-13`|`2034-01-13`|parse_date| | 279 | |`34-1-13`|`2034-01-13T00:00:00`|parse_datetime|`[assume_time: true]`| 280 | |`4/24/2019 0:00:00`|`2019-04-24`|parse_date| | 281 | |`4/24/2019 0:00:00`|`2019-04-24T00:00:00`|parse_datetime| | 282 | |`41261.6013888889`|`2012-12-18`|parse_date| | 283 | |`41261.6013888889`|`14:26:00`|parse_time| | 284 | |`41261.6013888889`|`2012-12-18T14:26:00`|parse_datetime| | 285 | |`5/12/2019 12:21:58 PM`|`2019-05-12T12:21:58`|parse| | 286 | |`5/2/2019 0:00:00`|`2019-05-02`|parse_date| | 287 | |`5/2/2019 0:00:00`|`2019-05-02T00:00:00`|parse_datetime| | 288 | |`5/2/2019 12:00:00 AM`|`2019-05-02`|parse_date| | 289 | |`5/2/2019 12:00:00 AM`|`2019-05-02T00:00:00`|parse_datetime| | 290 | |`5/31/2019 12:00:00 AM`|`2019-05-31`|parse_date| | 291 | |`5/31/2019 12:00:00 AM`|`2019-05-31T00:00:00`|parse_datetime| | 292 | |`62`|`1900-03-02`|parse_date| | 293 | |`62`|`1900-03-02T00:00:00`|parse|`[assume_time: true]`| 294 | |`62`|`1904-03-03T00:00:00`|parse|`[assume_time: true, use_1904_date_system: true]`| 295 | |`62.0`|`1900-03-02T00:00:00`|parse| | 296 | |`62.0`|`1900-03-02T00:00:00`|parse_datetime| | 297 | |`62.0`|`1904-03-03T00:00:00`|parse|`[use_1904_date_system: true]`| 298 | |`62.0`|`1904-03-03T00:00:00`|parse_datetime|`[use_1904_date_system: true]`| 299 | |`9-2-32`|`2032-02-09`|parse_date| | 300 | |`9-2-32`|`2032-02-09T00:00:00`|parse_datetime|`[assume_time: true]`| 301 | |`9-Feb-18`|`2018-02-09`|parse_date| | 302 | |`9-Feb-18`|`2018-02-09T00:00:00`|parse_datetime|`[assume_time: true]`| 303 | |`9/1/2018 10:26`|`2018-09-01`|parse_date| | 304 | |`9/1/2018 10:26`|`2018-09-01T10:26:00`|parse_datetime|`[assume_time: true]`| 305 | |`9/10/2018 11:08:13 AM`|`2018-09-10T11:08:13`|parse| | 306 | |`9/19/2018 20:38`|`2018-09-19T20:38:00`|parse| | 307 | |`9/20/2017 18:57:24 UTC`|`2017-09-20T18:57:24Z`|parse| | 308 | |`9/4/2018 0:00`|`2018-09-04`|parse_date| | 309 | |`9/4/2018 0:00`|`2018-09-04T00:00:00`|parse_datetime| | 310 | |`9999999999`|`2286-11-20`|parse_date| | 311 | |`9999999999`|`17:46:39`|parse_time| | 312 | |`9999999999`|`2286-11-20T17:46:39Z`|parse_datetime| | 313 | |`9999999999.0000000009`|`2286-11-20T17:46:39.000000Z`|parse_datetime| | 314 | |`9999999999.0000009000`|`2286-11-20T17:46:39.000000Z`|parse_datetime| | 315 | |`9999999999.000001`|`17:46:39.000001`|parse_time| | 316 | |`9999999999.000010`|`17:46:39.000010`|parse_time| | 317 | |`9999999999.000100`|`17:46:39.000100`|parse_time| | 318 | |`9999999999.001000`|`17:46:39.001000`|parse_time| | 319 | |`9999999999.009`|`2286-11-20`|parse_date| | 320 | |`9999999999.009`|`17:46:39.009`|parse_time| | 321 | |`9999999999.009`|`2286-11-20T17:46:39.009Z`|parse_datetime| | 322 | |`9999999999.010000`|`17:46:39.010000`|parse_time| | 323 | |`9999999999.090`|`2286-11-20T17:46:39.090Z`|parse_datetime| | 324 | |`9999999999.100000`|`17:46:39.100000`|parse_time| | 325 | |`9999999999.900`|`17:46:39.900`|parse_time| | 326 | |`9999999999.900`|`2286-11-20T17:46:39.900Z`|parse_datetime| | 327 | |`9999999999.999`|`2286-11-20`|parse_date| | 328 | |`9999999999.999`|`17:46:39.999`|parse_time| | 329 | |`9999999999.999`|`2286-11-20T17:46:39.999Z`|parse_datetime| | 330 | |`9999999999.999999`|`2286-11-20`|parse_date| | 331 | |`9999999999.999999`|`17:46:39.999999`|parse_time| | 332 | |`9999999999.999999`|`2286-11-20T17:46:39.999999Z`|parse_datetime| | 333 | |`9999999999.9999999999`|`2286-11-20`|parse_date| | 334 | |`9999999999.9999999999`|`17:46:39.999999`|parse_time| | 335 | |`9999999999.9999999999`|`2286-11-20T17:46:39.999999Z`|parse_datetime| | 336 | |`99999999999`|`5138-11-16`|parse_date| | 337 | |`99999999999`|`09:46:39`|parse_time| | 338 | |`99999999999`|`5138-11-16T09:46:39Z`|parse_datetime| | 339 | |`Fri Mar 2 09:01:57 2018`|`2018-03-02T09:01:57`|parse| | 340 | |`Fri Mar 31 2017 21:41:40 GMT+0000 (UTC)`|`2017-03-31T21:41:40Z`|parse| | 341 | |`Friday 02 February 2018 10:42:21 AM`|`2018-02-02T10:42:21`|parse| | 342 | |`Jan 2020`|`2020-01-01`|parse_date|`[assume_date: ~D[0001-12-01]]`| 343 | |`Jan-01-19`|`2019-01-01`|parse| | 344 | |`Jan-01-19`|`2019-01-01T00:00:00`|parse|`[assume_time: true]`| 345 | |`Jan-01-19`|`2019-01-01T10:13:15`|parse|`[assume_time: ~T[10:13:15]]`| 346 | |`Jan-01-2018`|`2018-01-01`|parse| | 347 | |`May 1442`|`1442-05-01`|parse_date|`[assume_date: ~D[0001-12-01]]`| 348 | |`May 30, 2019 4:31:09 AM PDT`|`2019-05-30`|parse_date| | 349 | |`May 30, 2019 4:31:09 AM PDT`|`2019-05-30T11:31:09Z`|parse_datetime|`[to_utc: true]`| 350 | |`Monday 01 October 2018 06:34:19 AM`|`2018-10-01T06:34:19`|parse| | 351 | |`Monday 02 October 2017 9:04:49 AM`|`2017-10-02T09:04:49`|parse| | 352 | |`November 29, 2016`|`2016-11-29`|parse_date| | 353 | |`November 29, 2016`|`2016-11-29T00:00:00`|parse_datetime|`[assume_time: true]`| 354 | |`Oct 5, 2018 6:16:56 PM PDT`|`2018-10-05`|parse_date| | 355 | |`Oct 5, 2018 6:16:56 PM PDT`|`2018-10-06T01:16:56Z`|parse_datetime|`[to_utc: true]`| 356 | |`October 1995`|`1995-10-01`|parse_date|`[assume_date: ~D[0001-12-01]]`| 357 | |`Sep-19-16`|`2016-09-19`|parse_date| | 358 | |`Sep-19-16`|`2016-09-19T00:00:00`|parse_datetime|`[assume_time: true]`| 359 | |`Sun 01 January 2017 10:11:02 PM`|`2017-01-01`|parse_date| | 360 | |`Sun 01 January 2017 10:11:02 PM`|`2017-01-01T22:11:02`|parse_datetime| | 361 | |`Sun Jan 08 2017 04:28:42 GMT+0000 (UTC)`|`2017-01-08T04:28:42Z`|parse| | 362 | |`Sun Jul 1 00:31:18 2018`|`2018-07-01T00:31:18`|parse| | 363 | |`Sun, 01 January 2017 10:11:02 PM`|`2017-01-01`|parse_date| | 364 | |`Sun, 01 January 2017 10:11:02 PM`|`2017-01-01T22:11:02`|parse_datetime| | 365 | |`Sunday 01 January 2017 09:22:46 AM`|`2017-01-01T09:22:46`|parse| | 366 | |`Sunday 01 January 2017 10:11:02 PM`|`2017-01-01`|parse_date| | 367 | |`Sunday 01 January 2017 10:11:02 PM`|`2017-01-01T22:11:02`|parse_datetime| | 368 | |`Sunday 01 January 2017 10:11:02 PM`|`2017-01-01T22:11:02`|parse| | 369 | |`Sunday, 01 January 2017 10:11:02 PM`|`2017-01-01`|parse_date| | 370 | |`Sunday, 01 January 2017 10:11:02 PM`|`2017-01-01T22:11:02`|parse_datetime| | 371 | |`Thu Aug 09 2018 17:13:43 GMT+0000 (UTC)`|`2018-08-09T17:13:43Z`|parse| | 372 | |`Thu Feb 08 00:24:33 2018`|`2018-02-08T00:24:33`|parse| | 373 | |`Thu Jul 5 12:19:56 2018`|`2018-07-05T12:19:56`|parse| | 374 | |`Thursday 30 August 2018 11:31:18 AM`|`2018-08-30T11:31:18`|parse| | 375 | |`Tue Jul 31 06:44:39 2018`|`2018-07-31T06:44:39`|parse| | 376 | |`Tuesday 11 July 2017 1:43:46 PM`|`2017-07-11T13:43:46`|parse| | 377 | |`jul-10-18`|`2018-07-10`|parse| | 378 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 David Bernheisel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DateTimeParser 2 | 3 | [![Hex.pm Version](http://img.shields.io/hexpm/v/date_time_parser.svg)](https://hex.pm/packages/date_time_parser) 4 | [![Hex docs](http://img.shields.io/badge/hex.pm-docs-blue.svg?style=flat)](https://hexdocs.pm/date_time_parser) 5 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE.md) 6 | [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v1.4%20adopted-ff69b4.svg)](./CODE_OF_CONDUCT.md) 7 | 8 | DateTimeParser is a tokenizer for strings that attempts to parse into a 9 | DateTime, NaiveDateTime if timezone is not determined, Date, or Time. 10 | 11 | You're currently looking at the main branch. [Check out the docs for the latest 12 | published version.](https://hexdocs.pm/date_time_parser) 13 | 14 | ## Documentation 15 | 16 | [See examples automatically generated by the tests](./EXAMPLES.md) 17 | 18 | 19 | 20 | The biggest ambiguity between datetime formats is whether it's `ymd` (year month 21 | day), `mdy` (month day year), or `dmy` (day month year); this is resolved by 22 | checking if there are slashes or dashes. If slashes, then it will try `dmy` 23 | first. All other cases will use the international format `ymd`. Sometimes, if 24 | the conditions are right, it can even parse `dmy` with dashes if the month is a 25 | vocal month (eg, `"Jan"`). 26 | 27 | If the string consists of only numbers, then we will try two other parsers 28 | depending on the number of digits: [Epoch] or [Serial]. Otherwise, we'll try the 29 | tokenizer. 30 | 31 | If the string is 10-11 digits with optional precision, then we'll try to parse 32 | it as a Unix [Epoch] timestamp. 33 | 34 | If the string is 1-5 digits with optional precision, then we'll try to parse it 35 | as a [Serial] timestamp (spreadsheet time) treating 1899-12-31 as 1. This will 36 | cause Excel-produced dates from 1900-01-01 until 1900-03-01 to be incorrect, as 37 | they really are. 38 | 39 | |digits|parser|range|notes| 40 | |---|----|---|---| 41 | |1-5|Serial|low = `1900-01-01`, high = `2173-10-15`. Negative numbers go to `1626-03-17`|Floats indicate time. Integers do not.| 42 | |6-9|Tokenizer|any|This allows for "20190429" to be parsed as `2019-04-29`| 43 | |10-11|Epoch|low = `-1100-02-15 14:13:21`, high = `5138-11-16 09:46:39`|If padded with 0s, then it can capture entire range.| 44 | |else|Tokenizer|any| | 45 | 46 | [Epoch]: https://en.wikipedia.org/wiki/Unix_time 47 | [Serial]: https://support.office.com/en-us/article/date-systems-in-excel-e7fe7167-48a9-4b96-bb53-5612a800b487 48 | 49 | ## Required reading 50 | 51 | * [Elixir DateTime docs](https://hexdocs.pm/elixir/DateTime.html) 52 | * [Elixir NaiveDateTime docs](https://hexdocs.pm/elixir/NaiveDateTime.html) 53 | * [Elixir Date docs](https://hexdocs.pm/elixir/Date.html) 54 | * [Elixir Time docs](https://hexdocs.pm/elixir/Time.html) 55 | * [Elixir Calendar docs](https://hexdocs.pm/elixir/Calendar.html) 56 | 57 | ## Examples 58 | 59 | ```elixir 60 | iex> DateTimeParser.parse("19 September 2018 08:15:22 AM") 61 | {:ok, ~N[2018-09-19 08:15:22]} 62 | 63 | iex> DateTimeParser.parse_datetime("19 September 2018 08:15:22 AM") 64 | {:ok, ~N[2018-09-19 08:15:22]} 65 | 66 | iex> DateTimeParser.parse_datetime("2034-01-13", assume_time: true) 67 | {:ok, ~N[2034-01-13 00:00:00]} 68 | 69 | iex> DateTimeParser.parse_datetime("2034-01-13", assume_time: ~T[06:00:00]) 70 | {:ok, ~N[2034-01-13 06:00:00]} 71 | 72 | iex> DateTimeParser.parse("invalid date 10:30pm") 73 | {:ok, ~T[22:30:00]} 74 | 75 | iex> DateTimeParser.parse("2019-03-11T99:99:99") 76 | {:ok, ~D[2019-03-11]} 77 | 78 | iex> DateTimeParser.parse("2019-03-11T10:30:00pm UNK") 79 | {:ok, ~N[2019-03-11T22:30:00]} 80 | 81 | iex> DateTimeParser.parse("2019-03-11T22:30:00.234+00:00") 82 | {:ok, DateTime.from_naive!(~N[2019-03-11T22:30:00.234Z], "Etc/UTC")} 83 | # `~U[2019-03-11T22:30:00.234Z]` in Elixir 1.9+ 84 | 85 | iex> DateTimeParser.parse_date("2034-01-13") 86 | {:ok, ~D[2034-01-13]} 87 | 88 | iex> DateTimeParser.parse_date("01/01/2017") 89 | {:ok, ~D[2017-01-01]} 90 | 91 | iex> DateTimeParser.parse_datetime("1564154204") 92 | {:ok, DateTime.from_naive!(~N[2019-07-26T15:16:44Z], "Etc/UTC")} 93 | # `~U[2019-07-26T15:16:44Z]` in Elixir 1.9+ 94 | 95 | iex> DateTimeParser.parse_datetime("41261.6013888889") 96 | {:ok, ~N[2012-12-18T14:26:00]} 97 | 98 | iex> DateTimeParser.parse_date("44262") 99 | {:ok, ~D[2021-03-07]} 100 | # This is a serial number date, commonly found in spreadsheets, eg: `=VALUE("03/07/2021")` 101 | 102 | iex> DateTimeParser.parse_datetime("1/1/18 3:24 PM") 103 | {:ok, ~N[2018-01-01T15:24:00]} 104 | 105 | iex> DateTimeParser.parse_datetime("1/1/18 3:24 PM", assume_utc: true) 106 | {:ok, DateTime.from_naive!(~N[2018-01-01T15:24:00Z], "Etc/UTC")} 107 | # `~U[2018-01-01T15:24:00Z]` in Elixir 1.9+ 108 | 109 | iex> DateTimeParser.parse_datetime(~s|"Mar 28, 2018 7:39:53 AM PDT"|, to_utc: true) 110 | {:ok, DateTime.from_naive!(~N[2018-03-28T14:39:53Z], "Etc/UTC")} 111 | 112 | iex> {:ok, datetime} = DateTimeParser.parse_datetime(~s|"Mar 1, 2018 7:39:53 AM PST"|) 113 | iex> datetime 114 | #DateTime<2018-03-01 07:39:53-08:00 PST America/Los_Angeles> 115 | 116 | iex> DateTimeParser.parse_datetime(~s|"Mar 1, 2018 7:39:53 AM PST"|, to_utc: true) 117 | {:ok, DateTime.from_naive!(~N[2018-03-01T15:39:53Z], "Etc/UTC")} 118 | 119 | iex> {:ok, datetime} = DateTimeParser.parse_datetime(~s|"Mar 28, 2018 7:39:53 AM PDT"|) 120 | iex> datetime 121 | #DateTime<2018-03-28 07:39:53-07:00 PDT America/Los_Angeles> 122 | 123 | iex> DateTimeParser.parse_time("10:13pm") 124 | {:ok, ~T[22:13:00]} 125 | 126 | iex> DateTimeParser.parse_time("10:13:34") 127 | {:ok, ~T[10:13:34]} 128 | 129 | iex> DateTimeParser.parse_time("18:14:21.2.0851000000Z") 130 | {:ok, ~T[18:14:21.2.0851]} 131 | 132 | iex> DateTimeParser.parse_datetime(nil) 133 | {:error, "Could not parse nil"} 134 | ``` 135 | 136 | ## Installation 137 | 138 | Add `date_time_parser` to your list of dependencies in `mix.exs`: 139 | 140 | ```elixir 141 | def deps do 142 | [ 143 | {:date_time_parser, "~> 1.2.0"} 144 | ] 145 | end 146 | ``` 147 | 148 | ## Configuration 149 | 150 | You must have a timezone database configured if you want parsing to consider 151 | timezones. See [tz](https://github.com/mathieuprog/tz) or [tzdata](https://github.com/lau/tzdata). 152 | 153 | ```elixir 154 | # This is the default config 155 | alias DateTimeParser.Parser 156 | config :date_time_parser, parsers: [Parser.Epoch, Parser.Serial, Parser.Tokenizer] 157 | 158 | # To enable only specific parsers, include them in the :parsers key. 159 | config :date_time_parser, parsers: [Parser.Tokenizer] 160 | 161 | # To consider more timezones from the past at a performance cost: 162 | config :date_time_parser, include_zones_from: ~N[1900-01-01T00:00:00] 163 | # default is 2020-01-01T00:00:00 164 | 165 | # Adding the timezone database from Tz 166 | config :elixir, :time_zone_database, Tz.TimeZoneDatabase 167 | 168 | # Or in runtime, pass in the parsers in the function. 169 | DateTimeParser.parse(mystring, parsers: [Parser.Tokenizer]) 170 | ``` 171 | 172 | ## Write your own parser 173 | 174 | You can write your own parser! 175 | 176 | If the built-in parsers are not applicable for your use-case, you may build your 177 | own parser to use with this library. Let's write a simple one together. 178 | 179 | First I will check `DateTimeParser.Parser` to see what behaviour my new parser 180 | should implement. It needs two functions: 181 | 182 | 1. `c:DateTimeParser.Parser.preflight/1` 183 | 1. `c:DateTimeParser.Parser.parse/1` 184 | 185 | These functions accept the `t:DateTimeParser.Parser.t/0` struct which contains the 186 | options supplied by the user, the string itself, and the context for which you 187 | should return your result. For example, if the context is `:time` then you should 188 | return a `%Time{}`; if `:datetime` you should return either a 189 | `%NaiveDateTime{}` or a `%DateTime{}`; if `:date` then you should return a 190 | `%Date{}`. 191 | 192 | Let's implement a parser that reads a special time string. Our string will 193 | represent time, but all the digits are shifted up by 10 and must be prefixed 194 | with the secret word: `"boomshakalaka:"`. For example, the real world time of 195 | `01:10` is represented as `boomshakalaka:11:20` in our toy time format. `12:30` 196 | is represented as `boomshakalaka:22:40`, and `5:55` is represented as 197 | `boomshakalaka:15:65`. 198 | 199 | ```elixir 200 | defmodule MyParser do 201 | @behaviour DateTimeParser.Parser 202 | @secret_regex ~r|boomshakalaka:(?