pub mod naive_date {
use crate::ToOrderableBytes;
use ::chrono::{Datelike, NaiveDate};
impl ToOrderableBytes for NaiveDate {
const ENCODED_LEN: usize = 4;
type Bytes = [u8; Self::ENCODED_LEN];
fn to_orderable_bytes(&self) -> [u8; Self::ENCODED_LEN] {
let biased = (self.num_days_from_ce() as u32) ^ (1u32 << 31);
biased.to_be_bytes()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn ymd(year: i32, month: u32, day: u32) -> NaiveDate {
NaiveDate::from_ymd_opt(year, month, day).unwrap()
}
#[test]
fn year_one_biases_to_known_u32() {
assert_eq!(ymd(1, 1, 1).to_orderable_bytes(), [0x80, 0x00, 0x00, 0x01]);
}
#[test]
fn byte_order_matches_chronological_order() {
let ascending = [
NaiveDate::MIN,
ymd(-10000, 1, 1),
ymd(-1, 12, 31),
ymd(1, 1, 1),
ymd(1970, 1, 1),
ymd(2000, 1, 1),
ymd(2026, 4, 29),
ymd(10000, 1, 1),
NaiveDate::MAX,
];
for window in ascending.windows(2) {
let a = window[0].to_orderable_bytes();
let b = window[1].to_orderable_bytes();
assert!(
a < b,
"to_orderable_bytes({}) < to_orderable_bytes({}) failed",
window[0],
window[1]
);
}
}
}
}
pub mod datetime_utc {
use crate::ToOrderableBytes;
use ::chrono::{DateTime, Utc};
pub const ENCODED_LEN: usize = 12;
impl ToOrderableBytes for DateTime<Utc> {
const ENCODED_LEN: usize = ENCODED_LEN;
type Bytes = [u8; ENCODED_LEN];
fn to_orderable_bytes(&self) -> [u8; ENCODED_LEN] {
let secs = self.timestamp();
let nanos = self.timestamp_subsec_nanos();
let secs_biased = (secs as u64) ^ (1u64 << 63);
let mut out = [0u8; ENCODED_LEN];
out[..8].copy_from_slice(&secs_biased.to_be_bytes());
out[8..].copy_from_slice(&nanos.to_be_bytes());
out
}
}
#[cfg(test)]
mod tests {
use super::*;
use ::chrono::TimeZone;
fn dt(secs: i64, nanos: u32) -> DateTime<Utc> {
Utc.timestamp_opt(secs, nanos).single().unwrap()
}
#[test]
fn unix_epoch_canonical_bytes() {
assert_eq!(
dt(0, 0).to_orderable_bytes(),
[0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
}
#[test]
fn byte_order_matches_chronological_order() {
let ascending = [
DateTime::<Utc>::MIN_UTC,
dt(-1_000_000_000_000, 0),
dt(-1_000_000_000, 0),
dt(-1, 999_999_999),
dt(0, 0),
dt(0, 1),
dt(100, 100),
dt(100, 200),
dt(101, 0),
dt(1_000_000_000, 0),
dt(1_000_000_000_000, 0),
DateTime::<Utc>::MAX_UTC,
];
for window in ascending.windows(2) {
let a = window[0].to_orderable_bytes();
let b = window[1].to_orderable_bytes();
assert!(
a < b,
"to_orderable_bytes({}) < to_orderable_bytes({}) failed",
window[0],
window[1]
);
}
}
}
}