Fast and simple datetime, date, time and duration parsing for rust.

Overview

speedate

CI Coverage Crates.io

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 are compliant with 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 allowed
  • 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 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 for each struct for more details.

This will be the datetime parsing logic for pydantic-core.

Usage

use speedate::{DateTime, Date, Time};

fn main() {
    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,
            },
            offset: Some(0),
        }
    );
    assert_eq!(dt.to_string(), "2022-01-01T12:13:14Z");
}

Performance

speedate is significantly faster than chrono's parse_from_rfc3339 and iso8601.

Micro-benchmarking from benches/main.rs:

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.

Most of these are unknown to most users, and not desired. This library aims to support the most common formats without introducing ambiguity.

Comments
  • How can i get timestamp_micros

    How can i get timestamp_micros

    How can i get timestamp_micros like:

    https://docs.rs/chrono/latest/chrono/struct.DateTime.html#method.timestamp_micros

    we need small unit for timestamp.

    opened by hengfeiyang 4
  • You are advised to optimize format performance

    You are advised to optimize format performance

    You are advised to optimize format performance this change Can improve format performance several times

    for example: this code

    write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day)
    

    to My Code

    impl Display for LogDate {
        fn fmt(&self, f: &mut Formatter) -> fmt::Result {
           let mut buf: [u8; 29] = *b"0000-00-00 00:00:00.000000000";
    
            buf[0] = b'0' + (self.year / 1000) as u8;
            buf[1] = b'0' + (self.year / 100 % 10) as u8;
            buf[2] = b'0' + (self.year / 10 % 10) as u8;
            buf[3] = b'0' + (self.year % 10) as u8;
    
    
            buf[5] = b'0' + (self.mon / 10) as u8;
            buf[6] = b'0' + (self.mon % 10) as u8;
    
            buf[8] = b'0' + (self.day / 10) as u8;
            buf[9] = b'0' + (self.day % 10) as u8;
    
            buf[11] = b'0' + (self.hour / 10) as u8;
            buf[12] = b'0' + (self.hour % 10) as u8;
            buf[14] = b'0' + (self.min / 10) as u8;
            buf[15] = b'0' + (self.min % 10) as u8;
            buf[17] = b'0' + (self.sec / 10) as u8;
            buf[18] = b'0' + (self.sec % 10) as u8;
    
            buf[19] = b'.';
    
            buf[20] = b'0' + (self.nano / 100000000) as u8;
            buf[21] = b'0' + (self.nano / 10000000 % 10) as u8;
            buf[22] = b'0' + (self.nano / 1000000 % 10) as u8;
            buf[23] = b'0' + (self.nano / 100000 % 10) as u8;
            buf[24] = b'0' + (self.nano / 10000 % 10) as u8;
            buf[25] = b'0' + (self.nano / 1000 % 10) as u8;
            buf[26] = b'0' + (self.nano / 100 % 10) as u8;
            buf[27] = b'0' + (self.nano / 10 % 10) as u8;
            buf[28] = b'0' + (self.nano % 10) as u8;
    
            f.write_str(std::str::from_utf8(&buf[..]).unwrap())
        }
    }
    
    opened by zhuxiujia 4
  • Optimise display methods

    Optimise display methods

    impl for #16 I think the time is also important to the performance of the Format string, such as crates the httpdate, It directly affects HTTP Web framework performance

    • bench code impl
    #[bench]
    fn bench_format_date(bench: &mut Bencher) {
        let date = Date{
            year: 2022,
            month: 7,
            day: 10
        };
        bench.iter(|| {
            date.to_string();
        })
    }
    
    #[bench]
    fn bench_format_time(bench: &mut Bencher) {
        let time = Time{
            hour: 10,
            minute: 11,
            second: 12,
            microsecond: 11
        };
        bench.iter(|| {
            time.to_string();
        })
    }
    
    #[bench]
    fn bench_format_date_time(bench: &mut Bencher) {
        let date = DateTime{
            date: Date {
                year: 2022,
                month: 7,
                day: 10
            },
            time: Time {
                hour: 0,
                minute: 0,
                second: 0,
                microsecond: 0
            },
            offset: Some(60)
        };
        bench.iter(|| {
            date.to_string();
        })
    }
    
    • befor change
    test bench_format_date               ... bench:         149 ns/iter (+/- 3)
    test bench_format_time               ... bench:         231 ns/iter (+/- 4)
    test bench_format_date_time          ... bench:         283 ns/iter (+/- 3) //offset None
    test bench_format_date_time          ... bench:         326 ns/iter (+/- 6) //offset Some(60)
    
    • after change
    test bench_format_date               ... bench:          51 ns/iter (+/- 0)
    test bench_format_time               ... bench:          53 ns/iter (+/- 3)
    test bench_format_date_time          ... bench:          50 ns/iter (+/- 0) //offset None
    test bench_format_date_time          ... bench:         115 ns/iter (+/- 0) //offset Some(60)
    

    The DateTime format will affect about 50ns due to offset, because it is divided twice of Memory allocation

    f.write_str(std::str::from_utf8(&buf[..]).unwrap())?;
    

    so.If I can fix the DateTime format string length to Fixed length, then this performance can be improved to 50ns/iter

    opened by zhuxiujia 2
  • Codecov issues

    Codecov issues

    Hi, Tom from Codecov here. I saw the tweet you posted earlier about having issues with Codecov, and I just wanted to reach out and see what we could do to help here.

    opened by thomasrockhu-codecov 1
  • negative duration comparison error

    negative duration comparison error

    the test is failed. but in python is ok

    let d7 = Duration::new(false, 3, 0, 0);
    let d8 = Duration::new(false, 4, 0, 0);
    assert!(d7 > d8);
    assert!(d8 < d7);
    
    In [3]: timedelta(days=-3) > timedelta(days=-4)
    Out[3]: True
    
    opened by fcfangcc 1
  • comparison operators

    comparison operators

    fix #6

    To check:

    • not sure the timezone comparison is right if the date/time is different
    • maybe we need to support naive vs. non-naive comparison for pydantic?
    opened by samuelcolvin 1
  • Duration

    Duration

    Add the following datetime formats:

    • ISO8601, e.g P1Y2M3DT4H5M6S
    • time formats, e.g. 12:13:14.56
    • time formats prefixed with day(s), e.g. 2 days 12:13:14
    opened by samuelcolvin 1
  • add support for `now()` and `today()`

    add support for `now()` and `today()`

    Using SystemTime.

    Only tricky question is how we get timezone, perhaps we make it an argument to now(), and I guess also to today() since "today in London != today in Syndey"

    opened by samuelcolvin 0
  • Remove iter, manually count positions

    Remove iter, manually count positions

    Tried a custom iterator, but in the end it's even faster if we remove the iterator and count byte positions manually.

    By f657cd3 there's a good custom iterator which is already significantly faster.

    opened by samuelcolvin 0
  • Conversions for DateTime and Duration into SystemTime/Duration

    Conversions for DateTime and Duration into SystemTime/Duration

    Having a way to convert into std::time structs would help, e.g.

    impl From<DateTime> for std::time::SystemTime { ...}
    impl From<&DateTime> for std::time::SystemTime { ...}
    impl From<Duration> for std::time::Duration { ...}
    impl From<&Duration> for std::time::Duration {...}
    
    opened by luben 0
  • Proleptic Gregorian calendar for unix timestamps

    Proleptic Gregorian calendar for unix timestamps

    Better support for the Proleptic Gregorian calendar for timestamps.

    Basically go back before 1600 to 0 BCE.

    Doubt many people will need this, but technically I think it would be correct.

    opened by samuelcolvin 0
  • move `offset` to `Time`

    move `offset` to `Time`

    Again to match python better and allow times with timezones.

    My mistake, didn't notice python times can have timezones.

    Should also rename to tz_offset to be more explicit.

    opened by samuelcolvin 0
