From 042bad68f74c14bfc44e9821b20ad3b5466b6f39 Mon Sep 17 00:00:00 2001 From: chenren Date: Wed, 10 Apr 2024 15:10:41 +0800 Subject: [PATCH] impl Duration::from_std_duration and TryFrom --- xsd-types/src/types/duration.rs | 114 +++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 1 deletion(-) diff --git a/xsd-types/src/types/duration.rs b/xsd-types/src/types/duration.rs index 575807f7..0488bf60 100644 --- a/xsd-types/src/types/duration.rs +++ b/xsd-types/src/types/duration.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::fmt; use std::fmt::Write; use std::str::FromStr; @@ -30,7 +31,31 @@ impl Duration { } } - // TODO: Add from_std_duration + pub fn from_std_duration(other: std::time::Duration) -> Result { + const NANOS_PER_SEC: u128 = 1_000_000_000; + let nanos = other.as_nanos() - other.as_secs() as u128 * NANOS_PER_SEC; + + let mut rest_value = other.as_secs(); + let seconds = (rest_value % 60) as f64 + nanos as f64 / 1e9; + rest_value /= 60; + let minutes = rest_value % 60; + rest_value /= 60; + let hours = rest_value % 24; + rest_value /= 24; + let days = rest_value; + + if days > 28 { + return Err("Duration longer than 28 days is undefined".into()); + } + + Ok(Self { + days, + hours, + minutes, + seconds, + ..Default::default() + }) + } // TODO: Add a version of to_std_duration that takes a moment at time to start from. @@ -38,6 +63,20 @@ impl Duration { // converts months & years to days. } +impl TryFrom for Duration { + type Error = String; + fn try_from(value: std::time::Duration) -> Result { + Duration::from_std_duration(value) + } +} + +impl TryFrom for std::time::Duration { + type Error = String; + fn try_from(value: Duration) -> Result { + value.to_std_duration() + } +} + impl FromStr for Duration { type Err = String; @@ -320,4 +359,77 @@ mod tests { check_invalid("P"); check_invalid("PT15.S"); } + + fn check_std_duration_equal(a: std::time::Duration, b: std::time::Duration) { + if (a.as_secs_f64() - b.as_secs_f64()).abs() > 1e-4 { + panic!( + "assertion `left == right` failed + left: {:?} + right: {:?}", + a, b + ) + } + } + #[test] + fn convert_duration() { + // 1.0s + assert_eq!( + Duration::from_std_duration(std::time::Duration::new(1, 0)).unwrap(), + Duration::from_str("PT1S").unwrap() + ); + check_std_duration_equal( + Duration::from_str("PT1S") + .unwrap() + .to_std_duration() + .unwrap(), + std::time::Duration::new(1, 0), + ); + + // 1:40.55 + assert_eq!( + Duration::from_std_duration(std::time::Duration::new(100, 550_000_000)).unwrap(), + Duration::from_str("PT1M40.55S").unwrap() + ); + check_std_duration_equal( + Duration::from_str("PT1M40.55S") + .unwrap() + .to_std_duration() + .unwrap(), + std::time::Duration::new(100, 550_000_000), + ); + + // 1:05:30.456 + assert_eq!( + Duration::from_std_duration(std::time::Duration::new( + (1 * 60 + 5) * 60 + 30, + 456_000_000 + )) + .unwrap(), + Duration::from_str("PT1H5M30.456S").unwrap() + ); + check_std_duration_equal( + Duration::from_str("PT1H5M30.456S") + .unwrap() + .to_std_duration() + .unwrap(), + std::time::Duration::new((1 * 60 + 5) * 60 + 30, 456_000_000), + ); + + // 10day 1:05:30.456 + assert_eq!( + Duration::from_std_duration(std::time::Duration::new( + ((10 * 24 + 1) * 60 + 5) * 60 + 30, + 456_000_000 + )) + .unwrap(), + Duration::from_str("P10DT1H5M30.456S").unwrap() + ); + check_std_duration_equal( + Duration::from_str("P10DT1H5M30.456S") + .unwrap() + .to_std_duration() + .unwrap(), + std::time::Duration::new(((10 * 24 + 1) * 60 + 5) * 60 + 30, 456_000_000), + ); + } }