bson/
datetime.rs

1//! Module containing functionality related to BSON DateTimes.
2//! For more information, see the documentation for the [`DateTime`] type.
3pub(crate) mod builder;
4
5use std::{
6    convert::TryInto,
7    fmt::{self, Display},
8    time::{Duration, SystemTime},
9};
10
11#[cfg(feature = "chrono-0_4")]
12use chrono::{LocalResult, TimeZone, Utc};
13use time::format_description::well_known::Rfc3339;
14
15pub use crate::datetime::builder::DateTimeBuilder;
16use crate::error::{Error, Result};
17
18/// Struct representing a BSON datetime.
19/// Note: BSON datetimes have millisecond precision.
20///
21/// To enable conversions between this type and [`chrono::DateTime`], enable the `"chrono-0_4"`
22/// feature flag in your `Cargo.toml`.
23/// ```
24/// use chrono::prelude::*;
25/// # fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
26/// # #[cfg(feature = "chrono-0_4")]
27/// # {
28/// let chrono_dt: chrono::DateTime<Utc> = "2014-11-28T12:00:09Z".parse()?;
29/// let bson_dt: bson::DateTime = chrono_dt.into();
30/// let bson_dt = bson::DateTime::from_chrono(chrono_dt);
31/// let back_to_chrono: chrono::DateTime<Utc> = bson_dt.into();
32/// let back_to_chrono = bson_dt.to_chrono();
33/// # }
34/// # Ok(())
35/// # }
36/// ```
37///
38/// You may also construct this type from a given `year`, `month`, `day`, and optionally,
39/// an `hour`, `minute`, `second` and `millisecond`, which default to 0 if not explicitly set.
40///
41/// ```
42/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
43/// let dt = bson::DateTime::builder().year(1998).month(2).day(12).minute(1).millisecond(23).build()?;
44/// let expected = bson::DateTime::parse_rfc3339_str("1998-02-12T00:01:00.023Z")?;
45/// assert_eq!(dt, expected);
46/// # Ok(())
47/// # }
48/// ```
49///
50/// ## Serde integration
51///
52/// This type differs from [`chrono::DateTime`] in that it serializes to and deserializes from a
53/// BSON datetime rather than an RFC 3339 formatted string.
54///
55/// e.g.
56/// ```rust
57/// use serde::{Serialize, Deserialize};
58///
59/// #[derive(Serialize, Deserialize)]
60/// struct Foo {
61///     // serializes as a BSON datetime.
62///     date_time: bson::DateTime,
63///
64///     // serializes as an RFC 3339 / ISO-8601 string.
65///     chrono_datetime: chrono::DateTime<chrono::Utc>,
66/// }
67///
68/// # fn main() -> bson::error::Result<()> {
69/// let f = Foo { date_time: bson::DateTime::now(), chrono_datetime: chrono::Utc::now() };
70/// println!("{:?}", bson::serialize_to_document(&f)?);
71/// # Ok(())
72/// # }
73/// ```
74/// Produces the following output:
75/// ```js
76/// { "date_time": DateTime("2023-01-23 20:11:47.316 +00:00:00"), "chrono_datetime": "2023-01-23T20:11:47.316114543Z" }
77/// ```
78///
79/// Additionally, in non-BSON formats, it will serialize to and deserialize from that format's
80/// equivalent of the [extended JSON representation](https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/) of a datetime.
81///
82/// e.g.
83/// ```rust
84/// # use serde::Serialize;
85/// # #[derive(Serialize)]
86/// # struct Foo {
87/// #    // serializes as a BSON datetime.
88/// #    date_time: bson::DateTime,
89/// #
90/// #   // serializes as an RFC 3339 / ISO-8601 string.
91/// #   chrono_datetime: chrono::DateTime<chrono::Utc>,
92/// # }
93/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
94/// let f = Foo { date_time: bson::DateTime::now(), chrono_datetime: chrono::Utc::now() };
95/// println!("{}", serde_json::to_string(&f)?);
96/// # Ok(())
97/// # }
98/// ```
99/// Produces the following output:
100/// ```js
101/// {"date_time":{"$date":{"$numberLong":"1674504029491"}},"chrono_datetime":"2023-01-23T20:00:29.491791120Z"}
102/// ```
103///
104/// ### `serde_helpers`
105/// The `bson` crate provides a number of useful helpers for serializing and deserializing
106/// various datetime types to and from different formats using the [`serde_with`](https://docs.rs/serde_with/latest/serde_with/)
107/// crate.
108///
109/// > **Note:** All helpers in this module require use of the [`#[serde_as]`](https://docs.rs/serde_with/latest/serde_with/attr.serde_as.html)
110/// > attribute on the struct. This enables the enhanced serialization behavior provided by
111/// > `serde_with-3`.
112///
113/// For example, to serialize a [`chrono::DateTime`] as a BSON datetime, you can use
114/// [`crate::serde_helpers::datetime::FromChrono04DateTime`].
115/// Similarly, to serialize a BSON [`DateTime`] to a string, you can use
116/// [`crate::serde_helpers::datetime::AsRfc3339String`]. Check out the
117/// [`crate::serde_helpers`] module documentation for a list of all of the helpers
118/// offered by the crate.
119/// ```rust
120/// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))]
121/// # {
122/// use serde::{Serialize, Deserialize};
123/// use serde_with::serde_as;
124/// use bson::serde_helpers::datetime;
125///
126/// #[serde_as]
127/// #[derive(Serialize, Deserialize)]
128/// struct Foo {
129///     // serializes as a BSON datetime.
130///     date_time: bson::DateTime,
131///
132///     // serializes as an RFC 3339 / ISO-8601 string.
133///     chrono_datetime: chrono::DateTime<chrono::Utc>,
134///
135///     // serializes as a BSON datetime.
136///     // this requires the "chrono-0_4" feature flag
137///     #[serde_as(as = "datetime::FromChrono04DateTime")]
138///     chrono_as_bson: chrono::DateTime<chrono::Utc>,
139///
140///     // serializes as an RFC 3339 / ISO-8601 string.
141///     // this requires the "serde_with-3" feature flag
142///     #[serde_as(as = "datetime::AsRfc3339String")]
143///     bson_as_string: bson::DateTime,
144/// }
145/// # }
146/// ```
147/// The main benefit of using the [`serde_with`](https://docs.rs/serde_with/latest/serde_with/) crate
148/// is that it can handle nested [`chrono::DateTime`] values (e.g. in [`Option`] or [`Vec`]).
149/// ```
150/// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))]
151/// # {
152/// use serde::{Deserialize, Serialize};
153/// use serde_with::serde_as;
154/// use bson::{doc, serde_helpers::datetime};
155///
156/// #[serde_as]
157/// #[derive(Deserialize, Serialize, PartialEq, Debug)]
158/// struct Foo {
159///   /// serializes as a BSON datetime rather than using [`chrono::DateTime`]'s serialization
160///   #[serde_as(as = "Option<datetime::FromChrono04DateTime>")]
161///   as_bson: Option<chrono::DateTime<chrono::Utc>>,
162/// }
163///
164/// let dt = chrono::Utc::now();
165/// let foo = Foo {
166///   as_bson: Some(dt),
167/// };
168///
169/// let expected = doc! {
170///   "as_bson": bson::DateTime::from_chrono(dt),
171/// };
172///
173/// assert_eq!(bson::serialize_to_document(&foo)?, expected);
174/// # }
175/// # Ok::<(), Box<dyn std::error::Error>>(())
176/// ```
177///
178/// ## Large Dates
179/// The range of dates supported by `DateTime` is defined by [`DateTime::MIN`] and
180/// [`DateTime::MAX`]. However, some utilities for constructing and converting `DateTimes`, such as
181/// interop with the [`time::OffsetDateTime`] type and with RFC 3339 strings, are bounded by the
182/// [`time`] crate's supported date range. The `large_dates` feature can be enabled to expand this
183/// range, which enables the
184/// [`large-dates` feature for `time`](https://docs.rs/time/latest/time/#feature-flags).
185#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Copy, Clone)]
186pub struct DateTime(i64);
187
188impl crate::DateTime {
189    /// The latest possible date that can be represented in BSON.
190    pub const MAX: Self = Self::from_millis(i64::MAX);
191
192    /// The earliest possible date that can be represented in BSON.
193    pub const MIN: Self = Self::from_millis(i64::MIN);
194
195    /// Makes a new [`DateTime`] from the number of non-leap milliseconds since
196    /// January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
197    pub const fn from_millis(date: i64) -> Self {
198        Self(date)
199    }
200
201    /// Returns a [`DateTime`] which corresponds to the current date and time.
202    pub fn now() -> DateTime {
203        Self::from_system_time(SystemTime::now())
204    }
205
206    /// Convert the given [`chrono::DateTime`] into a [`bson::DateTime`](DateTime), truncating it to
207    /// millisecond precision.
208    #[cfg(feature = "chrono-0_4")]
209    pub fn from_chrono<T: chrono::TimeZone>(dt: chrono::DateTime<T>) -> Self {
210        Self::from_millis(dt.timestamp_millis())
211    }
212
213    /// Convert the given [`jiff::Timestamp`] into a [`bson::DateTime`](DateTime), truncating it to
214    /// millisecond precision.
215    #[cfg(feature = "jiff-0_2")]
216    pub fn from_jiff(ts: jiff::Timestamp) -> Self {
217        Self::from_millis(ts.as_millisecond())
218    }
219
220    /// Returns a builder used to construct a [`DateTime`] from a given year, month,
221    /// day, and optionally, an hour, minute, second and millisecond, which default to
222    /// 0 if not explicitly set.
223    ///
224    /// Note: You cannot call `build()` before setting at least the year, month and day.
225    pub fn builder() -> DateTimeBuilder {
226        DateTimeBuilder::default()
227    }
228
229    /// Convert this [`DateTime`] to a [`chrono::DateTime<Utc>`].
230    ///
231    /// Note: Not every BSON datetime can be represented as a [`chrono::DateTime`]. For such dates,
232    /// [`chrono::DateTime::MIN_UTC`] or [`chrono::DateTime::MAX_UTC`] will be returned, whichever
233    /// is closer.
234    ///
235    /// ```
236    /// let bson_dt = bson::DateTime::now();
237    /// let chrono_dt = bson_dt.to_chrono();
238    /// assert_eq!(bson_dt.timestamp_millis(), chrono_dt.timestamp_millis());
239    ///
240    /// let big = bson::DateTime::from_millis(i64::MAX);
241    /// let chrono_big = big.to_chrono();
242    /// assert_eq!(chrono_big, chrono::DateTime::<chrono::Utc>::MAX_UTC)
243    /// ```
244    #[cfg(feature = "chrono-0_4")]
245    pub fn to_chrono(self) -> chrono::DateTime<Utc> {
246        match Utc.timestamp_millis_opt(self.0) {
247            LocalResult::Single(dt) => dt,
248            _ => {
249                if self.0 < 0 {
250                    chrono::DateTime::<Utc>::MIN_UTC
251                } else {
252                    chrono::DateTime::<Utc>::MAX_UTC
253                }
254            }
255        }
256    }
257
258    /// Convert this [`DateTime`] to a [`jiff::Timestamp`].
259    ///
260    /// Note: Not every BSON datetime can be represented as a [`jiff::Timestamp`]. For such dates,
261    /// [`jiff::Timestamp::MIN`] or [`jiff::Timestamp::MAX`] will be returned, whichever
262    /// is closer.
263    ///
264    /// ```
265    /// let bson_dt = bson::DateTime::now();
266    /// let jiff_ts = bson_dt.to_jiff();
267    /// assert_eq!(bson_dt.timestamp_millis(), jiff_ts.as_millisecond());
268    ///
269    /// let big = bson::DateTime::from_millis(i64::MAX);
270    /// let jiff_big = big.to_jiff();
271    /// assert_eq!(jiff_big, jiff::Timestamp::MAX)
272    /// ```
273    #[cfg(feature = "jiff-0_2")]
274    pub fn to_jiff(self) -> jiff::Timestamp {
275        jiff::Timestamp::from_millisecond(self.0).unwrap_or({
276            if self.0 < 0 {
277                jiff::Timestamp::MIN
278            } else {
279                jiff::Timestamp::MAX
280            }
281        })
282    }
283
284    fn from_time_private(dt: time::OffsetDateTime) -> Self {
285        let millis = dt.unix_timestamp_nanos() / 1_000_000;
286        match millis.try_into() {
287            Ok(ts) => Self::from_millis(ts),
288            _ => {
289                if millis > 0 {
290                    Self::MAX
291                } else {
292                    Self::MIN
293                }
294            }
295        }
296    }
297
298    #[cfg(not(feature = "time-0_3"))]
299    #[allow(unused)]
300    pub(crate) fn from_time_0_3(dt: time::OffsetDateTime) -> Self {
301        Self::from_time_private(dt)
302    }
303
304    /// Convert the given [`time::OffsetDateTime`] into a [`bson::DateTime`](DateTime), truncating
305    /// it to millisecond precision.
306    ///
307    /// If the provided time is too far in the future or too far in the past to be represented
308    /// by a BSON datetime, either [`DateTime::MAX`] or [`DateTime::MIN`] will be
309    /// returned, whichever is closer.
310    #[cfg(feature = "time-0_3")]
311    pub fn from_time_0_3(dt: time::OffsetDateTime) -> Self {
312        Self::from_time_private(dt)
313    }
314
315    fn to_time_private(self) -> time::OffsetDateTime {
316        match self.to_time_opt() {
317            Some(dt) => dt,
318            None => if self.0 < 0 {
319                time::PrimitiveDateTime::MIN
320            } else {
321                time::PrimitiveDateTime::MAX
322            }
323            .assume_utc(),
324        }
325    }
326
327    pub(crate) fn to_time_opt(self) -> Option<time::OffsetDateTime> {
328        time::OffsetDateTime::UNIX_EPOCH.checked_add(time::Duration::milliseconds(self.0))
329    }
330
331    #[cfg(not(feature = "time-0_3"))]
332    #[allow(unused)]
333    pub(crate) fn to_time_0_3(self) -> time::OffsetDateTime {
334        self.to_time_private()
335    }
336
337    /// Convert this [`DateTime`] to a [`time::OffsetDateTime`].
338    ///
339    /// Note: Not every BSON datetime can be represented as a [`time::OffsetDateTime`]. For such
340    /// dates, [`time::PrimitiveDateTime::MIN`] or [`time::PrimitiveDateTime::MAX`] will be
341    /// returned, whichever is closer.
342    ///
343    /// ```
344    /// let bson_dt = bson::DateTime::now();
345    /// let time_dt = bson_dt.to_time_0_3();
346    /// assert_eq!(bson_dt.timestamp_millis() / 1000, time_dt.unix_timestamp());
347    ///
348    /// let big = bson::DateTime::from_millis(i64::MIN);
349    /// let time_big = big.to_time_0_3();
350    /// assert_eq!(time_big, time::PrimitiveDateTime::MIN.assume_utc())
351    /// ```
352    #[cfg(feature = "time-0_3")]
353    pub fn to_time_0_3(self) -> time::OffsetDateTime {
354        self.to_time_private()
355    }
356
357    /// Convert the given [`std::time::SystemTime`] to a [`DateTime`].
358    ///
359    /// If the provided time is too far in the future or too far in the past to be represented
360    /// by a BSON datetime, either [`DateTime::MAX`] or [`DateTime::MIN`] will be
361    /// returned, whichever is closer.
362    pub fn from_system_time(st: SystemTime) -> Self {
363        match st.duration_since(SystemTime::UNIX_EPOCH) {
364            Ok(d) => {
365                if d.as_millis() <= i64::MAX as u128 {
366                    Self::from_millis(d.as_millis() as i64)
367                } else {
368                    Self::MAX
369                }
370            }
371            // handle SystemTime from before the Unix Epoch
372            Err(e) => {
373                let millis = e.duration().as_millis();
374                if millis > i64::MAX as u128 {
375                    Self::MIN
376                } else {
377                    Self::from_millis(-(millis as i64))
378                }
379            }
380        }
381    }
382
383    /// Convert this [`DateTime`] to a [`std::time::SystemTime`].
384    pub fn to_system_time(self) -> SystemTime {
385        if self.0 >= 0 {
386            SystemTime::UNIX_EPOCH + Duration::from_millis(self.0 as u64)
387        } else {
388            // need to convert to i128 before calculating absolute value since i64::MIN.abs()
389            // overflows and panics.
390            SystemTime::UNIX_EPOCH - Duration::from_millis((self.0 as i128).unsigned_abs() as u64)
391        }
392    }
393
394    /// Returns the number of non-leap-milliseconds since January 1, 1970 UTC.
395    pub const fn timestamp_millis(self) -> i64 {
396        self.0
397    }
398
399    /// Adds `millis` milliseconds to the [`DateTime`] saturating at [`DateTime::MIN`] and
400    /// [`DateTime::MAX`].
401    pub const fn saturating_add_millis(self, millis: i64) -> Self {
402        Self::from_millis(self.0.saturating_add(millis))
403    }
404
405    /// Adds `duration` to the [`DateTime`] saturating at [`DateTime::MAX`].
406    ///
407    /// As [`DateTime`] only have millisecond-precision this will only use the whole milliseconds
408    /// of `duration`.
409    pub const fn saturating_add_duration(self, duration: Duration) -> Self {
410        // i64::try_from isn't const
411        let millis = duration.as_millis();
412        if millis > i64::MAX as u128 {
413            Self::from_millis(i64::MAX)
414        } else {
415            self.saturating_add_millis(millis as i64)
416        }
417    }
418
419    /// Convert this [`DateTime`] to an RFC 3339 formatted string.
420    pub fn try_to_rfc3339_string(self) -> Result<String> {
421        self.to_time_0_3().format(&Rfc3339).map_err(Error::datetime)
422    }
423
424    /// Convert the given RFC 3339 formatted string to a [`DateTime`], truncating it to millisecond
425    /// precision.
426    pub fn parse_rfc3339_str(s: impl AsRef<str>) -> Result<Self> {
427        let odt = time::OffsetDateTime::parse(s.as_ref(), &Rfc3339).map_err(Error::datetime)?;
428        Ok(Self::from_time_0_3(odt))
429    }
430
431    /// Returns the time elapsed since `earlier`, or `None` if the given `DateTime` is later than
432    /// this one.
433    pub fn checked_duration_since(self, earlier: Self) -> Option<Duration> {
434        if earlier.0 > self.0 {
435            return None;
436        }
437        Some(Duration::from_millis((self.0 - earlier.0) as u64))
438    }
439
440    /// Returns the time elapsed since `earlier`, or a [`Duration`] of zero if the given `DateTime`
441    /// is later than this one.
442    pub fn saturating_duration_since(self, earlier: Self) -> Duration {
443        self.checked_duration_since(earlier)
444            .unwrap_or(Duration::ZERO)
445    }
446}
447
448impl fmt::Debug for crate::DateTime {
449    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
450        let mut tup = f.debug_tuple("DateTime");
451        match self.to_time_opt() {
452            Some(dt) => tup.field(&dt),
453            _ => tup.field(&self.0),
454        };
455        tup.finish()
456    }
457}
458
459impl Display for crate::DateTime {
460    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461        match self.to_time_opt() {
462            Some(dt) => Display::fmt(&dt, f),
463            _ => Display::fmt(&self.0, f),
464        }
465    }
466}
467
468impl From<SystemTime> for crate::DateTime {
469    fn from(st: SystemTime) -> Self {
470        Self::from_system_time(st)
471    }
472}
473
474impl From<crate::DateTime> for SystemTime {
475    fn from(dt: crate::DateTime) -> Self {
476        dt.to_system_time()
477    }
478}
479
480#[cfg(feature = "chrono-0_4")]
481impl From<crate::DateTime> for chrono::DateTime<Utc> {
482    fn from(bson_dt: DateTime) -> Self {
483        bson_dt.to_chrono()
484    }
485}
486
487#[cfg(feature = "chrono-0_4")]
488impl<T: chrono::TimeZone> From<chrono::DateTime<T>> for crate::DateTime {
489    fn from(x: chrono::DateTime<T>) -> Self {
490        Self::from_chrono(x)
491    }
492}
493
494#[cfg(feature = "jiff-0_2")]
495impl From<crate::DateTime> for jiff::Timestamp {
496    fn from(bson_dt: DateTime) -> Self {
497        bson_dt.to_jiff()
498    }
499}
500
501#[cfg(feature = "jiff-0_2")]
502impl From<jiff::Timestamp> for crate::DateTime {
503    fn from(x: jiff::Timestamp) -> Self {
504        Self::from_jiff(x)
505    }
506}
507
508#[cfg(feature = "time-0_3")]
509impl From<crate::DateTime> for time::OffsetDateTime {
510    fn from(bson_dt: DateTime) -> Self {
511        bson_dt.to_time_0_3()
512    }
513}
514
515#[cfg(feature = "time-0_3")]
516impl From<time::OffsetDateTime> for crate::DateTime {
517    fn from(x: time::OffsetDateTime) -> Self {
518        Self::from_time_0_3(x)
519    }
520}