rfc_manager/
rfc_3339.rs

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/// RUST_LOG=debug cargo test --all-features --package rfc-manager --lib -- rfc_3339::test_parse --exact --show-output
37#[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/// RUST_LOG=debug cargo test --all-features --package rfc-manager --lib -- rfc_3339::tests::rfc_3339_parse_with_proptest --exact --show-output
55/// ref. https://altsysrq.github.io/proptest-book/proptest/getting-started.html
56#[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}