async_graphql/types/external/
time_offset_date_time.rs

1use time::{OffsetDateTime, UtcOffset, format_description::well_known::Rfc3339};
2
3use crate::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
4
5/// A datetime with timezone offset.
6///
7/// The input is a string in RFC3339 format, e.g. "2022-01-12T04:00:19.12345Z"
8/// or "2022-01-12T04:00:19+03:00". The output is also a string in RFC3339
9/// format, but it is always normalized to the UTC (Z) offset, e.g.
10/// "2022-01-12T04:00:19.12345Z".
11#[Scalar(
12    internal,
13    name = "DateTime",
14    specified_by_url = "https://datatracker.ietf.org/doc/html/rfc3339"
15)]
16impl ScalarType for OffsetDateTime {
17    fn parse(value: Value) -> InputValueResult<Self> {
18        match &value {
19            Value::String(s) => Ok(Self::parse(s, &Rfc3339)?),
20            _ => Err(InputValueError::expected_type(value)),
21        }
22    }
23
24    fn to_value(&self) -> Value {
25        Value::String(
26            self.to_offset(UtcOffset::UTC)
27                .format(&Rfc3339)
28                .unwrap_or_else(|e| panic!("Failed to format `OffsetDateTime`: {}", e)),
29        )
30    }
31}
32
33#[cfg(test)]
34mod tests {
35    use time::{OffsetDateTime, macros::datetime};
36
37    use crate::{ScalarType, Value};
38
39    #[test]
40    fn test_offset_date_time_to_value() {
41        let cases = [
42            (
43                datetime!(2022-01-12 07:30:19.12345 +3:30),
44                "2022-01-12T04:00:19.12345Z",
45            ),
46            (datetime!(2022-01-12 07:30:19-0), "2022-01-12T07:30:19Z"),
47        ];
48        for (value, expected) in cases {
49            let value = value.to_value();
50
51            if let Value::String(s) = value {
52                assert_eq!(s, expected);
53            } else {
54                panic!(
55                    "Unexpected Value type when formatting OffsetDateTime: {:?}",
56                    value
57                );
58            }
59        }
60    }
61
62    #[test]
63    fn test_offset_date_time_parse() {
64        let cases = [
65            (
66                "2022-01-12T04:00:19.12345Z",
67                datetime!(2022-01-12 07:30:19.12345 +3:30),
68            ),
69            (
70                "2022-01-12T23:22:19.12345-00:00",
71                datetime!(2022-01-12 23:22:19.12345-0),
72            ),
73        ];
74        for (value, expected) in cases {
75            let value = Value::String(value.to_string());
76            let parsed = <OffsetDateTime as ScalarType>::parse(value).unwrap();
77            assert_eq!(parsed, expected);
78        }
79    }
80}