1use std::{
2 io::{self, Error, ErrorKind},
3 time::SystemTime,
4};
5
6use chrono::{DateTime, NaiveDateTime, SecondsFormat, TimeZone, Utc};
7
8pub fn now_str() -> io::Result<String> {
9 let now_unix = SystemTime::now()
10 .duration_since(SystemTime::UNIX_EPOCH)
11 .expect("unexpected None duration_since")
12 .as_secs();
13 let native_dt = NaiveDateTime::from_timestamp(now_unix as i64, 0);
14 let now_utc = DateTime::<Utc>::from_utc(native_dt, Utc);
15 Ok(now_utc.to_rfc3339_opts(SecondsFormat::Millis, true))
16}
17
18pub fn to_str(now_unix: u64) -> io::Result<String> {
19 let native_dt = NaiveDateTime::from_timestamp(now_unix as i64, 0);
20 let now_utc = DateTime::<Utc>::from_utc(native_dt, Utc);
21 Ok(now_utc.to_rfc3339_opts(SecondsFormat::Millis, true))
22}
23
24pub fn parse(s: &str) -> io::Result<DateTime<Utc>> {
25 match DateTime::parse_from_rfc3339(s) {
26 Ok(dt) => Ok(Utc.from_utc_datetime(&dt.naive_utc())),
27 Err(e) => {
28 return Err(Error::new(
29 ErrorKind::Other,
30 format!("failed to parse {} ({})", s, e),
31 ));
32 }
33 }
34}
35
36#[test]
38fn test_parse() {
39 let _ = env_logger::builder()
40 .filter_level(log::LevelFilter::Info)
41 .is_test(true)
42 .try_init();
43 use log::info;
44
45 let dt = Utc.ymd(2018, 1, 26).and_hms_micro(18, 30, 9, 453_000);
46 let s = "2018-01-26T18:30:09.453Z";
47 let parsed = parse(s).unwrap();
48 assert_eq!(dt, parsed);
49
50 let parsed = parse(&(now_str().unwrap())).unwrap();
51 info!("{:?}", parsed);
52}
53
54#[cfg(test)]
57mod tests {
58 use crate::rfc_3339;
59 use chrono::{TimeZone, Utc};
60
61 proptest::proptest! {
62 #[test]
63 fn rfc_3339_parse_with_proptest(
64 y in 0i32..10000,
65 m in 1u32..13,
66 d in 1u32..28,
67 hr in 0u32..24,
68 min in 0u32..60,
69 sec in 0u32..60,
70 msec in 0u32..1000,
71 ) {
72 let dt = Utc.ymd(y, m, d).and_hms_micro(hr, min, sec, msec);
73 let s = format!("{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:06}Z", y, m, d, hr, min, sec, msec);
74 let parsed = rfc_3339::parse(&s).unwrap();
75 assert_eq!(dt, parsed);
76 }
77 }
78}