# speedate [![CI](https://github.com/pydantic/speedate/actions/workflows/ci.yml/badge.svg?event=push)](https://github.com/pydantic/speedate/actions/workflows/ci.yml?query=branch%3Amain) [![Coverage](https://codecov.io/gh/pydantic/speedate/branch/main/graph/badge.svg)](https://codecov.io/gh/pydantic/speedate) [![Crates.io](https://img.shields.io/crates/v/speedate?color=green)](https://crates.io/crates/speedate) Fast and simple datetime, date, time and duration parsing for rust. **speedate** is a lax† **RFC 3339** date and time parser, in other words, it parses common **ISO 8601** formats. **†** - all relaxations of from [RFC 3339](https://tools.ietf.org/html/rfc3339) are compliant with [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601). The following formats are supported: * Date: `YYYY-MM-DD` * Time: `HH:MM:SS` * Time: `HH:MM:SS.FFFFFF` 1 to 6 digits are reflected in the `time.microsecond`, extra digits are ignored * Time: `HH:MM` * Date time: `YYYY-MM-DDTHH:MM:SS` - all the above time formats are allowed for the time part * Date time: `YYYY-MM-DD HH:MM:SS` - `T`, `t`, ` ` and `_` are allowed as separators * Date time: `YYYY-MM-DDTHH:MM:SSZ` - `Z` or `z` is allowed as timezone * Date time: `YYYY-MM-DDTHH:MM:SS+08:00`- positive and negative timezone are allowed, as per ISO 8601, U+2212 minus `−` is allowed as well as ascii minus `-` (U+002D) * Date time: `YYYY-MM-DDTHH:MM:SS+0800` - the colon (`:`) in the timezone is optional * Duration: `PnYnMnDTnHnMnS` - ISO 8601 duration format, see [wikipedia](https://en.wikipedia.org/wiki/ISO_8601#Durations) for more details, `W` for weeks is also allowed * Duration: `HH:MM:SS` - any of the above time formats are allowed to represent a duration * Duration: `D days, HH:MM:SS` - time prefixed by `X days`, case-insensitive, spaces `s` and `,` are all optional * Duration: `D d, HH:MM:SS` - time prefixed by `X d`, case-insensitive, spaces and `,` are optional * Duration: `±...` - all duration formats shown here can be prefixed with `+` or `-` to indicate positive and negative durations respectively In addition, unix timestamps (both seconds and milliseconds) can be used to create dates and datetimes. See [the documentation](https://docs.rs/speedate/latest/speedate/index.html#structs) for each struct for more details. This will be the datetime parsing logic for [pydantic-core](https://github.com/pydantic/pydantic-core). ## Usage ```rust use speedate::{DateTime, Date, Time}; let dt = DateTime::parse_str("2022-01-01T12:13:14Z").unwrap(); assert_eq!( dt, DateTime { date: Date { year: 2022, month: 1, day: 1, }, time: Time { hour: 12, minute: 13, second: 14, microsecond: 0, tz_offset: Some(0), }, } ); assert_eq!(dt.to_string(), "2022-01-01T12:13:14Z"); ``` To control the specifics of time parsing you can use provide a `TimeConfig`: ```rust use speedate::{DateTime, Date, Time, TimeConfig, MicrosecondsPrecisionOverflowBehavior}; let dt = DateTime::parse_bytes_with_config( "1689102037.5586429".as_bytes(), &TimeConfig::builder() .unix_timestamp_offset(Some(0)) .microseconds_precision_overflow_behavior(MicrosecondsPrecisionOverflowBehavior::Truncate) .build(), ).unwrap(); assert_eq!( dt, DateTime { date: Date { year: 2023, month: 7, day: 11, }, time: Time { hour: 19, minute: 0, second: 37, microsecond: 558643, tz_offset: Some(0), }, } ); assert_eq!(dt.to_string(), "2023-07-11T19:00:37.558643Z"); ``` ## Performance **speedate** is significantly faster than [chrono's `parse_from_rfc3339`](https://docs.rs/chrono/latest/chrono/struct.DateTime.html#method.parse_from_rfc3339) and [iso8601](https://crates.io/crates/iso8601). Micro-benchmarking from [`benches/main.rs`](https://github.com/pydantic/speedate/blob/main/benches/main.rs): ```text test datetime_error_speedate ... bench: 6 ns/iter (+/- 0) test datetime_error_chrono ... bench: 50 ns/iter (+/- 1) test datetime_error_iso8601 ... bench: 118 ns/iter (+/- 2) test datetime_ok_speedate ... bench: 9 ns/iter (+/- 0) test datetime_ok_chrono ... bench: 182 ns/iter (+/- 0) test datetime_ok_iso8601 ... bench: 77 ns/iter (+/- 1) test duration_ok_speedate ... bench: 23 ns/iter (+/- 0) test duration_ok_iso8601 ... bench: 48 ns/iter (+/- 0) test timestamp_ok_speedate ... bench: 9 ns/iter (+/- 0) test timestamp_ok_chrono ... bench: 10 ns/iter (+/- 0) ``` ## Why not full iso8601? ISO8601 allows many formats, see [ijmacd.github.io/rfc3339-iso8601](https://ijmacd.github.io/rfc3339-iso8601/). Most of these are unknown to most users, and not desired. This library aims to support the most common formats without introducing ambiguity.