aws_sdk_manager/utils/
rfc3339.rs1use std::{
2 io::{self, Error, ErrorKind},
3 time::SystemTime,
4};
5
6use chrono::{DateTime, NaiveDateTime, SecondsFormat, TimeZone, Utc};
7use serde::{self, Deserialize, Deserializer};
8
9pub mod serde_format {
11 use chrono::{DateTime, SecondsFormat, TimeZone, Utc};
12 use serde::{self, Deserialize, Deserializer, Serializer};
13
14 pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
15 where
16 S: Serializer,
17 {
18 serializer.serialize_str(&dt.to_rfc3339_opts(SecondsFormat::Millis, true))
20 }
21
22 pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
23 where
24 D: Deserializer<'de>,
25 {
26 let s = String::deserialize(deserializer)?;
27
28 match DateTime::parse_from_rfc3339(&s).map_err(serde::de::Error::custom) {
30 Ok(dt) => Ok(Utc.from_utc_datetime(&dt.naive_utc())),
31 Err(e) => Err(e),
32 }
33 }
34}
35
36fn fmt_date<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
37where
38 D: Deserializer<'de>,
39{
40 let s = String::deserialize(deserializer)?;
41 match DateTime::parse_from_rfc3339(&s).map_err(serde::de::Error::custom) {
43 Ok(dt) => Ok(Utc.from_utc_datetime(&dt.naive_utc())),
44 Err(e) => Err(e),
45 }
46}
47
48pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<DateTime<Utc>>, D::Error>
49where
50 D: Deserializer<'de>,
51{
52 #[derive(Deserialize)]
53 struct Wrapper(#[serde(deserialize_with = "fmt_date")] DateTime<Utc>);
54 let v = Option::deserialize(deserializer)?;
55 Ok(v.map(|Wrapper(a)| a))
56}
57
58pub fn now_str() -> io::Result<String> {
59 let now_unix = SystemTime::now()
60 .duration_since(SystemTime::UNIX_EPOCH)
61 .expect("unexpected None duration_since")
62 .as_secs();
63 let native_dt = NaiveDateTime::from_timestamp(now_unix as i64, 0);
64 let now_utc = DateTime::<Utc>::from_utc(native_dt, Utc);
65 Ok(now_utc.to_rfc3339_opts(SecondsFormat::Millis, true))
66}
67
68pub fn to_str(now_unix: u64) -> io::Result<String> {
69 let native_dt = NaiveDateTime::from_timestamp(now_unix as i64, 0);
70 let now_utc = DateTime::<Utc>::from_utc(native_dt, Utc);
71 Ok(now_utc.to_rfc3339_opts(SecondsFormat::Millis, true))
72}
73
74pub fn parse(s: &str) -> io::Result<DateTime<Utc>> {
75 match DateTime::parse_from_rfc3339(s) {
76 Ok(dt) => Ok(Utc.from_utc_datetime(&dt.naive_utc())),
77 Err(e) => {
78 return Err(Error::new(
79 ErrorKind::Other,
80 format!("failed to parse {} ({})", s, e),
81 ));
82 }
83 }
84}
85
86#[test]
88fn test_parse() {
89 let _ = env_logger::builder()
90 .filter_level(log::LevelFilter::Info)
91 .is_test(true)
92 .try_init();
93 use log::info;
94
95 let dt = Utc.ymd(2018, 1, 26).and_hms_micro(18, 30, 9, 453_000);
96 let s = "2018-01-26T18:30:09.453Z";
97 let parsed = parse(s).unwrap();
98 assert_eq!(dt, parsed);
99
100 let parsed = parse(&(now_str().unwrap())).unwrap();
101 info!("{:?}", parsed);
102}
103
104#[cfg(test)]
107mod tests {
108 use crate::utils::rfc3339;
109 use chrono::{TimeZone, Utc};
110
111 proptest::proptest! {
112 #[test]
113 fn rfc3339_parse(
114 y in 0i32..10000,
115 m in 1u32..13,
116 d in 1u32..28,
117 hr in 0u32..24,
118 min in 0u32..60,
119 sec in 0u32..60,
120 msec in 0u32..1000,
121 ) {
122 let dt = Utc.ymd(y, m, d).and_hms_micro(hr, min, sec, msec);
123 let s = format!("{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:06}Z", y, m, d, hr, min, sec, msec);
124 let parsed = rfc3339::parse(&s).unwrap();
125 assert_eq!(dt, parsed);
126 }
127 }
128}