Releases(v0.7.0)
  • v0.7.0(Aug 16, 2022)

    • Optimise display methods by @zhuxiujia in #17
    • Add Date::today() and DateTime::now() #20

    Full Changelog: https://github.com/pydantic/speedate/compare/v0.6.0...v0.7.0

    Source code(tar.gz)
    Source code(zip)
  • v0.6.0(Jul 5, 2022)

    • set Duration limit to match python's timedelta at 999_999_999 days c6dc909

    Breaking Change: Duration.day is now a u32, not a u64.

    Full Changelog: https://github.com/samuelcolvin/speedate/compare/v0.5.0...v0.6.0

    Source code(tar.gz)
    Source code(zip)
  • v0.5.0(Jul 2, 2022)

    Changes

    • Avoid overflow from large duration values #11
    • Fix negative duration comparisons #13

    Breaking Change: as part of #11, Duration::new now returns a Result<Duration, ParseError>, not just a Duration since arithmetic while normalising durations can overflow.

    Full Changelog: https://github.com/samuelcolvin/speedate/compare/v0.4.1...v0.5.0

    Source code(tar.gz)
    Source code(zip)
  • v0.4.1(Jun 14, 2022)

  • v0.4.0(Jun 9, 2022)

    • comparison/inequality operators for DateTime, Date, Time and Duration #8

    Full Changelog: https://github.com/samuelcolvin/speedate/compare/v0.3.0...v0.4.0

    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(Jun 8, 2022)

    • make datetime tz offset seconds, more tests and strictness #7
    • more test cases from python c10565b9cb1542560882d56c02229d917be292a0
    • lower case error messages 1bbd69d8c7a8bb15c83ce78f982bcb01d6ec64fa

    Full Changelog: https://github.com/samuelcolvin/speedate/compare/v0.2.0...v0.3.0

    Source code(tar.gz)
    Source code(zip)
  • v0.2.0(Jun 7, 2022)

