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
16pub(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 #[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 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}