Uh oh!
There was an error while loading. Please reload this page.
Support for chrono (#39) and jiff - #88
Conversation
…ate_time_fixed_offset
Uh oh!
There was an error while loading. Please reload this page.
l-7-l
commented
Oct 13, 2025
🫣 |
dsegovia90
commented
Dec 19, 2025
This would be lovely to have, nice work 👍🏻 |
Sorry for the delay. I took a cursory look and found lots of I don't work with date libraries very often (I always use Unix time in my code) so I could be wrong about the existence or scope of the problem here.
TODO for usIt might be a good idea to introduce Finally, for code style, we would prefer if each date library used its own date types ( |
Sorry for the delay.
Here is what I need to do:
Is my understanding correct? Best regards |
To be clear, the
What I meant was
Yes, please use separate types for the crate (whether from the crate or new types in |
- Separate chrono and time feature support by moving chrono-specific code into dedicated modules/files and removing the shared Date component - Implement conversion from DateTimeDecode to chrono::NaiveDateTime and remove the previous conversion from DateTimeEncode - Remove support for chrono::DateTime<FixedOffset> - Add fuzz testing for: - chrono::NaiveTime - chrono::NaiveDate - chrono::NaiveDateTime - chrono::DateTime<Utc> This change improves modularity, eliminates unnecessary shared types, and strengthens testing coverage for the remaining chrono types.
…oftbearStudios#90) * Appease clippy. * Switch CI to a big endian arch that has rust-std component. * Remove unstable error number from test.
…ate_time_fixed_offset
…ate_time_fixed_offset
- Separate chrono and time feature support by moving chrono-specific code into dedicated modules/files and removing the shared Date component - Implement conversion from DateTimeDecode to chrono::NaiveDateTime and remove the previous conversion from DateTimeEncode - Remove support for chrono::DateTime<FixedOffset> - Add fuzz testing for: - chrono::NaiveTime - chrono::NaiveDate - chrono::NaiveDateTime - chrono::DateTime<Utc> This change improves modularity, eliminates unnecessary shared types, and strengthens testing coverage for the remaining chrono types.
…ate_time_fixed_offset
@finnbear It seems that if we want to use TryFrom, both Decoder and Decode need to be changed… impl<'a,F:TryConvertFrom<T>,T:Decode<'a>>TryDecoder<'a,F>forTryConvertFromDecoder<'a,T>{#[inline(always)]fntry_decode(&mutself) -> Result<F,crate::Error>{F::try_convert_from(self.0.decode())}}It seems that, if we don’t consider bit flips or data tampering, using unwrap is safe, since we’re just decoding data that we ourselves encoded. Also, implementing it in chrono |
I agree |
@finnbear It seems like I don't need ConvertTryFrom anymore collapsed legacy code```rust use crate::{ coder::{Decoder, View}, convert::{impl_convert, ConvertFrom, ConvertIntoEncoder}, error::error, int::{ranged_int, IntDecoder}, Decode, Encode, }; use jiff::{ civil::{DateTime, Time}, tz::TimeZone, Timestamp, };// The value is guaranteed to be in the range type TimeEncode = (u8, u8, u8, u32); impl ConvertFrom<&Time> for TimeEncode { impl ConvertFrom for Time { type TimeStampEncode = (i64, i32); impl ConvertFrom<&Timestamp> for TimeStampEncode { impl Encode for Timestamp { #[derive(Default)] impl<'a> View<'a> for TimestampDecoder { } impl<'a> Decoder<'a, Timestamp> for TimestampDecoder { impl<'a> Decode<'a> for Timestamp { #[cfg(test)] } usecrate::{
coder::{Decoder,View},
convert::{ConvertFrom,ConvertIntoEncoder},
error::error,
int::{ranged_int,IntDecoder},Decode,Encode,};use jiff::{civil::Time,Timestamp};// The value is guaranteed to be in the range `0..=23`.ranged_int!(Hour,u8,0,23);// The value is guaranteed to be in the range `0..=59`.ranged_int!(Minute,u8,0,59);// The value is guaranteed to be in the range `0..=59`.ranged_int!(Second,u8,0,59);// The value is guaranteed to be in the range `0..=999_999_999`ranged_int!(Nanosecond,u32,0,999_999_999);typeTimeEncode = (u8,u8,u8,u32);typeTimeDecode = (Hour,Minute,Second,Nanosecond);implConvertFrom<&Time>forTimeEncode{fnconvert_from(value:&Time) -> Self{(
value.hour()asu8,
value.minute()asu8,
value.second()asu8,
value.subsec_nanosecond()asu32,)}}implConvertFrom<TimeDecode>forTime{fnconvert_from(value:TimeDecode) -> Self{Time::constant(
value.0.into_inner()asi8,
value.1.into_inner()asi8,
value.2.into_inner()asi8,
value.3.into_inner()asi32,)}}typeTimeStampEncode = (i64,i32);implConvertFrom<&Timestamp>forTimeStampEncode{fnconvert_from(value:&Timestamp) -> Self{(value.as_second(), value.subsec_nanosecond())}}implEncodeforTimestamp{typeEncoder = ConvertIntoEncoder<TimeStampEncode>;}#[derive(Default)]pubstructTimestampDecoder{seconds:Vec<i64>,nano:Vec<i32>,pos:usize,}impl<'a>View<'a>forTimestampDecoder{fnpopulate(&mutself,input:&mut&'a[u8],length:usize) -> Result<(),crate::Error>{self.seconds.clear();self.nano.clear();self.pos = 0;letmut sec_decoder = IntDecoder::<i64>::default();
sec_decoder.populate(input, length)?;letmut nano_decoder = IntDecoder::<i32>::default();
nano_decoder.populate(input, length)?;constSEC_MIN:i64 = -377705023201;constSEC_MAX:i64 = 253402207200;constNANOS_MIN:i32 = -999_999_999;constNANOS_MAX:i32 = 999_999_999;for _ in0..length {let sec:i64 = sec_decoder.decode();let nano:i32 = nano_decoder.decode();if !(SEC_MIN..=SEC_MAX).contains(&sec){returnErr(error("seconds out of range"));}if !(NANOS_MIN..=NANOS_MAX).contains(&nano){returnErr(error("nanoseconds out of range"));}if sec == SEC_MIN && nano < 0{returnErr(error("nanoseconds must be >=0 when seconds are minimal"));}self.seconds.push(sec);self.nano.push(nano);}// self.seconds = seconds;// self.nano = nanos;Ok(())}}impl<'a>Decoder<'a,Timestamp>forTimestampDecoder{fndecode(&mutself) -> Timestamp{let sec = self.seconds[self.pos];let nano = self.nano[self.pos];self.pos += 1;Timestamp::constant(sec, nano)}}impl<'a>Decode<'a>forTimestamp{typeDecoder = TimestampDecoder;}#[cfg(test)]mod tests {#[test]fntest(){// 合法:正常秒和纳秒let bytes = bitcode::encode(&(0i64,0i32));let ts:Timestamp = bitcode::decode(&bytes).unwrap();assert_eq!(ts,Timestamp::new(0,0).unwrap());// 合法:秒最小值,纳秒 0let bytes = bitcode::encode(&(UNIX_SECONDS_MIN,0i32));let ts:Timestamp = bitcode::decode(&bytes).unwrap();assert_eq!(ts,Timestamp::new(UNIX_SECONDS_MIN,0).unwrap());// 合法:秒最大值,纳秒最大值let bytes = bitcode::encode(&(UNIX_SECONDS_MAX,NANOS_MAX));let ts:Timestamp = bitcode::decode(&bytes).unwrap();assert_eq!(ts,Timestamp::new(UNIX_SECONDS_MAX,NANOS_MAX).unwrap());// 非法:秒小于最小值let bytes = bitcode::encode(&(UNIX_SECONDS_MIN - 1,0i32));let result:Result<Timestamp,_> = bitcode::decode(&bytes);assert!(result.is_err());// 非法:秒大于最大值let bytes = bitcode::encode(&(UNIX_SECONDS_MAX + 1,0i32));let result:Result<Timestamp,_> = bitcode::decode(&bytes);assert!(result.is_err());// 非法:纳秒小于最小值(-1_000_000_000)let bytes = bitcode::encode(&(0i64, -1_000_000_000i32));let result:Result<Timestamp,_> = bitcode::decode(&bytes);assert!(result.is_err());// 非法:纳秒大于最大值(1_000_000_000)let bytes = bitcode::encode(&(0i64,1_000_000_000i32));let result:Result<Timestamp,_> = bitcode::decode(&bytes);assert!(result.is_err());// 非法:秒为最小值,纳秒为负(组合约束)let bytes = bitcode::encode(&(UNIX_SECONDS_MIN, -1i32));let result:Result<Timestamp,_> = bitcode::decode(&bytes);assert!(result.is_err());// 合法:秒为最小值+1,纳秒为负let bytes = bitcode::encode(&(UNIX_SECONDS_MIN + 1, -500i32));let ts:Timestamp = bitcode::decode(&bytes).unwrap();assert_eq!(ts,Timestamp::new(UNIX_SECONDS_MIN + 1, -500).unwrap());// 合法:秒远大于最小值,纳秒为负let bytes = bitcode::encode(&(1000i64,NANOS_MIN));let ts:Timestamp = bitcode::decode(&bytes).unwrap();assert_eq!(ts,Timestamp::new(1000, -999_999_999).unwrap());// 合法:秒为最小值,纳秒为 0(已测)// 合法:秒为最小值,纳秒为正let bytes = bitcode::encode(&(UNIX_SECONDS_MIN,500i32));let ts:Timestamp = bitcode::decode(&bytes).unwrap();assert_eq!(ts,Timestamp::new(UNIX_SECONDS_MIN,500).unwrap());assert!(crate::decode::<Timestamp>(&crate::encode(&Timestamp::now())).is_ok());}use alloc::vec::Vec;use jiff::Timestamp;constUNIX_SECONDS_MIN:i64 = -377705023201;constUNIX_SECONDS_MAX:i64 = 253402207200;constNANOS_MIN:i32 = -999_999_999;constNANOS_MAX:i32 = 999_999_999;fnbench_data() -> Vec<Timestamp>{crate::random_data(1000).into_iter().map(|(s, n):(i64,i32)| Timestamp::new(s % UNIX_SECONDS_MAX, n % NANOS_MAX).unwrap()).collect()}crate::bench_encode_decode!(duration_vec:Vec<_>);}unning 3 tests
test ext::jiff::tests::test ... ok
test ext::jiff::tests::bench_duration_vec_encode ... ok
test ext::jiff::tests::bench_duration_vec_decode ... ok
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 205 filtered out; finished in 0.00s
* Terminal will be reused by tasks, press any key to close it. Can I submit a PR for Jiff(crates.io:Github:)? |
…000_000_000 ns is valid (leap second support) 23:59:58 with 1_000_000_000 ns is invalid
- Timestamp::now() accesses system clock which is not allowed in Miri isolation mode. - The existing test cases already fully cover all valid/invalid scenarios, so removing this line does not affect test validity.
l-7-l
commented
Mar 28, 2026
@finnbear cc |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
finnbear
commented
Apr 4, 2026
Sorry for the delay. My review comments are above. Thanks very much for this PR! |
1. Add comprehensive tests for Zoned encoding/decoding 2. Use infallible ConvertFrom for Zoned (Timestamp, Offset) tuple conversion 3. Fix comment for Day in date module
l-7-l
commented
Apr 5, 2026
@finnbear Greetings to you, my friend far away. May you be blessed with peace, health, and joy. |
| name = "bitcode" | ||
| authors = [ "Cai Bear", "Finn Bear" ] | ||
| version = "0.6.9" | ||
| version = "0.7.0" |
There was a problem hiding this comment.
Thanks again! Your changes look good to me. Before merging, @caibear and I will need to review more deeply, and I'm not sure when we'll have time to do that (could be weeks/months from now). If any further changes are required, such as not bumping the major version here, there is a good chance I'd personally make them during review, rather than requesting further changes.
Until then, I hope you can get some value from this PR using a patch in your Cargo.toml:
[patch.crates-io]
yew = { git = "https://github.com/l-7-l/bitcode.git", branch = "support_for_chrono#39" }(this doesn't work if you're making a library that you plan to publish on Crates.io, but otherwise it should work)
There was a problem hiding this comment.
Thank you, I will change the 0.70 version back to 0.69.

#39
chrono version>= 0.4:
jiff (version >= 0.2):
crate version 0.6.9 -> 0.7.0
lz4_flex 0.11.2(yanked) -> 0.13.0