surrealdb_expr/val/
datetime.rs1use std::fmt::{self, Display, Formatter};
2use std::ops::Deref;
3use std::str::FromStr;
4use std::{ops, str};
5
6use anyhow::{Result, anyhow};
7use chrono::offset::LocalResult;
8use chrono::{DateTime, SecondsFormat, TimeZone, Utc};
9use common::fmt::QuoteStr;
10use revision::revisioned;
11use storekey::{BorrowDecode, Encode};
12use surrealdb_kvs::timestamp::TimeStampImpl;
13use surrealdb_types::{SqlFormat, ToSql, write_sql};
14
15use crate::expr::Error;
16use crate::syn;
17use crate::val::{Duration, TrySub};
18
19#[revisioned(revision = 1)]
20#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
21#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
22pub struct Datetime(pub DateTime<Utc>);
23
24impl Datetime {
25 pub fn now() -> Datetime {
26 Datetime(Utc::now())
27 }
28}
29
30impl Datetime {
31 pub const MIN_UTC: Self = Datetime(DateTime::<Utc>::MIN_UTC);
32 pub const MAX_UTC: Self = Datetime(DateTime::<Utc>::MAX_UTC);
33}
34
35impl From<DateTime<Utc>> for Datetime {
36 fn from(v: DateTime<Utc>) -> Self {
37 Self(v)
38 }
39}
40
41impl From<Datetime> for DateTime<Utc> {
42 fn from(x: Datetime) -> Self {
43 x.0
44 }
45}
46
47impl From<surrealdb_types::Datetime> for Datetime {
48 fn from(v: surrealdb_types::Datetime) -> Self {
49 Self(v.into_inner())
50 }
51}
52
53impl From<Datetime> for surrealdb_types::Datetime {
54 fn from(x: Datetime) -> Self {
55 surrealdb_types::Datetime::from(x.0)
56 }
57}
58
59impl FromStr for Datetime {
60 type Err = ();
61 fn from_str(s: &str) -> Result<Self, Self::Err> {
62 match syn::datetime(s) {
63 Ok(v) => Ok(v.into()),
64 _ => Err(()),
65 }
66 }
67}
68
69impl TryFrom<(i64, u32)> for Datetime {
70 type Error = ();
71 fn try_from(v: (i64, u32)) -> Result<Self, Self::Error> {
72 match Utc.timestamp_opt(v.0, v.1) {
73 LocalResult::Single(v) => Ok(Self(v)),
74 _ => Err(()),
75 }
76 }
77}
78
79impl Deref for Datetime {
80 type Target = DateTime<Utc>;
81 fn deref(&self) -> &Self::Target {
82 &self.0
83 }
84}
85
86impl Datetime {
87 pub fn to_version_stamp(self, ts_impl: &dyn TimeStampImpl) -> Result<u64> {
89 let ts = ts_impl
90 .create_from_datetime(self.0)
91 .ok_or_else(|| anyhow!(Error::TimestampOverflow(self.to_string())))?;
92 Ok(ts.as_versionstamp() as u64)
93 }
94
95 pub fn to_i64(self) -> Option<i64> {
97 self.0.timestamp_nanos_opt()
98 }
99
100 pub fn to_secs(self) -> i64 {
102 self.0.timestamp()
103 }
104}
105
106impl Display for Datetime {
107 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
108 self.0.to_rfc3339_opts(SecondsFormat::AutoSi, true).fmt(f)
109 }
110}
111
112impl ToSql for Datetime {
113 fn fmt_sql(&self, f: &mut String, sql_fmt: SqlFormat) {
114 write_sql!(f, sql_fmt, "d{}", QuoteStr(&self.to_string()))
115 }
116}
117
118impl ops::Sub<Self> for Datetime {
119 type Output = Duration;
120 fn sub(self, other: Self) -> Duration {
121 match (self.0 - other.0).to_std() {
122 Ok(d) => Duration::from(d),
123 Err(_) => Duration::default(),
124 }
125 }
126}
127
128impl TrySub for Datetime {
129 type Output = Duration;
130 fn try_sub(self, other: Self) -> Result<Duration> {
131 (self.0 - other.0)
132 .to_std()
133 .map_err(|_| Error::ArithmeticNegativeOverflow(format!("{self} - {other}")))
134 .map_err(anyhow::Error::new)
135 .map(Duration::from)
136 }
137}
138
139impl<F> Encode<F> for Datetime {
140 fn encode<W: std::io::Write>(
141 &self,
142 w: &mut storekey::Writer<W>,
143 ) -> std::result::Result<(), storekey::EncodeError> {
144 let encode = self.to_rfc3339_opts(SecondsFormat::AutoSi, true);
145 Encode::<F>::encode(&encode, w)
146 }
147}
148
149impl<'de, F> BorrowDecode<'de, F> for Datetime {
150 fn borrow_decode(
151 r: &mut storekey::BorrowReader<'de>,
152 ) -> std::result::Result<Self, storekey::DecodeError> {
153 let s = r.read_str_cow()?;
154 DateTime::parse_from_rfc3339(s.as_ref())
155 .map_err(|_| storekey::DecodeError::InvalidFormat)
156 .map(|x| Datetime(x.to_utc()))
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use rstest::rstest;
163
164 use super::*;
165 use crate::types::PublicDatetime;
166
167 #[rstest]
168 #[case("2021-01-01T00:00:00Z", Datetime(DateTime::<Utc>::from_timestamp(1_609_459_200, 0).unwrap()), PublicDatetime::from_timestamp(1_609_459_200, 0).unwrap())]
169 fn test_from_str(
170 #[case] input: &str,
171 #[case] expected: Datetime,
172 #[case] expected_public: PublicDatetime,
173 ) {
174 let internal_actual = Datetime::from_str(input).unwrap();
175 let public_actual = PublicDatetime::from_str(input).unwrap();
176
177 assert_eq!(internal_actual.timestamp(), expected.timestamp());
178
179 assert_eq!(internal_actual, expected);
180 assert_eq!(public_actual, expected_public);
181
182 assert_eq!(internal_actual.to_string(), public_actual.to_string());
183 }
184}