Owner
Samuel Colvin
Python & Rust developer with a hint of TypeScript, maintainer of pydantic and other libraries. he/him.
Samuel Colvin
Keep a Hetzner Cloud firewall up to date with your dynamic public IP address.

hcloud-firewall-controller hcloud-firewall-controller determines the current public IP and creates or updates a Hetzner Cloud firewall with this IP. S

Max Rosin 4 Feb 6, 2023
PE Parsing, but blazing fast

PE Parser A blazing fast ?? PE Parser written in Rust Motivation The main goals of pe-parser is to write something suitable for a PE Loader. Is editin

Isaac Marovitz 8 Apr 21, 2023
A fast little combinational parsing library.

neure A fast little combinational parsing library Performance rel is mean release, fat is mean release with lto=fat See examples Example use neure::*;

loren 9 Aug 16, 2023
Fast fail2ban-like tools for parsing nginx logs

Fast2ban This is simple fail2ban-like replacement written in Rust. Usage: ./fast2ban # reads default config.toml from current directory ./fast2ban <co

null 36 May 10, 2023
S-expression parsing and writing in Rust

rsexp S-expression parsing and writing in Rust using nom parser combinators. This implemantion aims at being compatible with OCaml's sexplib. The main

Laurent Mazare 12 Oct 18, 2022
Lightweight parsing for Rust proc macros

Lightweight parsing for Rust proc macros Venial is a WIP parser for Rust proc macros. When writing proc macros that need to parse Rust code (such as a

Olivier FAURE 148 Dec 30, 2022
Tutorial for parsing with nom 5.

Nom Tutorial Nom is a wonderful parser combinators library written in Rust. It can handle binary and text files. Consider it where you would otherwise

Benjamin Kay 265 Dec 11, 2022
Static-checked parsing of regexes into structs

Statically-checked regex parsing into structs. This avoids common regex pitfalls like Off by one capture indexes Trying to get nonexistent captures De

Andrew Baxter 4 Dec 18, 2022
Simple time handling in Rust

time Documentation: latest release main branch book Minimum Rust version policy The time crate is guaranteed to compile with any release of rustc from

Time 680 Dec 31, 2022
Construct complex structures within single call + simple compile-time meta-inheritance model with mixins.

Introduction constructivism is a Rust sample-library designed to simplify the construction of structured data by defining and manipulating sequences o

polako.rs 5 Oct 24, 2023
Simple and fast proxy checker that include protocol validation;

Open Proxies ⭐️ Leave me a start please ⭐️ it will motivate me to continue maintaining and adding futures About | Technologies | Requirements | Starti

kmoz000 3 Nov 29, 2022
Rust crate: Overloaded Literals to construct your datatypes without boilerplate and with compile-time validation.

overloaded_literals   Overloaded Literals to construct your datatypes without boilerplate and with compile-time validation. Features Compile-time vali

Qqwy / Marten 6 Apr 14, 2023
Safe, comp time generated queries in rust

query_builder For each struct field following methods will be generated. All fields where_FIELDNAME_eq Numeric fields where_FIELDNAME_le where_FIELDNA

Amirreza Askarpour 2 Oct 31, 2021
Compile time static maps for Rust

Rust-PHF Documentation Rust-PHF is a library to generate efficient lookup tables at compile time using perfect hash functions. It currently uses the C

null 1.3k Jan 1, 2023
Time to dive into Rust!

Lets-Learn-Rust Time to dive into Rust! Day 1 Installation Running a Simple Rust Program Managing Projects with Cargo Basic Programming - Comments and

null 6 Jun 10, 2023
🦀 A Rust CLI to find the optimal time to meet given a when2meet URL

when3meet ?? The Rust when2meet CLI Install | Usage | Contributing & Issues | Docs Built with ❤️ and ?? by Garrett Ladley Install cargo install when3m

Garrett Ladley 4 Sep 18, 2023
Compile-time stuff and other goodies for rustaceans 🦀

?? bagel: Always baked, never fried bagel is a collection of macros and other things that we frequently use at Skytable, primarily to get work done at

Skytable 3 Jul 4, 2022
Mindful Time Tracking: Simplify Your Focus and Boost Productivity Effortlessly.

Mindful Time Tracking: Simplify Your Focus and Boost Productivity Effortlessly. About pace is a mindful productivity tool designed to help you keep tr

pace 6 Mar 1, 2024
A real-time mixer

Pagana Pagana is a real-time mixer. This project is still in early stages of development and is not ready for any kind of production use or testing. D

null 1 Nov 26, 2021