Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
804163f
add support for naive_date naive_date_time naive_time date_time_utc d…
l-7-l Oct 6, 2025
b23a415
feat(chrono): refactor chrono support and fuzz testing
l-7-l Dec 21, 2025
1954e56
Appease clippy and fix CI using a better-supported big endian arch. (…
finnbear Oct 17, 2025
7f6d6c8
Fix thread local access error. (#93)
finnbear Dec 17, 2025
d7df262
Version 0.6.8 (#96)
finnbear Dec 17, 2025
946257c
Version 0.6.9 - remove deprecated doc_auto_cfg feature. (#97)
finnbear Dec 18, 2025
7d5981f
add support for naive_date naive_date_time naive_time date_time_utc d…
l-7-l Oct 6, 2025
437ec56
Merge branch 'SoftbearStudios:main' into support_for_chrono#39
l-7-l Dec 21, 2025
bd26bd4
resolve conflicts
l-7-l Dec 21, 2025
e68f05d
chore: add chrono to fuzz
l-7-l Dec 21, 2025
be7a642
add support for naive_date naive_date_time naive_time date_time_utc d…
l-7-l Oct 6, 2025
9427f9f
feat(chrono): refactor chrono support and fuzz testing
l-7-l Dec 21, 2025
ad2a731
add support for naive_date naive_date_time naive_time date_time_utc d…
l-7-l Oct 6, 2025
843f389
resolve conflicts
l-7-l Dec 21, 2025
232a3a7
add try_convert_from
l-7-l Mar 28, 2026
fe60bb0
feat: use try_convert_from for chrono
l-7-l Mar 28, 2026
53823bb
add jiff support
l-7-l Mar 28, 2026
a0d7e2c
feat: add jiff support
l-7-l Mar 28, 2026
ada3fd1
chore: 1. Bump crate version from 0.6.9 to 0.7.0 2. change jiff depe…
l-7-l Mar 28, 2026
6d583dc
chore: bump lz4_flex from 0.11.2(yanked) to 0.13.0
l-7-l Mar 28, 2026
cbfecb7
fix(chrono): correct NaiveTime conversion assertions 23:59:59 with 1…
l-7-l Mar 28, 2026
60c51df
chore: remove chrono in default features
l-7-l Mar 28, 2026
37b09f2
test(jiff): remove jiff::Timestamp::now() for Miri compatibility
l-7-l Mar 28, 2026
41954b1
fix(try_convert): use next_unchecked_as_ptr for decode
l-7-l Apr 5, 2026
9bcb09e
refactor(chrono): restructure module and use ConvertFrom for infallib…
l-7-l Apr 5, 2026
a30071f
refactor(jiff): refine Zoned implementation, add tests and fix comment
l-7-l Apr 5, 2026
7239369
chore(chrono): remove src/ext/chrono.rs
l-7-l Apr 5, 2026
4885071
Merge branch 'main' into support_for_chrono#39
l-7-l Apr 5, 2026
277b6eb
chore(jiff/zoned/test): remove Zoned::now
l-7-l Apr 5, 2026
f5e222d
opt(jiff/chorno): add #[inline(always)] for Convert/TryConvert From
l-7-l Apr 5, 2026
25b0983
fix(try_convert): add Copy bound to Decode implementation for memory …
l-7-l Apr 5, 2026
dee35ad
chore(Cargo.toml): change 0.7.0 to 0.6.9
l-7-l Apr 5, 2026
7fe44f0
chore(cargo.toml/jiff): set default-features=false to jiff
l-7-l Apr 5, 2026
86a1736
chore(cargo.toml): remove jiff from the default enabled features
l-7-l Apr 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ exclude = ["fuzz/"]
arrayvec = { version = "0.7", default-features = false, optional = true }
bitcode_derive = { version = "=0.6.7", path = "./bitcode_derive", optional = true }
bytemuck = { version = "1.14", features = [ "min_const_generics", "must_cast" ] }
chrono = { version = ">=0.4", default-features = false, optional = true }
glam = { version = ">=0.21", default-features = false, optional = true }
rust_decimal = { version = "1.36", default-features = false, optional = true }
serde = { version = "1.0", default-features = false, features = [ "alloc" ], optional = true }
Expand All @@ -43,6 +44,7 @@ zstd = "0.13.0"
derive = [ "dep:bitcode_derive" ]
std = [ "serde?/std", "glam?/std", "arrayvec?/std" ]
default = [ "derive", "std" ]
chrono_datetime_fixedoffset = ["dep:chrono"]

[package.metadata.docs.rs]
features = [ "derive", "serde", "std" ]
Expand Down
28 changes: 28 additions & 0 deletions src/ext/date.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::int::ranged_int;
#[cfg(feature = "chrono")]
mod chrono;
#[cfg(feature = "time")]
mod time;

ranged_int!(Hour, u8, 0, 23);
ranged_int!(Minute, u8, 0, 59);
ranged_int!(Second, u8, 0, 59);
ranged_int!(Nanosecond, u32, 0, 999_999_999);

type TimeEncode = (u8, u8, u8, u32);
type TimeDecode = (Hour, Minute, Second, Nanosecond);

#[cfg(feature = "chrono")]
type DateEncode = i32;
#[cfg(feature = "chrono")]
type DateDecode = i32;

#[cfg(feature = "chrono")]
type DateTimeEncode = (DateEncode, TimeEncode);
#[cfg(feature = "chrono")]
type DateTimeDecode = (DateEncode, TimeEncode);

#[cfg(feature = "chrono")]
pub type DateTimeWithOffsetEncode = (DateTimeEncode, i32);
#[cfg(feature = "chrono")]
pub type DateTimeWithOffsetDecode = (DateTimeDecode, i32);
6 changes: 6 additions & 0 deletions src/ext/date/chrono.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#[cfg(feature = "chrono_datetime_fixedoffset")]
mod date_time_fixed_offset;
mod date_time_utc;
mod naive_date;
mod naive_date_time;
mod naive_time;
107 changes: 107 additions & 0 deletions src/ext/date/chrono/date_time_fixed_offset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use chrono::{DateTime, FixedOffset, NaiveDateTime};

use crate::{
convert::{impl_convert, ConvertFrom},
ext::date::{DateTimeEncode, DateTimeWithOffsetDecode, DateTimeWithOffsetEncode},
};

impl_convert!(
DateTime<FixedOffset>,
DateTimeWithOffsetEncode,
DateTimeWithOffsetDecode
);

impl ConvertFrom<&DateTime<FixedOffset>> for DateTimeWithOffsetEncode {
fn convert_from(x: &DateTime<FixedOffset>) -> Self {
let naive_enc = DateTimeEncode::convert_from(&x.naive_utc());
let offset_sec = x.offset().local_minus_utc();

(naive_enc, offset_sec)
}
}

impl ConvertFrom<DateTimeWithOffsetEncode> for DateTime<FixedOffset> {
fn convert_from(enc: DateTimeWithOffsetEncode) -> Self {
let naive = NaiveDateTime::convert_from(enc.0);
let offset =
FixedOffset::east_opt(enc.1).unwrap_or_else(|| FixedOffset::east_opt(0).unwrap());

DateTime::<FixedOffset>::from_naive_utc_and_offset(naive, offset)
}
}

#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use chrono::{DateTime, FixedOffset, NaiveDate};

#[test]
fn test_chrono_datetime_fixedoffset() {
let dates = [
(1, 1, 1),
(1970, 1, 1), // epoch
(2025, 10, 6),
(-44, 3, 15), // BCE
(9999, 12, 31),
];

let offsets = [
-12 * 3600, // UTC-12, Baker Island Time
-11 * 3600, // UTC-11, Niue / Samoa
-5 * 3600, // UTC-5, EST (Eastern Standard Time, 美东冬令时)
-3 * 3600, // UTC-3, BRT (Brasilia Time)
0, // UTC+0, GMT
3600, // UTC+1, CET (Central European Time)
3 * 3600, // UTC+3, MSK (Moscow Time)
5 * 3600 + 1800, // UTC+5:30, IST (India Standard Time)
8 * 3600, // UTC+8, CST (China Standard Time)
14 * 3600, // UTC+14, Line Islands Time
];

let times = [(0, 0, 0), (12, 34, 56), (23, 59, 59)];

for &(y, m, d) in &dates {
for &(h, mi, s) in &times {
let naive = NaiveDate::from_ymd_opt(y, m, d)
.unwrap()
.and_hms_opt(h, mi, s)
.unwrap();

for &offset_sec in &offsets {
let offset = FixedOffset::east_opt(offset_sec).unwrap();
let dt_fixed =
DateTime::<FixedOffset>::from_naive_utc_and_offset(naive, offset);

let enc = crate::encode(&dt_fixed);
let decoded: DateTime<FixedOffset> = crate::decode(&enc).unwrap();

assert_eq!(
dt_fixed, decoded,
"Failed for datetime {:?} with offset {}",
dt_fixed, offset
);
}
}
}
}

fn bench_data() -> Vec<DateTime<FixedOffset>> {
crate::random_data(1000)
.into_iter()
.map(
|(y, m, d, h, mi, s, n, offset_sec): (i32, u32, u32, u32, u32, u32, u32, i32)| {
let naive =
NaiveDate::from_ymd_opt((y % 9999).max(1), (m % 12).max(1), (d % 28) + 1)
.unwrap()
.and_hms_nano_opt(h % 24, mi % 60, s % 60, n % 1_000_000_000)
.unwrap();
let offset = FixedOffset::east_opt(offset_sec % 86_400)
.unwrap_or(FixedOffset::east_opt(0).unwrap());
DateTime::<FixedOffset>::from_naive_utc_and_offset(naive, offset)
},
)
.collect()
}

crate::bench_encode_decode!(data: Vec<DateTime<FixedOffset>>);
}
70 changes: 70 additions & 0 deletions src/ext/date/chrono/date_time_utc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use chrono::{DateTime, NaiveDateTime, Utc};

use crate::{
convert::{impl_convert, ConvertFrom},
ext::date::{DateTimeDecode, DateTimeEncode},
};

impl_convert!(DateTime<Utc>, DateTimeEncode, DateTimeDecode);

impl ConvertFrom<&DateTime<Utc>> for DateTimeEncode {
fn convert_from(x: &DateTime<Utc>) -> Self {
DateTimeEncode::convert_from(&x.naive_utc())
}
}

impl ConvertFrom<DateTimeEncode> for DateTime<Utc> {
fn convert_from(enc: DateTimeEncode) -> Self {
let naive = NaiveDateTime::convert_from(enc);

DateTime::from_naive_utc_and_offset(naive, Utc)
}
}

#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use chrono::{DateTime, NaiveDate, Utc};

#[test]
fn test_chrono_datetime_utc() {
let ymds = [
(1970, 1, 1), // epoch
(2025, 10, 6),
(1, 1, 1),
(-44, 3, 15), // BCE
(9999, 12, 31),
];

for &(y, m, d) in ymds.iter() {
let naive = NaiveDate::from_ymd_opt(y, m, d)
.unwrap()
.and_hms_opt(12, 34, 56)
.unwrap();
let dt_utc = DateTime::<Utc>::from_naive_utc_and_offset(naive, Utc);

let enc = crate::encode(&dt_utc);
let decoded: DateTime<Utc> = crate::decode(&enc).unwrap();

assert_eq!(dt_utc, decoded, "failed for datetime {:?}", dt_utc);
}
}

fn bench_data() -> Vec<DateTime<Utc>> {
crate::random_data(1000)
.into_iter()
.map(
|(y, m, d, h, mi, s, n, _offset_sec): (i32, u32, u32, u32, u32, u32, u32, i32)| {
let naive =
NaiveDate::from_ymd_opt((y % 9999).max(1), (m % 12).max(1), (d % 28) + 1)
.unwrap()
.and_hms_nano_opt(h % 24, mi % 60, s % 60, n % 1_000_000_000)
.unwrap();
DateTime::<Utc>::from_naive_utc_and_offset(naive, Utc)
},
)
.collect()
}

crate::bench_encode_decode!(utc_vec: Vec<DateTime<Utc>>);
}
57 changes: 57 additions & 0 deletions src/ext/date/chrono/naive_date.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use chrono::{Datelike, NaiveDate};

use crate::{
convert::{impl_convert, ConvertFrom},
ext::date::{DateDecode, DateEncode},
};

impl_convert!(NaiveDate, DateEncode, DateDecode);

impl ConvertFrom<&NaiveDate> for DateEncode {
fn convert_from(days: &NaiveDate) -> Self {
days.num_days_from_ce() - 719_163 // 1970-1-1
}
}

impl ConvertFrom<DateDecode> for NaiveDate {
fn convert_from(days: DateDecode) -> Self {
NaiveDate::from_num_days_from_ce_opt(days + 719_163).unwrap() // 1970-1-1
}
}

#[cfg(test)]
mod tests {
#[test]
fn test_chrono_naive_date() {
let dates = [
NaiveDate::from_ymd_opt(1970, 1, 1).unwrap(), // epoch
NaiveDate::from_ymd_opt(2025, 10, 6).unwrap(),
NaiveDate::from_ymd_opt(1, 1, 1).unwrap(),
NaiveDate::from_ymd_opt(-44, 3, 15).unwrap(), // BCE
NaiveDate::from_ymd_opt(9999, 12, 31).unwrap(),
];

for x in dates {
let enc = crate::encode(&x);
let date: NaiveDate = crate::decode(&enc).unwrap();

assert_eq!(x, date, "failed for date {:?}", x);
}
}

use alloc::vec::Vec;
use chrono::NaiveDate;

fn bench_data() -> Vec<NaiveDate> {
crate::random_data(1000)
.into_iter()
.map(|(y, m, d): (i32, u32, u32)| {
let year = (y % 9999).max(1); // 1 ~ 9998
let month = (m % 12).max(1); // 1 ~ 12
let day = (d % 28) + 1; // 1 ~ 28
NaiveDate::from_ymd_opt(year, month, day).unwrap()
})
.collect()
}
crate::bench_encode_decode!(data: Vec<_>);
}
87 changes: 87 additions & 0 deletions src/ext/date/chrono/naive_date_time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};

use crate::{
convert::{impl_convert, ConvertFrom},
ext::date::{DateEncode, DateTimeDecode, DateTimeEncode, TimeEncode},
};

impl_convert!(NaiveDateTime, DateTimeEncode, DateTimeDecode);

impl ConvertFrom<&NaiveDateTime> for DateTimeEncode {
fn convert_from(x: &NaiveDateTime) -> Self {
(
DateEncode::convert_from(&x.date()),
TimeEncode::convert_from(&x.time()),
)
}
}

impl ConvertFrom<DateTimeEncode> for NaiveDateTime {
fn convert_from((date, time): DateTimeEncode) -> Self {
NaiveDateTime::new(
NaiveDate::convert_from(date),
NaiveTime::from_hms_nano_opt(time.0 as u32, time.1 as u32, time.2 as u32, time.3)
.unwrap(),
)
}
}

#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};

use crate::decode;
use crate::encode;

#[test]
fn test_chrono_naive_datetime() {
let dt = NaiveDateTime::new(
NaiveDate::from_ymd_opt(2025, 10, 6).unwrap(),
NaiveTime::from_hms_nano_opt(12, 34, 56, 123_456_789).unwrap(),
);

let encoded = encode(&dt);
let decoded: NaiveDateTime = decode(&encoded).unwrap();

assert_eq!(dt, decoded);

let dt2 = NaiveDateTime::new(
NaiveDate::from_ymd_opt(1, 1, 1).unwrap(),
NaiveTime::from_hms_nano_opt(0, 0, 0, 0).unwrap(),
);
let encoded2 = encode(&dt2);
let decoded2: NaiveDateTime = decode(&encoded2).unwrap();
assert_eq!(dt2, decoded2);
}

fn bench_data() -> Vec<NaiveDateTime> {
crate::random_data(1000)
.into_iter()
.map(
|(y, m, d, h, min, s, n): (i32, u32, u32, u8, u8, u8, u32)| {
let year = (y % 9999).max(1);
let month = (m % 12).max(1);
let day = (d % 28) + 1;
let date = NaiveDate::from_ymd_opt(year, month, day).unwrap();

let hour = h % 24;
let minute = min % 60;
let second = s % 60;
let nano = n % 1_000_000_000;
let time = NaiveTime::from_hms_nano_opt(
hour as u32,
minute as u32,
second as u32,
nano,
)
.unwrap();

NaiveDateTime::new(date, time)
},
)
.collect()
}

crate::bench_encode_decode!(data_vec: Vec<_>);
}
Loading
Loading