hdbconnect_impl/serde_db_impl/time/
hana_offset_date_time.rs

1use super::hana_primitive_date_time::HanaPrimitiveDateTimeVisitor;
2use crate::ToHana;
3use serde::ser::Error as _;
4use time::{OffsetDateTime, format_description::FormatItem, macros::format_description};
5
6/// Wraps a `time::OffsetDateTime`, helps with serializing from and deserializing
7/// into `time::OffsetDateTime`.
8///
9/// Note that this is completely based on
10/// [`time::HanaPrimitiveDateTime`](crate::time::HanaPrimitiveDateTime),
11/// since HANA's own date formats have no understanding of timezones.
12/// All deserialized instances of `OffsetDateTime` have offset `UTC`.
13/// All serialized instances of `OffsetDateTime` _must_ have offset `UTC`.
14///
15/// # Example for serialization
16/// ```rust, no_run
17/// # let stmt = "...";
18/// use hdbconnect::ToHana;
19/// use time::{macros::datetime, OffsetDateTime};
20/// # let connection = hdbconnect::Connection::new("...").unwrap();
21/// let ts: OffsetDateTime = datetime!(2012-02-02 02:02:02.200000000 +2);
22/// let response = connection.prepare_and_execute(stmt, &(ts.to_hana())).unwrap();
23/// ```
24///
25/// # Example for deserialization
26///
27/// Deserialize into `HanaOffsetDateTime`,
28/// then use `deref` or `to_inner()` to access the contained `OffsetDateTime`.
29///
30/// ```rust, no_run
31/// use hdbconnect::time::HanaOffsetDateTime;
32/// # let the_query = "...";
33/// # let mut connection = hdbconnect::Connection::new("...").unwrap();
34/// let dates: Vec<HanaOffsetDateTime> = connection.query(the_query).unwrap().try_into().unwrap();
35/// let year = (*dates[0]).year();
36/// ```
37#[derive(Debug)]
38pub struct HanaOffsetDateTime(OffsetDateTime);
39impl HanaOffsetDateTime {
40    /// Consumes the `HanaOffsetDateTime`, returning the wrapped `OffsetDateTime`.
41    #[must_use]
42    pub fn into_inner(self) -> OffsetDateTime {
43        self.0
44    }
45}
46impl std::ops::Deref for HanaOffsetDateTime {
47    type Target = OffsetDateTime;
48    fn deref(&self) -> &Self::Target {
49        &self.0
50    }
51}
52
53// ***********
54// deserialize
55// ***********
56impl<'de> serde::de::Deserialize<'de> for HanaOffsetDateTime {
57    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
58    where
59        D: serde::de::Deserializer<'de>,
60    {
61        deserializer.deserialize_str(HanaOffsetDateTimeVisitor)
62    }
63}
64
65struct HanaOffsetDateTimeVisitor;
66impl serde::de::Visitor<'_> for HanaOffsetDateTimeVisitor {
67    type Value = HanaOffsetDateTime;
68
69    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
70        HanaPrimitiveDateTimeVisitor.expecting(formatter)
71    }
72
73    fn visit_str<E>(self, value: &str) -> Result<HanaOffsetDateTime, E>
74    where
75        E: serde::de::Error,
76    {
77        Ok(HanaOffsetDateTime(
78            OffsetDateTime::now_utc()
79                .replace_date_time(HanaPrimitiveDateTimeVisitor.visit_str(value)?.into_inner()),
80        ))
81    }
82}
83
84/// Helper method for deserializing database values
85/// into values of type `time::OffsetDateTime`.
86///
87/// Since HANA's types [`LongDate`](crate::types::LongDate) and
88/// [`SecondDate`](crate::types::SecondDate) have no understanding of time zones,
89/// they deserialize only into `OffsetDateTime` values with zero offset
90/// (offset =
91/// [`time::UtcOffset::UTC`](https://docs.rs/time/latest/time/struct.UtcOffset.html#associatedconstant.UTC)).
92///
93/// # Example
94///
95/// Use serde's annotation `serde(deserialize_with = "..")` to refer to this method:
96///
97/// ```rust
98///     #[derive(serde::Deserialize)]
99///     struct WithTs {
100///         #[serde(deserialize_with = "hdbconnect::time::to_offset_date_time")]
101///         ts_o: time::OffsetDateTime,
102///     }
103/// ```
104///
105/// Unfortunately, the serde-annotation `deserialize_with` does not cover all cases,
106/// since it can only be applied to struct fields;
107/// it cannot be applied if you want to deserialize into a `Vec<OffsetDateTime>`
108/// or a plain `OffsetDateTime`.
109/// The best you can do then is deserialize instead into [`HanaOffsetDateTime`] and use
110/// `deref()` or `into_inner()` to access the contained `time::OffsetDateTime`.
111#[allow(clippy::missing_errors_doc)]
112pub fn to_offset_date_time<'de, D>(input: D) -> Result<OffsetDateTime, D::Error>
113where
114    D: serde::de::Deserializer<'de>,
115{
116    input
117        .deserialize_str(HanaOffsetDateTimeVisitor)
118        .map(HanaOffsetDateTime::into_inner)
119}
120
121//
122// serialize
123//
124
125impl ToHana<HanaOffsetDateTime> for OffsetDateTime {
126    fn to_hana(self) -> HanaOffsetDateTime {
127        HanaOffsetDateTime(self)
128    }
129}
130
131impl serde::ser::Serialize for HanaOffsetDateTime {
132    fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
133        const DATE_T_TIME: &[FormatItem<'static>] = format_description!(
134            "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:9]"
135        );
136
137        serializer.serialize_str(
138            &self
139                .0
140                .format(DATE_T_TIME)
141                .map_err(|_| S::Error::custom("failed formatting `OffsetDateTime`"))?,
142        )
143    }
144}