Skip to content

Commit ac69580

Browse files
committed
Add integration with jiff::Timestamp
1 parent 5622ad4 commit ac69580

File tree

9 files changed

+284
-1
lines changed

9 files changed

+284
-1
lines changed

Cargo.lock

Lines changed: 40 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ default = ["compat-3-0-0"]
3737
compat-3-0-0 = []
3838
# if enabled, include API for interfacing with chrono 0.4
3939
chrono-0_4 = ["dep:chrono"]
40+
# if enabled, include API for interfacing with jiff 0.2
41+
jiff-0_2 = ["dep:jiff"]
4042
# enable the large-dates feature for the time crate
4143
large_dates = ["time/large-dates"]
4244
# if enabled, include API for interfacing with uuid 1.x
@@ -56,6 +58,7 @@ name = "bson"
5658
[dependencies]
5759
ahash = "0.8.0"
5860
chrono = { version = "0.4.15", features = ["std"], default-features = false, optional = true }
61+
jiff = { version = "0.2", default-features = false, optional = true }
5962
rand = "0.9"
6063
serde = { version = "1.0", features = ["derive"], optional = true }
6164
serde_json = { version = "1.0", features = ["preserve_order"], optional = true }
@@ -87,6 +90,7 @@ serde_bytes = "0.11"
8790
serde_path_to_error = "0.1.16"
8891
serde_json = "1"
8992
chrono = { version = "0.4", features = ["serde", "clock", "std"], default-features = false }
93+
jiff = { version = "0.2", default-features = false, features = ["std"] }
9094

9195
[package.metadata.docs.rs]
9296
all-features = true

src/bson.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,13 @@ impl<T: chrono::TimeZone> From<chrono::DateTime<T>> for Bson {
408408
}
409409
}
410410

411+
#[cfg(feature = "jiff-0_2")]
412+
impl From<jiff::Timestamp> for Bson {
413+
fn from(a: jiff::Timestamp) -> Bson {
414+
Bson::DateTime(crate::DateTime::from(a))
415+
}
416+
}
417+
411418
#[cfg(feature = "uuid-1")]
412419
impl From<uuid::Uuid> for Bson {
413420
fn from(uuid: uuid::Uuid) -> Self {

src/datetime.rs

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::{
1212
use chrono::{LocalResult, TimeZone, Utc};
1313
#[cfg(all(
1414
feature = "serde_with-3",
15-
any(feature = "chrono-0_4", feature = "time-0_3")
15+
any(feature = "chrono-0_4", feature = "time-0_3", feature = "jiff-0_2")
1616
))]
1717
use serde::{Deserialize, Deserializer, Serialize};
1818
use time::format_description::well_known::Rfc3339;
@@ -215,6 +215,13 @@ impl crate::DateTime {
215215
Self::from_millis(dt.timestamp_millis())
216216
}
217217

218+
/// Convert the given [`jiff::Timestamp`] into a [`bson::DateTime`](DateTime), truncating it to
219+
/// millisecond precision.
220+
#[cfg(feature = "jiff-0_2")]
221+
pub fn from_jiff(ts: jiff::Timestamp) -> Self {
222+
Self::from_millis(ts.as_millisecond())
223+
}
224+
218225
/// Returns a builder used to construct a [`DateTime`] from a given year, month,
219226
/// day, and optionally, an hour, minute, second and millisecond, which default to
220227
/// 0 if not explicitly set.
@@ -253,6 +260,33 @@ impl crate::DateTime {
253260
}
254261
}
255262

