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}