aboutsummaryrefslogtreecommitdiffstats
path: root/src/stream/datetime.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/stream/datetime.rs')
-rw-r--r--src/stream/datetime.rs93
1 files changed, 93 insertions, 0 deletions
diff --git a/src/stream/datetime.rs b/src/stream/datetime.rs
new file mode 100644
index 0000000..8b67e78
--- /dev/null
+++ b/src/stream/datetime.rs
@@ -0,0 +1,93 @@
+/**
+ * Date and time representation in the stream.
+ *
+ * SPDX-FileCopyrightText: 2023 Eilertsens Kodeknekkeri
+ * SPDX-FileCopyrightText: 2023 Harald Eilertsen <haraldei@anduin.net>
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+use chrono::NaiveDateTime;
+use serde::Deserialize;
+
+/**
+ * Handle date and time.
+ *
+ * This is mainly a wrapper around the chrono::NaiveDateTime struct,
+ * as the date and time returned by the json data from the zot stream
+ * API's does not contain any timezone information.
+ */
+#[derive(Debug, PartialEq)]
+pub struct DateTime {
+ datetime: Option<NaiveDateTime>
+}
+
+/*
+ * Implement Display for the DateTime type, so that it will display
+ * using the correct time and date format by default.
+ */
+impl std::fmt::Display for DateTime {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self.datetime {
+ Some(dt) => write!(f, "{}", dt.format("%Y-%m-%d %H:%M:%S")),
+ None => write!(f, "0000-00-00 00:00:00"),
+ }
+ }
+}
+
+/*
+ * Since the date and time format returned in the json from the
+ * stream API, does not conform to the RFC3339 format expected by
+ * the default chrono deserializer, we have to implement our own
+ * deserializer.
+ */
+impl<'de> Deserialize<'de> for DateTime {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::de::Deserializer<'de>,
+ {
+ Ok(deserializer.deserialize_str(DateTimeVisitor)?)
+ }
+}
+
+struct DateTimeVisitor;
+
+impl<'de> serde::de::Visitor<'de> for DateTimeVisitor {
+ type Value = DateTime;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(formatter, "a date and time formatted string")
+ }
+
+ fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ let datetime = if s == "0000-00-00 00:00:00" {
+ None
+ } else {
+ Some(NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S")
+ .map_err(|_| E::custom(format!("invalid date time format: {}", s)))?)
+ };
+
+ Ok(Self::Value{ datetime })
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_deserialize_datetime() {
+ let tests = vec![
+ r#""0000-00-00 00:00:00""#,
+ r#""2016-12-17 13:37:00""#,
+ ];
+
+ for test in tests {
+ let datetime: DateTime = serde_json::from_str(test).unwrap();
+ assert_eq!(test.trim_matches('"'), datetime.to_string());
+ }
+ }
+}