tantivy_common/
datetime.rs

1use std::fmt;
2use std::io::{Read, Write};
3
4use serde::{Deserialize, Serialize};
5use time::format_description::well_known::Rfc3339;
6use time::{OffsetDateTime, PrimitiveDateTime, UtcOffset};
7
8use crate::BinarySerializable;
9
10/// Precision with which datetimes are truncated when stored in fast fields. This setting is only
11/// relevant for fast fields. In the docstore, datetimes are always saved with nanosecond precision.
12#[derive(
13    Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default,
14)]
15#[serde(rename_all = "lowercase")]
16pub enum DateTimePrecision {
17    /// Second precision.
18    #[default]
19    Seconds,
20    /// Millisecond precision.
21    Milliseconds,
22    /// Microsecond precision.
23    Microseconds,
24    /// Nanosecond precision.
25    Nanoseconds,
26}
27
28/// A date/time value with nanoseconds precision.
29///
30/// This timestamp does not carry any explicit time zone information.
31/// Users are responsible for applying the provided conversion
32/// functions consistently. Internally the time zone is assumed
33/// to be UTC, which is also used implicitly for JSON serialization.
34///
35/// All constructors and conversions are provided as explicit
36/// functions and not by implementing any `From`/`Into` traits
37/// to prevent unintended usage.
38#[derive(Clone, Default, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
39pub struct DateTime {
40    // Timestamp in nanoseconds.
41    pub(crate) timestamp_nanos: i64,
42}
43
44impl DateTime {
45    /// Minimum possible `DateTime` value.
46    pub const MIN: DateTime = DateTime {
47        timestamp_nanos: i64::MIN,
48    };
49
50    /// Maximum possible `DateTime` value.
51    pub const MAX: DateTime = DateTime {
52        timestamp_nanos: i64::MAX,
53    };
54
55    /// Create new from UNIX timestamp in seconds
56    pub const fn from_timestamp_secs(seconds: i64) -> Self {
57        Self {
58            timestamp_nanos: seconds * 1_000_000_000,
59        }
60    }
61
62    /// Create new from UNIX timestamp in milliseconds
63    pub const fn from_timestamp_millis(milliseconds: i64) -> Self {
64        Self {
65            timestamp_nanos: milliseconds * 1_000_000,
66        }
67    }
68
69    /// Create new from UNIX timestamp in microseconds.
70    pub const fn from_timestamp_micros(microseconds: i64) -> Self {
71        Self {
72            timestamp_nanos: microseconds * 1_000,
73        }
74    }
75
76    /// Create new from UNIX timestamp in nanoseconds.
77    pub const fn from_timestamp_nanos(nanoseconds: i64) -> Self {
78        Self {
79            timestamp_nanos: nanoseconds,
80        }
81    }
82
83    /// Create new from `OffsetDateTime`
84    ///
85    /// The given date/time is converted to UTC and the actual
86    /// time zone is discarded.
87    pub fn from_utc(dt: OffsetDateTime) -> Self {
88        let timestamp_nanos = dt.unix_timestamp_nanos() as i64;
89        Self { timestamp_nanos }
90    }
91
92    /// Create new from `PrimitiveDateTime`
93    ///
94    /// Implicitly assumes that the given date/time is in UTC!
95    /// Otherwise the original value must only be reobtained with
96    /// [`Self::into_primitive()`].
97    pub fn from_primitive(dt: PrimitiveDateTime) -> Self {
98        Self::from_utc(dt.assume_utc())
99    }
100
101    /// Convert to UNIX timestamp in seconds.
102    pub const fn into_timestamp_secs(self) -> i64 {
103        self.timestamp_nanos / 1_000_000_000
104    }
105
106    /// Convert to UNIX timestamp in milliseconds.
107    pub const fn into_timestamp_millis(self) -> i64 {
108        self.timestamp_nanos / 1_000_000
109    }
110
111    /// Convert to UNIX timestamp in microseconds.
112    pub const fn into_timestamp_micros(self) -> i64 {
113        self.timestamp_nanos / 1_000
114    }
115
116    /// Convert to UNIX timestamp in nanoseconds.
117    pub const fn into_timestamp_nanos(self) -> i64 {
118        self.timestamp_nanos
119    }
120
121    /// Convert to UTC `OffsetDateTime`
122    pub fn into_utc(self) -> OffsetDateTime {
123        let utc_datetime = OffsetDateTime::from_unix_timestamp_nanos(self.timestamp_nanos as i128)
124            .expect("valid UNIX timestamp");
125        debug_assert_eq!(UtcOffset::UTC, utc_datetime.offset());
126        utc_datetime
127    }
128
129    /// Convert to `OffsetDateTime` with the given time zone
130    pub fn into_offset(self, offset: UtcOffset) -> OffsetDateTime {
131        self.into_utc().to_offset(offset)
132    }
133
134    /// Convert to `PrimitiveDateTime` without any time zone
135    ///
136    /// The value should have been constructed with [`Self::from_primitive()`].
137    /// Otherwise the time zone is implicitly assumed to be UTC.
138    pub fn into_primitive(self) -> PrimitiveDateTime {
139        let utc_datetime = self.into_utc();
140        // Discard the UTC time zone offset
141        debug_assert_eq!(UtcOffset::UTC, utc_datetime.offset());
142        PrimitiveDateTime::new(utc_datetime.date(), utc_datetime.time())
143    }
144
145    /// Truncates the microseconds value to the corresponding precision.
146    pub fn truncate(self, precision: DateTimePrecision) -> Self {
147        let truncated_timestamp_micros = match precision {
148            DateTimePrecision::Seconds => (self.timestamp_nanos / 1_000_000_000) * 1_000_000_000,
149            DateTimePrecision::Milliseconds => (self.timestamp_nanos / 1_000_000) * 1_000_000,
150            DateTimePrecision::Microseconds => (self.timestamp_nanos / 1_000) * 1_000,
151            DateTimePrecision::Nanoseconds => self.timestamp_nanos,
152        };
153        Self {
154            timestamp_nanos: truncated_timestamp_micros,
155        }
156    }
157}
158
159impl fmt::Debug for DateTime {
160    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
161        let utc_rfc3339 = self.into_utc().format(&Rfc3339).map_err(|_| fmt::Error)?;
162        f.write_str(&utc_rfc3339)
163    }
164}
165
166impl BinarySerializable for DateTime {
167    fn serialize<W: Write + ?Sized>(&self, writer: &mut W) -> std::io::Result<()> {
168        let timestamp_micros = self.into_timestamp_micros();
169        <i64 as BinarySerializable>::serialize(&timestamp_micros, writer)
170    }
171
172    fn deserialize<R: Read>(reader: &mut R) -> std::io::Result<Self> {
173        let timestamp_micros = <i64 as BinarySerializable>::deserialize(reader)?;
174        Ok(Self::from_timestamp_micros(timestamp_micros))
175    }
176}