sui_graphql_rpc/types/
date_time.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::str::FromStr;
5
6use async_graphql::*;
7use chrono::{
8    prelude::{DateTime as ChronoDateTime, TimeZone, Utc as ChronoUtc},
9    ParseError as ChronoParseError,
10};
11
12use crate::error::Error;
13
14/// The DateTime in UTC format. The milliseconds part is optional,
15/// and it will be omitted if the ms value is zero.
16#[derive(Clone, Debug, Eq, PartialEq)]
17pub(crate) struct DateTime(ChronoDateTime<ChronoUtc>);
18
19impl DateTime {
20    pub fn from_ms(timestamp_ms: i64) -> Result<Self, Error> {
21        ChronoUtc
22            .timestamp_millis_opt(timestamp_ms)
23            .single()
24            .ok_or_else(|| Error::Internal("Cannot convert timestamp into DateTime".to_string()))
25            .map(Self)
26    }
27}
28
29/// The DateTime in UTC format. The milliseconds part is optional,
30/// and it will be omitted if the ms value is zero.
31#[Scalar(use_type_description = true)]
32impl ScalarType for DateTime {
33    fn parse(value: Value) -> InputValueResult<Self> {
34        match value {
35            Value::String(s) => DateTime::from_str(&s)
36                .map_err(|e| InputValueError::custom(format!("Error parsing DateTime: {}", e))),
37            _ => Err(InputValueError::expected_type(value)),
38        }
39    }
40
41    fn to_value(&self) -> Value {
42        // Debug format for chrono::DateTime is YYYY-MM-DDTHH:MM:SS.mmmZ
43        Value::String(format!("{:?}", self.0))
44    }
45}
46
47impl Description for DateTime {
48    fn description() -> &'static str {
49        "ISO-8601 Date and Time: RFC3339 in UTC with format: YYYY-MM-DDTHH:MM:SS.mmmZ. Note that the milliseconds part is optional, and it may be omitted if its value is 0."
50    }
51}
52
53impl FromStr for DateTime {
54    type Err = ChronoParseError;
55
56    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
57        Ok(DateTime(s.parse::<ChronoDateTime<ChronoUtc>>()?))
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    #[test]
66    fn test_parse() {
67        let dt: &str = "2023-08-19T15:37:24.761850Z";
68        let date_time = DateTime::from_str(dt).unwrap();
69        let Value::String(s) = async_graphql::ScalarType::to_value(&date_time) else {
70            panic!("Invalid date time scalar");
71        };
72        assert_eq!(dt, s);
73
74        let dt: &str = "2023-08-19T15:37:24.700Z";
75        let date_time = DateTime::from_str(dt).unwrap();
76        let Value::String(s) = async_graphql::ScalarType::to_value(&date_time) else {
77            panic!("Invalid date time scalar");
78        };
79        assert_eq!(dt, s);
80
81        let dt: &str = "2023-08-";
82        assert!(DateTime::from_str(dt).is_err());
83    }
84}