263+
264+
/// Convert this [`DateTime`] to a [`jiff::Timestamp`].
265+
///
266+
/// Note: Not every BSON datetime can be represented as a [`jiff::Timestamp`]. For such dates,
267+
/// [`jiff::Timestamp::MIN`] or [`jiff::Timestamp::MAX`] will be returned, whichever
268+
/// is closer.
269+
///
270+
/// ```
271+
/// let bson_dt = bson::DateTime::now();
272+
/// let jiff_ts = bson_dt.to_jiff();
273+
/// assert_eq!(bson_dt.timestamp_millis(), jiff_ts.as_millisecond());
274+
///
275+
/// let big = bson::DateTime::from_millis(i64::MAX);
276+
/// let jiff_big = big.to_jiff();
277+
/// assert_eq!(jiff_big, jiff::Timestamp::MAX)
278+
/// ```
279+
#[cfg(feature = "jiff-0_2")]
280+
pub fn to_jiff(self) -> jiff::Timestamp {
281+
jiff::Timestamp::from_millisecond(self.0).unwrap_or_else(|_| {
282+
if self.0 < 0 {
283+
jiff::Timestamp::MIN
284+
} else {
285+
jiff::Timestamp::MAX
286+
}
287+
})
288+
}
289+
256290
fn from_time_private(dt: time::OffsetDateTime) -> Self {
257291
let millis = dt.unix_timestamp_nanos() / 1_000_000;
258292
match millis.try_into() {
@@ -488,6 +522,46 @@ impl serde_with::SerializeAs<chrono::DateTime<Utc>> for crate::DateTime {
488522
}
489523
}
490524

525+
526+
#[cfg(feature = "jiff-0_2")]
527+
impl From<crate::DateTime> for jiff::Timestamp {
528+
fn from(bson_dt: DateTime) -> Self {
529+
bson_dt.to_jiff()
530+
}
531+
}
532+
533+
#[cfg(feature = "jiff-0_2")]
534+
impl From<jiff::Timestamp> for crate::DateTime {
535+
fn from(x: jiff::Timestamp) -> Self {
536+
Self::from_jiff(x)
537+
}
538+
}
539+
540+
#[cfg(all(feature = "jiff-0_2", feature = "serde_with-3"))]
541+
impl<'de> serde_with::DeserializeAs<'de, jiff::Timestamp> for crate::DateTime {
542+
fn deserialize_as<D>(deserializer: D) -> std::result::Result<jiff::Timestamp, D::Error>
543+
where
544+
D: Deserializer<'de>,
545+
{
546+
let dt = DateTime::deserialize(deserializer)?;
547+
Ok(dt.to_jiff())
548+
}
549+
}
550+
551+
#[cfg(all(feature = "jiff-0_2", feature = "serde_with-3"))]
552+
impl serde_with::SerializeAs<jiff::Timestamp> for crate::DateTime {
553+
fn serialize_as<S>(
554+
source: &jiff::Timestamp,
555+
serializer: S,
556+
) -> std::result::Result<S::Ok, S::Error>
557+
where
558+
S: serde::Serializer,
559+
{
560+
let dt = DateTime::from_jiff(*source);
561+
dt.serialize(serializer)
562+
}
563+
}
564+
491565
#[cfg(feature = "time-0_3")]
492566
impl From<crate::DateTime> for time::OffsetDateTime {
493567
fn from(bson_dt: DateTime) -> Self {

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
//! | Feature | Description | Default |
6363
//! |:-------------|:-----------------------------------------------------------------------------------------------------|:--------|
6464
//! | `chrono-0_4` | Enable support for v0.4 of the [`chrono`](https://docs.rs/chrono/0.4) crate in the public API. | no |
65+
//! | `jiff-0_2` | Enable support for v0.2 of the [`jiff`](https://docs.rs/jiff/0.2) crate in the public API. | no |
6566
//! | `uuid-1` | Enable support for v1.x of the [`uuid`](https://docs.rs/uuid/1.x) crate in the public API. | no |
6667
//! | `time-0_3` | Enable support for v0.3 of the [`time`](https://docs.rs/time/0.3) crate in the public API. | no |
6768
//! | `serde_with-3` | Enable [`serde_with`](https://docs.rs/serde_with/3.x) 3.x integrations for [`DateTime`] and [`Uuid`]. | no |

src/serde_helpers.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ pub mod object_id {
8181
/// - [`datetime::FromI64`] — converts an `i64` to and from a [`crate::DateTime`].
8282
/// - [`datetime::FromChrono04DateTime`] — converts a [`chrono::DateTime`] to and from a
8383
/// [`crate::DateTime`].
84+
/// - [`datetime::FromJiff02Timestamp`] — converts a [`jiff::Timestamp`] to and from a
85+
/// [`crate::DateTime`].
8486
/// - [`datetime::FromTime03OffsetDateTime`] — converts a [`time::OffsetDateTime`] to and from a
8587
/// [`crate::DateTime`].
8688
#[cfg(feature = "serde_with-3")]
@@ -200,6 +202,33 @@ pub mod datetime {
200202
}
201203
);
202204

205+
#[cfg(feature = "jiff-0_2")]
206+
serde_conv_doc!(
207+
/// Converts a [`jiff::Timestamp`] to and from a [`DateTime`].
208+
/// ```rust
209+
/// # #[cfg(all(feature = "jiff-0_2", feature = "serde_with-3"))]
210+
/// # {
211+
/// use bson::serde_helpers::datetime;
212+
/// use serde::{Serialize, Deserialize};
213+
/// use serde_with::serde_as;
214+
/// #[serde_as]
215+
/// #[derive(Serialize, Deserialize)]
216+
/// struct Event {
217+
/// #[serde_as(as = "datetime::FromJiff02Timestamp")]
218+
/// pub date: jiff::Timestamp,
219+
/// }
220+
/// # }
221+
/// ```
222+
pub FromJiff02Timestamp,
223+
jiff::Timestamp,
224+
|jiff_ts: &jiff::Timestamp| -> Result<DateTime, String> {
225+
Ok(DateTime::from_jiff(*jiff_ts))
226+
},
227+
|bson_date: DateTime| -> Result<jiff::Timestamp, String> {
228+
Ok(bson_date.to_jiff())
229+
}
230+
);
231+
203232
#[cfg(feature = "time-0_3")]
204233
serde_conv_doc!(
205234
/// Converts a [`time::OffsetDateTime`] to and from a [`DateTime`].

src/tests/modules/bson.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,15 @@ fn from_external_datetime() {
274274
let from_chrono = DateTime::from(now);
275275
assert_millisecond_precision(from_chrono);
276276
}
277+
#[cfg(feature = "jiff-0_2")]
278+
{
279+
let now = jiff::Timestamp::now();
280+
let bson = Bson::from(now);
281+
assert_millisecond_precision(bson.as_datetime().unwrap().to_owned());
282+
283+
let from_jiff = DateTime::from(now);
284+
assert_millisecond_precision(from_jiff);
285+
}
277286

278287
let no_subsec_millis = datetime!(2014-11-28 12:00:09 UTC);
279288
let dt = DateTime::from_time_0_3(no_subsec_millis);
@@ -298,6 +307,18 @@ fn from_external_datetime() {
298307
assert_millisecond_precision(bson.as_datetime().unwrap().to_owned());
299308
assert_subsec_millis(bson.as_datetime().unwrap().to_owned(), 0);
300309
}
310+
#[cfg(feature = "jiff-0_2")]
311+
{
312+
let no_subsec_millis: jiff::Timestamp =
313+
"2014-11-28T12:00:09Z".parse().unwrap();
314+
let dt = DateTime::from(no_subsec_millis);
315+
assert_millisecond_precision(dt);
316+
assert_subsec_millis(dt, 0);
317+
318+
let bson = Bson::from(dt);
319+
assert_millisecond_precision(bson.as_datetime().unwrap().to_owned());
320+
assert_subsec_millis(bson.as_datetime().unwrap().to_owned(), 0);
321+
}
301322

302323
for s in &[
303324
"2014-11-28T12:00:09.123Z",
@@ -327,6 +348,17 @@ fn from_external_datetime() {
327348
assert_millisecond_precision(bson.as_datetime().unwrap().to_owned());
328349
assert_subsec_millis(bson.as_datetime().unwrap().to_owned(), 123);
329350
}
351+
#[cfg(feature = "jiff-0_2")]
352+
{
353+
let jiff_ts: jiff::Timestamp = s.parse().unwrap();
354+
let dt = DateTime::from(jiff_ts);
355+
assert_millisecond_precision(dt);
356+
assert_subsec_millis(dt, 123);
357+
358+
let bson = Bson::from(jiff_ts);
359+
assert_millisecond_precision(bson.as_datetime().unwrap().to_owned());
360+
assert_subsec_millis(bson.as_datetime().unwrap().to_owned(), 123);
361+
}
330362
}
331363

332364
#[cfg(feature = "time-0_3")]
@@ -373,6 +405,28 @@ fn from_external_datetime() {
373405
let bdt = DateTime::MIN;
374406
assert_eq!(bdt.to_chrono(), chrono::DateTime::<Utc>::MIN_UTC);
375407
}
408+
#[cfg(feature = "jiff-0_2")]
409+
{
410+
use chrono::Utc;
411+
412+
let bdt = DateTime::from(jiff::Timestamp::MAX);
413+
assert_eq!(
414+
bdt.to_jiff().as_millisecond(),
415+
jiff::Timestamp::MAX.as_millisecond()
416+
);
417+
418+
let bdt = DateTime::from(jiff::Timestamp::MIN);
419+
assert_eq!(
420+
bdt.to_jiff().as_millisecond(),
421+
jiff::Timestamp::MIN.as_millisecond()
422+
);
423+
424+
let bdt = DateTime::MAX;
425+
assert_eq!(bdt.to_jiff(), jiff::Timestamp::MAX);
426+
427+
let bdt = DateTime::MIN;
428+
assert_eq!(bdt.to_jiff(), jiff::Timestamp::MAX);
429+
}
376430
}
377431

378432
#[test]

src/tests/modules/serializer_deserializer.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,8 @@ fn test_serialize_utc_date_time() {
327327
let src = time::OffsetDateTime::from_unix_timestamp(1_286_705_410).unwrap();
328328
#[cfg(feature = "chrono-0_4")]
329329
let src = chrono::Utc.timestamp_opt(1_286_705_410, 0).unwrap();
330+
#[cfg(feature = "jiff-0_2")]
331+
let src = jiff::Timestamp::from_second(1_286_705_410, 0).unwrap();
330332
let dst = vec![
331333
18, 0, 0, 0, 9, 107, 101, 121, 0, 208, 111, 158, 149, 43, 1, 0, 0, 0,
332334
];

0 commit comments

Comments
 (0)