Expand description
Low-level date algorithms for libraries
Warning This is a very preliminary release as I test the releasing process. A proper release will follow soon.
This library aims to provide the highest performance algorithms for date manipulation in an unopinionated way. It is meant to be used by the various date and time libraries which can then provide ergonomic and opinionated interfaces for their users.
Usage
The primary contribution of this crate for date libraries are the conversions between a day number from Unix epoch (January 1st, 1970) and a Gregorian date:
use datealgo::{rd_to_date, date_to_rd};
assert_eq!(date_to_rd((1970, 1, 1)), 0);
assert_eq!(date_to_rd((2023, 5, 12)), 19489);
assert_eq!(rd_to_date(19489), (2023, 5, 12));
For convenience, there is also converters to and from Unix timestamps:
use datealgo::{secs_to_datetime, datetime_to_secs};
assert_eq!(datetime_to_secs((1970, 1, 1, 0, 0, 0)), 0);
assert_eq!(datetime_to_secs((2023, 5, 20, 9, 24, 38)), 1684574678);
assert_eq!(secs_to_datetime(1684574678), (2023, 5, 20, 9, 24, 38));
If the std
feature is enabled, there are also converters to and from
SystemTime
:
use datealgo::{systemtime_to_datetime, datetime_to_systemtime};
use std::time::{Duration, UNIX_EPOCH};
assert_eq!(systemtime_to_datetime(UNIX_EPOCH), Some((1970, 1, 1, 0, 0, 0, 0)));
assert_eq!(systemtime_to_datetime(UNIX_EPOCH + Duration::from_secs(1684574678)), Some((2023, 5, 20, 9, 24, 38, 0)));
assert_eq!(datetime_to_systemtime((2023, 5, 20, 9, 24, 38, 0)), UNIX_EPOCH + Duration::from_secs(1684574678));
Features
The crate works in no_std
environments and has no allocations. Most of the
functions also work in constant contexts.
std
(default): Include std::time::SystemTime conversion
Background
There are many date and time libraries for Rust for varying use cases as the standard library doesn’t include any utilities for dealing with dates. Most of these libraries contain their own copies of date algorithms, most prominently the conversion from days since an epoch to a Gregorian calendar date (year, month, day). These algorithms have been sourced from various places with various licenses, often translated either by machine or by hand from C algorithms found in different libc variants. The algorithms are usually somewhat optimized for performance, but fall short of fastest algorithms available.
Notes
The library does not expose any kind of Date
or DateTime
structures, but
simply tuples for the necessary values. There is bounds checking via
debug_assert
, which means that it is not present in release builds.
Callers are required to do their own bounds checking where ever input
require it. Datatypes are selected for performance and utility, rather than
what is most natural for the value.
Currently the library implements algorithms for the Proleptic Gregorian Calendar which is our current calendar extended backwards indefinitely. The Gregorian calendar defines the average year to be 365.2425 days long by defining every fourth year to be a leap year, unless the year is divisible by 100 and not by 400.
The algorithms do not account for leap seconds, as is customary for Unix time. Every day is exactly 86400 in length, and the calculated times do not adjust for leap seconds between timestamps.
We define Rata Die to be integral
day numbers counted from 1st of January, 1979, which is the Unix epoch. We
use the abbreviation rd
to concisely refer to such values. This differs
from the epoch originally chosen by Howard Jacobson, but is more convenient
for usage.
The Rata Die values are represented as i32
for performance reasons. The
needed calculations reduce that to roughly an effective i30
integer range,
which means a usable range of roughly -1,460,000 to 1,460,000 years.
Benchmarks
Results on GitHub Codespaces default VM:
x | datealgo | hinnant | httpdate | humantime | time | chrono |
---|---|---|---|---|---|---|
rd_to_date | 5.0 ns | 9.6 ns | 12.4 ns | 12.3 ns | 23.6 ns | 10.1 ns |
date_to_rd | 3.1 ns | 3.9 ns | 4.2 ns | 3.8 ns | 18.5 ns | 8.6 ns |
systemtime_to_datetime | 17.2 ns | 27.0 ns | 26.8 ns | 51.1 ns | 216.8 ns | |
datetime_to_systemtime | 6.2 ns | 10.9 ns | 10.1 ns | 46.1 ns | 47.5 ns |
Some code has been adapted from the libraries to produce comparable benchmarks.
Modules
- Convenience constants, mostly for input validation
Constants
- Maximum Rata Die for conversion
- Minimum Rata Die for conversion
- Maximum Rata die in seconds for conversion
- Minimum Rata Die in seconds for conversion
- Maximum supported year for conversion
- Minimum supported year for conversion
Functions
- Convert Gregorian date to Rata Die
- Convert Gregorian date to day of week
- Convert year, month, day, hours, minutes and seconds to total seconds
- Convert year, month, day, hours, minutes, seconds and nanoseconds to
SystemTime
- Determine the number of days in the given month in the given year
- Combine days, hours, minutes and seconds to total seconds
- Determine if the given year is a leap year
- Convert Rata Die to Gregorian date
- Convert Rata Die to day of week
- Convert total seconds to year, month, day, hours, minutes and seconds
- Split total seconds to days, hours, minutes and seconds
- Convert seconds and nanoseconds to
SystemTime
- Convert
SystemTime
to year, month, day, hours, minutes, seconds and nanoseconds - Convert
std::time::SystemTime
to seconds and nanoseconds