clickhouse_data_value/
datetime.rs

1use core::{
2    num::ParseIntError,
3    ops::{Deref, DerefMut},
4    str::FromStr,
5};
6
7use chrono::NaiveDateTime as ChronoNaiveDateTime;
8use pest::{iterators::Pairs, Parser as _};
9use serde::{
10    de::{self, Visitor},
11    ser, Deserialize, Deserializer, Serialize, Serializer,
12};
13
14use crate::date_and_time_parser::{DateAndTimeParser, Rule};
15
16// 2105-12-31 23:59:59
17pub(crate) const UNIX_TIMESTAMP_MAX: u64 = 4291718399;
18
19#[derive(PartialEq, Debug, Clone)]
20pub struct NaiveDateTime(pub ChronoNaiveDateTime);
21impl From<ChronoNaiveDateTime> for NaiveDateTime {
22    fn from(inner: ChronoNaiveDateTime) -> Self {
23        Self(inner)
24    }
25}
26impl Deref for NaiveDateTime {
27    type Target = ChronoNaiveDateTime;
28
29    fn deref(&self) -> &Self::Target {
30        &self.0
31    }
32}
33impl DerefMut for NaiveDateTime {
34    fn deref_mut(&mut self) -> &mut Self::Target {
35        &mut self.0
36    }
37}
38
39#[derive(thiserror::Error, Debug)]
40pub enum ParseError {
41    #[error("FormatMismatch {0}")]
42    FormatMismatch(String),
43    #[error("ValueInvalid {0}")]
44    ValueInvalid(String),
45    #[error("Unknown")]
46    Unknown,
47}
48impl FromStr for NaiveDateTime {
49    type Err = ParseError;
50
51    fn from_str(s: &str) -> Result<Self, Self::Err> {
52        let pair = DateAndTimeParser::parse(Rule::datetime, s)
53            .map_err(|err| ParseError::FormatMismatch(err.to_string()))?
54            .next()
55            .ok_or(ParseError::Unknown)?
56            .into_inner()
57            .next()
58            .ok_or(ParseError::Unknown)?;
59
60        match pair.as_rule() {
61            Rule::datetime_simple => from_simple_pairs(pair.into_inner()),
62            Rule::datetime_iso => from_iso_pairs(pair.into_inner()),
63            Rule::datetime_unix_timestamp => from_unix_timestamp_pairs(pair.into_inner()),
64            _ => Err(ParseError::Unknown),
65        }
66    }
67}
68
69fn from_simple_pairs(
70    mut datetime_simple_pairs: Pairs<'_, Rule>,
71) -> Result<NaiveDateTime, ParseError> {
72    let date_pair = datetime_simple_pairs.next().ok_or(ParseError::Unknown)?;
73    let time_pair = datetime_simple_pairs.next().ok_or(ParseError::Unknown)?;
74    let precision_str = datetime_simple_pairs
75        .next()
76        .map(|time_nf_pair| time_nf_pair.as_str());
77
78    let (str, fmt) = if let Some(precision_str) = precision_str {
79        if precision_str.is_empty() {
80            return Err(ParseError::Unknown);
81        }
82        if precision_str.len() > 9 {
83            return Err(ParseError::Unknown);
84        }
85
86        (
87            format!(
88                "{} {}.{:0<width$}",
89                date_pair.as_str(),
90                time_pair.as_str(),
91                precision_str,
92                width = [3, 3, 3, 6, 6, 6, 9, 9, 9][precision_str.len() - 1]
93            ),
94            format!(
95                "%Y-%m-%d %H:%M:%S%.{}f",
96                [3, 3, 3, 6, 6, 6, 9, 9, 9][precision_str.len() - 1]
97            ),
98        )
99    } else {
100        (
101            format!("{} {}", date_pair.as_str(), time_pair.as_str()),
102            "%Y-%m-%d %H:%M:%S".to_string(),
103        )
104    };
105
106    ChronoNaiveDateTime::parse_from_str(&str, &fmt)
107        .map(Into::into)
108        .map_err(|err| ParseError::ValueInvalid(err.to_string()))
109}
110
111fn from_iso_pairs(mut datetime_iso_pairs: Pairs<'_, Rule>) -> Result<NaiveDateTime, ParseError> {
112    let date_pair = datetime_iso_pairs.next().ok_or(ParseError::Unknown)?;
113    let time_pair = datetime_iso_pairs.next().ok_or(ParseError::Unknown)?;
114    let precision_str = datetime_iso_pairs
115        .next()
116        .map(|time_nf_pair| time_nf_pair.as_str());
117
118    let (str, fmt) = if let Some(precision_str) = precision_str {
119        if precision_str.is_empty() {
120            return Err(ParseError::Unknown);
121        }
122        if precision_str.len() > 9 {
123            return Err(ParseError::Unknown);
124        }
125
126        (
127            format!(
128                "{}T{}.{:0<width$}Z",
129                date_pair.as_str(),
130                time_pair.as_str(),
131                precision_str,
132                width = [3, 3, 3, 6, 6, 6, 9, 9, 9][precision_str.len() - 1]
133            ),
134            format!(
135                "%Y-%m-%dT%H:%M:%S%.{}fZ",
136                [3, 3, 3, 6, 6, 6, 9, 9, 9][precision_str.len() - 1]
137            ),
138        )
139    } else {
140        (
141            format!("{}T{}Z", date_pair.as_str(), time_pair.as_str()),
142            "%Y-%m-%dT%H:%M:%SZ".to_string(),
143        )
144    };
145
146    ChronoNaiveDateTime::parse_from_str(&str, &fmt)
147        .map(Into::into)
148        .map_err(|err| ParseError::ValueInvalid(err.to_string()))
149}
150
151fn from_unix_timestamp_pairs(
152    mut datetime_unix_timestamp_pairs: Pairs<'_, Rule>,
153) -> Result<NaiveDateTime, ParseError> {
154    let unix_timestamp_pair = datetime_unix_timestamp_pairs
155        .next()
156        .ok_or(ParseError::Unknown)?;
157    let precision_str = datetime_unix_timestamp_pairs
158        .next()
159        .map(|time_nf_pair| time_nf_pair.as_str());
160
161    let secs: u64 = unix_timestamp_pair
162        .as_str()
163        .parse()
164        .map_err(|err: ParseIntError| ParseError::ValueInvalid(err.to_string()))?;
165
166    if secs > UNIX_TIMESTAMP_MAX {
167        return Err(ParseError::ValueInvalid(
168            "Override the max Unix Timestamp".to_string(),
169        ));
170    }
171
172    if let Some(precision_str) = precision_str {
173        let nsecs_str = format!("{precision_str:0<9}");
174
175        let nsecs: u32 = nsecs_str
176            .parse()
177            .map_err(|err: ParseIntError| ParseError::ValueInvalid(err.to_string()))?;
178
179        Ok(ChronoNaiveDateTime::from_timestamp_opt(secs as i64, nsecs)
180            .ok_or(ParseError::ValueInvalid(format!(
181                "secs [{secs}] or nsecs [{nsecs}] invalid"
182            )))?
183            .into())
184    } else {
185        Ok(ChronoNaiveDateTime::from_timestamp_opt(secs as i64, 0)
186            .ok_or(ParseError::ValueInvalid(format!("secs [{secs}] invalid")))?
187            .into())
188    }
189}
190
191struct NaiveDateTimeVisitor;
192impl<'de> Visitor<'de> for NaiveDateTimeVisitor {
193    type Value = NaiveDateTime;
194
195    fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
196        formatter.write_str("format simple or iso or unix_timestamp")
197    }
198
199    fn visit_str<E>(self, string: &str) -> Result<Self::Value, E>
200    where
201        E: de::Error,
202    {
203        string
204            .parse()
205            .map_err(|err: ParseError| de::Error::custom(err.to_string()))
206    }
207}
208impl<'de> Deserialize<'de> for NaiveDateTime {
209    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
210    where
211        D: Deserializer<'de>,
212    {
213        deserializer.deserialize_str(NaiveDateTimeVisitor)
214    }
215}
216pub fn deserialize<'de, D>(d: D) -> Result<ChronoNaiveDateTime, D::Error>
217where
218    D: de::Deserializer<'de>,
219{
220    d.deserialize_str(NaiveDateTimeVisitor).map(|x| x.0)
221}
222
223impl Serialize for NaiveDateTime {
224    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
225    where
226        S: Serializer,
227    {
228        serialize(&self.0, serializer)
229    }
230}
231
232pub fn serialize<S>(dt: &ChronoNaiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
233where
234    S: ser::Serializer,
235{
236    serializer.serialize_str(dt.format("%Y-%m-%d %H:%M:%S").to_string().as_str())
237}
238
239#[cfg(test)]
240mod tests {
241    use super::*;
242
243    use std::{fs, path::PathBuf};
244
245    use chrono::NaiveDate;
246
247    #[test]
248    fn test_parse() -> Result<(), Box<dyn std::error::Error>> {
249        let dt_vec = vec![
250            NaiveDate::from_ymd_opt(2021, 3, 1)
251                .expect("")
252                .and_hms_opt(1, 2, 3)
253                .expect(""),
254            NaiveDate::from_ymd_opt(2021, 3, 1)
255                .expect("")
256                .and_hms_milli_opt(1, 2, 3, 100)
257                .expect(""),
258            NaiveDate::from_ymd_opt(2021, 3, 1)
259                .expect("")
260                .and_hms_milli_opt(1, 2, 3, 120)
261                .expect(""),
262            NaiveDate::from_ymd_opt(2021, 3, 1)
263                .expect("")
264                .and_hms_milli_opt(1, 2, 3, 123)
265                .expect(""),
266            NaiveDate::from_ymd_opt(2021, 3, 1)
267                .expect("")
268                .and_hms_micro_opt(1, 2, 3, 123400)
269                .expect(""),
270            NaiveDate::from_ymd_opt(2021, 3, 1)
271                .expect("")
272                .and_hms_micro_opt(1, 2, 3, 123450)
273                .expect(""),
274            NaiveDate::from_ymd_opt(2021, 3, 1)
275                .expect("")
276                .and_hms_micro_opt(1, 2, 3, 123456)
277                .expect(""),
278            NaiveDate::from_ymd_opt(2021, 3, 1)
279                .expect("")
280                .and_hms_nano_opt(1, 2, 3, 123456700)
281                .expect(""),
282            NaiveDate::from_ymd_opt(2021, 3, 1)
283                .expect("")
284                .and_hms_nano_opt(1, 2, 3, 123456780)
285                .expect(""),
286            NaiveDate::from_ymd_opt(2021, 3, 1)
287                .expect("")
288                .and_hms_nano_opt(1, 2, 3, 123456789)
289                .expect(""),
290        ];
291
292        for (s, dt) in vec![
293            "2021-03-01 01:02:03",
294            "2021-03-01 01:02:03.1",
295            "2021-03-01 01:02:03.12",
296            "2021-03-01 01:02:03.123",
297            "2021-03-01 01:02:03.1234",
298            "2021-03-01 01:02:03.12345",
299            "2021-03-01 01:02:03.123456",
300            "2021-03-01 01:02:03.1234567",
301            "2021-03-01 01:02:03.12345678",
302            "2021-03-01 01:02:03.123456789",
303        ]
304        .into_iter()
305        .zip(dt_vec.clone())
306        {
307            assert_eq!(s.parse::<NaiveDateTime>()?, dt.into());
308        }
309
310        for (s, dt) in vec![
311            "2021-03-01T01:02:03Z",
312            "2021-03-01T01:02:03.1Z",
313            "2021-03-01T01:02:03.12Z",
314            "2021-03-01T01:02:03.123Z",
315            "2021-03-01T01:02:03.1234Z",
316            "2021-03-01T01:02:03.12345Z",
317            "2021-03-01T01:02:03.123456Z",
318            "2021-03-01T01:02:03.1234567Z",
319            "2021-03-01T01:02:03.12345678Z",
320            "2021-03-01T01:02:03.123456789Z",
321        ]
322        .into_iter()
323        .zip(dt_vec.clone())
324        {
325            assert_eq!(s.parse::<NaiveDateTime>()?, dt.into());
326        }
327
328        for (s, dt) in vec![
329            "1614560523",
330            "1614560523.1",
331            "1614560523.12",
332            "1614560523.123",
333            "1614560523.1234",
334            "1614560523.12345",
335            "1614560523.123456",
336            "1614560523.1234567",
337            "1614560523.12345678",
338            "1614560523.123456789",
339        ]
340        .into_iter()
341        .zip(dt_vec)
342        {
343            assert_eq!(s.parse::<NaiveDateTime>()?, dt.into());
344        }
345
346        match "".parse::<NaiveDateTime>() {
347            Ok(_) => panic!(),
348            Err(ParseError::FormatMismatch(err)) if err.ends_with("= expected datetime") => {}
349            Err(err) => panic!("{err:?}"),
350        }
351
352        match format!(
353            "{}",
354            NaiveDate::from_ymd_opt(2106, 1, 1)
355                .expect("")
356                .and_hms_opt(0, 0, 0)
357                .expect("")
358                .timestamp()
359        )
360        .parse::<NaiveDateTime>()
361        {
362            Ok(_) => panic!(),
363            Err(ParseError::ValueInvalid(err)) if err == "Override the max Unix Timestamp" => {}
364            Err(err) => panic!("{err:?}"),
365        }
366
367        Ok(())
368    }
369
370    #[derive(Deserialize, Serialize)]
371    struct Row {
372        #[serde(with = "crate::datetime")]
373        datetime_utc: chrono::NaiveDateTime,
374        #[allow(dead_code)]
375        datetime_shanghai: NaiveDateTime,
376    }
377    #[derive(Deserialize)]
378    struct Row64 {
379        #[serde(deserialize_with = "crate::datetime::deserialize")]
380        datetime64_precision0_utc: chrono::NaiveDateTime,
381        #[serde(deserialize_with = "crate::datetime::deserialize")]
382        datetime64_precision1_utc: chrono::NaiveDateTime,
383        //
384        #[serde(deserialize_with = "crate::datetime::deserialize")]
385        datetime64_milli_utc: chrono::NaiveDateTime,
386        #[allow(dead_code)]
387        datetime64_milli_shanghai: NaiveDateTime,
388        #[serde(deserialize_with = "crate::datetime::deserialize")]
389        datetime64_micro_utc: chrono::NaiveDateTime,
390        #[allow(dead_code)]
391        datetime64_micro_shanghai: NaiveDateTime,
392        #[serde(deserialize_with = "crate::datetime::deserialize")]
393        datetime64_nano_utc: chrono::NaiveDateTime,
394        #[allow(dead_code)]
395        datetime64_nano_shanghai: NaiveDateTime,
396    }
397
398    #[test]
399    fn test_de() -> Result<(), Box<dyn std::error::Error>> {
400        let deserializer = de::IntoDeserializer::<de::value::Error>::into_deserializer;
401        assert_eq!(
402            super::deserialize(deserializer("2021-03-01 01:02:03")).unwrap(),
403            NaiveDate::from_ymd_opt(2021, 3, 1)
404                .expect("")
405                .and_hms_opt(1, 2, 3)
406                .expect("")
407        );
408
409        for format in ["simple", "iso", "unix_timestamp"].iter() {
410            let content = fs::read_to_string(
411                PathBuf::new().join(format!("tests/files/datetime_{format}.txt")),
412            )?;
413            let line = content.lines().next().unwrap();
414
415            let Row {
416                datetime_utc,
417                datetime_shanghai: _,
418            } = serde_json::from_str(line)?;
419            assert_eq!(
420                datetime_utc,
421                NaiveDate::from_ymd_opt(2021, 3, 1)
422                    .expect("")
423                    .and_hms_opt(1, 2, 3)
424                    .expect("")
425            );
426
427            //
428            let content = fs::read_to_string(
429                PathBuf::new().join(format!("tests/files/datetime64_{format}.txt")),
430            )?;
431            let line = content.lines().next().unwrap();
432
433            let Row64 {
434                datetime64_precision0_utc,
435                datetime64_precision1_utc,
436                datetime64_milli_utc,
437                datetime64_milli_shanghai: _,
438                datetime64_micro_utc,
439                datetime64_micro_shanghai: _,
440                datetime64_nano_utc,
441                datetime64_nano_shanghai: _,
442            } = serde_json::from_str(line)?;
443            assert_eq!(
444                datetime64_precision0_utc,
445                NaiveDate::from_ymd_opt(2021, 3, 1)
446                    .expect("")
447                    .and_hms_opt(1, 2, 3)
448                    .expect("")
449            );
450            assert_eq!(
451                datetime64_precision1_utc,
452                NaiveDate::from_ymd_opt(2021, 3, 1)
453                    .expect("")
454                    .and_hms_milli_opt(1, 2, 3, 100)
455                    .expect("")
456            );
457            assert_eq!(
458                datetime64_milli_utc,
459                NaiveDate::from_ymd_opt(2021, 3, 1)
460                    .expect("")
461                    .and_hms_milli_opt(1, 2, 3, 123)
462                    .expect("")
463            );
464            assert_eq!(
465                datetime64_micro_utc,
466                NaiveDate::from_ymd_opt(2021, 3, 1)
467                    .expect("")
468                    .and_hms_micro_opt(1, 2, 3, 123456)
469                    .expect("")
470            );
471            assert_eq!(
472                datetime64_nano_utc,
473                NaiveDate::from_ymd_opt(2021, 3, 1)
474                    .expect("")
475                    .and_hms_nano_opt(1, 2, 3, 123456789)
476                    .expect("")
477            );
478        }
479
480        Ok(())
481    }
482
483    #[test]
484    fn test_ser() {
485        let row = Row {
486            datetime_utc: NaiveDate::from_ymd_opt(2023, 1, 2)
487                .expect("")
488                .and_hms_opt(3, 4, 5)
489                .expect(""),
490            datetime_shanghai: NaiveDate::from_ymd_opt(2023, 11, 12)
491                .expect("")
492                .and_hms_opt(12, 13, 14)
493                .expect("")
494                .into(),
495        };
496        assert_eq!(
497            serde_json::to_value(&row).unwrap(),
498            serde_json::json!({
499                "datetime_utc": "2023-01-02 03:04:05",
500                "datetime_shanghai": "2023-11-12 12:13:14",
501            })
502        );
503    }
504}