Skip to content

Commit a0d7e2c

Browse files
committed
feat: add jiff support
1 parent 53823bb commit a0d7e2c

8 files changed

Lines changed: 464 additions & 0 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ bitcode_derive = { version = "=0.6.9", path = "./bitcode_derive", optional = tru
2020
bytemuck = { version = "1.14", features = [ "min_const_generics", "must_cast" ] }
2121
chrono = { version = ">=0.4", default-features = false, optional = true }
2222
glam = { version = ">=0.21", default-features = false, optional = true }
23+
jiff = { version = "0.2.23", optional = true }
2324
rust_decimal = { version = "1.36", default-features = false, optional = true }
2425
serde = { version = "1.0", default-features = false, features = [ "alloc" ], optional = true }
2526
time = { version = "0.3", default-features = false, optional = true }

src/ext/jiff/date.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
use jiff::civil::Date;
2+
3+
use crate::{
4+
convert::ConvertFrom,
5+
error::error,
6+
int::ranged_int,
7+
try_convert::{impl_try_convert, TryConvertFrom},
8+
};
9+
10+
impl_try_convert!(Date, DateEncode, DateDecode);
11+
12+
// The value is guaranteed to be in the range `-9999..=9999`.
13+
ranged_int!(Year, i16, -9999, 9999);
14+
// The value is guaranteed to be in the range `1..=12`.
15+
ranged_int!(Month, u8, 1, 12);
16+
// The value is guaranteed to be in the range `0..=59`.
17+
ranged_int!(Day, u8, 1, 31);
18+
19+
pub type DateEncode = (i16, u8, u8);
20+
pub type DateDecode = (Year, Month, Day);
21+
22+
impl ConvertFrom<&Date> for DateEncode {
23+
fn convert_from(value: &Date) -> Self {
24+
(value.year(), value.month() as u8, value.day() as u8)
25+
}
26+
}
27+
28+
impl TryConvertFrom<DateDecode> for Date {
29+
fn try_convert_from(value: DateDecode) -> Result<Self, crate::Error> {
30+
Date::new(
31+
value.0.into_inner(),
32+
value.1.into_inner() as i8,
33+
value.2.into_inner() as i8,
34+
)
35+
.map_err(|_| error("Failed to decode date"))
36+
}
37+
}
38+
39+
#[cfg(test)]
40+
mod tests {
41+
use jiff::civil::Date;
42+
43+
#[test]
44+
fn test_date() {
45+
// -9999-01-01
46+
let date = Date::new(-9999, 1, 1).unwrap();
47+
let bytes = crate::encode(&date);
48+
let decoded: Date = crate::decode(&bytes).unwrap();
49+
assert_eq!(decoded, date);
50+
51+
// 9999-12-30
52+
let date = Date::new(9999, 12, 30).unwrap();
53+
let bytes = crate::encode(&date);
54+
let decoded: Date = crate::decode(&bytes).unwrap();
55+
assert_eq!(decoded, date);
56+
57+
// 2025-03-28
58+
let date = Date::new(2025, 3, 28).unwrap();
59+
let bytes = crate::encode(&date);
60+
let decoded: Date = crate::decode(&bytes).unwrap();
61+
assert_eq!(decoded, date);
62+
63+
let date = Date::new(2025, 1, 15).unwrap();
64+
let bytes = crate::encode(&date);
65+
let decoded: Date = crate::decode(&bytes).unwrap();
66+
assert_eq!(decoded, date);
67+
68+
let date = Date::new(2025, 12, 15).unwrap();
69+
let bytes = crate::encode(&date);
70+
let decoded: Date = crate::decode(&bytes).unwrap();
71+
assert_eq!(decoded, date);
72+
73+
let date = Date::new(2025, 4, 30).unwrap();
74+
let bytes = crate::encode(&date);
75+
let decoded: Date = crate::decode(&bytes).unwrap();
76+
assert_eq!(decoded, date);
77+
78+
let bytes = crate::encode(&(-10000i16, 1u8, 1u8));
79+
let result: Result<Date, _> = crate::decode(&bytes);
80+
assert!(result.is_err());
81+
82+
let bytes = crate::encode(&(10000i16, 1u8, 1u8));
83+
let result: Result<Date, _> = crate::decode(&bytes);
84+
assert!(result.is_err());
85+
86+
let bytes = crate::encode(&(2025i16, 0u8, 1u8));
87+
let result: Result<Date, _> = crate::decode(&bytes);
88+
assert!(result.is_err());
89+
90+
let bytes = crate::encode(&(2025i16, 13u8, 1u8));
91+
let result: Result<Date, _> = crate::decode(&bytes);
92+
assert!(result.is_err());
93+
94+
let bytes = crate::encode(&(2025i16, 1u8, 0u8));
95+
let result: Result<Date, _> = crate::decode(&bytes);
96+
assert!(result.is_err());
97+
98+
let date = Date::new(2025, 3, 28).unwrap();
99+
assert!(crate::decode::<Date>(&crate::encode(&date)).is_ok());
100+
}
101+
102+
use alloc::vec::Vec;
103+
104+
fn bench_data() -> Vec<Date> {
105+
crate::random_data(1000)
106+
.into_iter()
107+
.map(|(year, month, day): (i16, i8, i8)| {
108+
let year = year.clamp(-9999, 9999);
109+
let month = month.clamp(1, 12);
110+
let day = day.clamp(1, 28);
111+
112+
Date::new(year, month, day).unwrap()
113+
})
114+
.collect()
115+
}
116+
crate::bench_encode_decode!(date_vec: Vec<_>);
117+
}

src/ext/jiff/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mod date;
2+
mod offset;
3+
mod time;
4+
mod timestamp;
5+
mod zoned;

src/ext/jiff/offset.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use jiff::tz::Offset;
2+
3+
use crate::{
4+
convert::ConvertFrom,
5+
error::error,
6+
int::ranged_int,
7+
try_convert::{impl_try_convert, TryConvertFrom},
8+
};
9+
10+
impl_try_convert!(Offset, OffsetEncoder, OffsetDecoder);
11+
12+
ranged_int!(OffsetDecoder, i32, -93599, 93599);
13+
14+
pub(super) type OffsetEncoder = i32;
15+
16+
impl ConvertFrom<&Offset> for OffsetEncoder {
17+
fn convert_from(value: &Offset) -> Self {
18+
value.seconds()
19+
}
20+
}
21+
22+
impl TryConvertFrom<OffsetDecoder> for Offset {
23+
fn try_convert_from(value: OffsetDecoder) -> Result<Self, crate::Error> {
24+
Offset::from_seconds(value.into_inner()).map_err(|_| error("Failed to decode offset"))
25+
}
26+
}
27+
28+
#[cfg(test)]
29+
mod tests {
30+
31+
#[test]
32+
fn test_offset() {
33+
let offset = Offset::UTC;
34+
let bytes = bitcode::encode(&offset);
35+
let decoded: Offset = bitcode::decode(&bytes).unwrap();
36+
assert_eq!(decoded, offset);
37+
38+
let offset = Offset::from_seconds(93599).unwrap();
39+
let bytes = bitcode::encode(&offset);
40+
let decoded: Offset = bitcode::decode(&bytes).unwrap();
41+
assert_eq!(decoded, offset);
42+
43+
let offset = Offset::from_seconds(-93599).unwrap();
44+
let bytes = bitcode::encode(&offset);
45+
let decoded: Offset = bitcode::decode(&bytes).unwrap();
46+
assert_eq!(decoded, offset);
47+
48+
let offset = Offset::from_seconds(28800).unwrap();
49+
let bytes = bitcode::encode(&offset);
50+
let decoded: Offset = bitcode::decode(&bytes).unwrap();
51+
assert_eq!(decoded, offset);
52+
53+
let offset = Offset::from_seconds(-21600).unwrap();
54+
let bytes = bitcode::encode(&offset);
55+
let decoded: Offset = bitcode::decode(&bytes).unwrap();
56+
assert_eq!(decoded, offset);
57+
58+
let bytes = bitcode::encode(&93600);
59+
let result: Result<Offset, _> = bitcode::decode(&bytes);
60+
assert!(result.is_err());
61+
62+
let bytes = bitcode::encode(&-93600);
63+
let result: Result<Offset, _> = bitcode::decode(&bytes);
64+
assert!(result.is_err());
65+
66+
assert!(crate::decode::<Offset>(&crate::encode(&Offset::UTC)).is_ok());
67+
}
68+
69+
use alloc::vec::Vec;
70+
use jiff::tz::Offset;
71+
72+
fn offset_min() -> i32 {
73+
Offset::MIN.seconds()
74+
}
75+
fn offset_max() -> i32 {
76+
Offset::MAX.seconds()
77+
}
78+
79+
fn bench_data() -> Vec<Offset> {
80+
crate::random_data(1000)
81+
.into_iter()
82+
.map(|secs: i32| {
83+
let secs = secs.clamp(offset_min(), offset_max());
84+
Offset::from_seconds(secs).unwrap()
85+
})
86+
.collect()
87+
}
88+
crate::bench_encode_decode!(offset_vec: Vec<_>);
89+
}

src/ext/jiff/time.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
use jiff::civil::Time;
2+
3+
use crate::{
4+
convert::{impl_convert, ConvertFrom},
5+
int::ranged_int,
6+
};
7+
8+
impl_convert!(Time, TimeEncode, TimeDecode);
9+
10+
// The value is guaranteed to be in the range `0..=23`.
11+
ranged_int!(Hour, u8, 0, 23);
12+
// The value is guaranteed to be in the range `0..=59`.
13+
ranged_int!(Minute, u8, 0, 59);
14+
// The value is guaranteed to be in the range `0..=59`.
15+
ranged_int!(Second, u8, 0, 59);
16+
// The value is guaranteed to be in the range `0..=999_999_999`
17+
ranged_int!(Nanosecond, u32, 0, 999_999_999);
18+
19+
type TimeEncode = (u8, u8, u8, u32);
20+
type TimeDecode = (Hour, Minute, Second, Nanosecond);
21+
22+
impl ConvertFrom<&Time> for TimeEncode {
23+
fn convert_from(value: &Time) -> Self {
24+
(
25+
value.hour() as u8,
26+
value.minute() as u8,
27+
value.second() as u8,
28+
value.subsec_nanosecond() as u32,
29+
)
30+
}
31+
}
32+
33+
impl ConvertFrom<TimeDecode> for Time {
34+
fn convert_from(value: TimeDecode) -> Self {
35+
Time::constant(
36+
value.0.into_inner() as i8,
37+
value.1.into_inner() as i8,
38+
value.2.into_inner() as i8,
39+
value.3.into_inner() as i32,
40+
)
41+
}
42+
}
43+
#[cfg(test)]
44+
mod tests {
45+
#[test]
46+
fn test_time() {
47+
// 00:00:00.000000000
48+
let time = Time::constant(0, 0, 0, 0);
49+
let bytes = bitcode::encode(&time);
50+
let decoded: Time = bitcode::decode(&bytes).unwrap();
51+
assert_eq!(decoded, time);
52+
53+
// 23:59:59.999999999
54+
let time = Time::constant(23, 59, 59, 999_999_999);
55+
let bytes = bitcode::encode(&time);
56+
let decoded: Time = bitcode::decode(&bytes).unwrap();
57+
assert_eq!(decoded, time);
58+
59+
// 23:00:00
60+
let time = Time::constant(23, 0, 0, 0);
61+
let bytes = bitcode::encode(&time);
62+
let decoded: Time = bitcode::decode(&bytes).unwrap();
63+
assert_eq!(decoded, time);
64+
65+
// 00:59:00
66+
let time = Time::constant(0, 59, 0, 0);
67+
let bytes = bitcode::encode(&time);
68+
let decoded: Time = bitcode::decode(&bytes).unwrap();
69+
assert_eq!(decoded, time);
70+
71+
// 00:00:59
72+
let time = Time::constant(0, 0, 59, 0);
73+
let bytes = bitcode::encode(&time);
74+
let decoded: Time = bitcode::decode(&bytes).unwrap();
75+
assert_eq!(decoded, time);
76+
77+
// 12:30:45.123456789
78+
let time = Time::constant(12, 30, 45, 123456789);
79+
let bytes = bitcode::encode(&time);
80+
let decoded: Time = bitcode::decode(&bytes).unwrap();
81+
assert_eq!(decoded, time);
82+
83+
let time = Time::default();
84+
let bytes = bitcode::encode(&time);
85+
let decoded: Time = bitcode::decode(&bytes).unwrap();
86+
assert_eq!(decoded, time);
87+
88+
assert!(crate::decode::<Time>(&crate::encode(&Time::default())).is_ok());
89+
}
90+
91+
use alloc::vec::Vec;
92+
use jiff::civil::Time;
93+
94+
fn bench_data() -> Vec<Time> {
95+
crate::random_data(1000)
96+
.into_iter()
97+
.map(|(h, m, s, n): (u8, u8, u8, u32)| {
98+
Time::constant(
99+
(h % 23) as i8,
100+
(m % 59) as i8,
101+
(s % 59) as i8,
102+
(n % 999_999_999) as i32,
103+
)
104+
})
105+
.collect()
106+
}
107+
crate::bench_encode_decode!(time_vec: Vec<_>);
108+
}

0 commit comments

Comments
 (0)