Skip to content

Commit

Permalink
Serialize duration to hour minute second, instead of just seconds (#50)
Browse files Browse the repository at this point in the history
Parse duration to hour minute second
  • Loading branch information
kakilangit committed Dec 13, 2023
1 parent ce08d32 commit 4ea725c
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 16 deletions.
30 changes: 25 additions & 5 deletions src/duration.rs
Expand Up @@ -70,12 +70,22 @@ impl fmt::Display for Duration {
}
}
if self.second != 0 || self.microsecond != 0 {
write!(f, "T{}", self.second)?;
if self.microsecond != 0 {
let s = format!("{:06}", self.microsecond);
write!(f, ".{}", s.trim_end_matches('0'))?;
let (hour, minute, sec) = self.to_hms();
write!(f, "T")?;
if hour != 0 {
write!(f, "{hour}H")?;
}
if minute != 0 {
write!(f, "{minute}M")?;
}
if sec != 0 || self.microsecond != 0 {
write!(f, "{sec}")?;
if self.microsecond != 0 {
let s = format!("{:06}", self.microsecond);
write!(f, ".{}", s.trim_end_matches('0'))?;
}
write!(f, "S")?;
}
write!(f, "S")?;
}
if self.second == 0 && self.microsecond == 0 && self.day == 0 {
write!(f, "T0S")?;
Expand All @@ -84,6 +94,16 @@ impl fmt::Display for Duration {
}
}

impl Duration {
fn to_hms(&self) -> (u32, u32, u32) {
let hours = self.second / 3600;
let minutes = (self.second % 3600) / 60;
let remaining_seconds = self.second % 60;

(hours, minutes, remaining_seconds)
}
}

impl PartialOrd for Duration {
/// Compare two durations by inequality.
///
Expand Down
56 changes: 45 additions & 11 deletions tests/main.rs
Expand Up @@ -858,7 +858,7 @@ fn test_ok_values_txt() {
let mut success = 0;
for (i, line) in contents.split('\n').enumerate() {
let line_no = i + 1;
if line.starts_with('#') || line.is_empty() {
if line.starts_with('#') || line.is_empty() || line == "\r" {
continue;
} else if line.starts_with("date:") {
let (input, expected_str) = extract_values(line, "date:");
Expand Down Expand Up @@ -1053,6 +1053,40 @@ fn duration_new_err() {
}
}

#[test]
fn duration_hours() {
let d = Duration::parse_str("PT5H45M").unwrap();
assert_eq!(
d,
Duration {
positive: true,
day: 0,
second: 20700,
microsecond: 0
}
);
assert_eq!(d.to_string(), "PT5H45M");
assert_eq!(d.signed_total_seconds(), (5 * 60 * 60) + (45 * 60));
assert_eq!(d.signed_microseconds(), 0);
}

#[test]
fn duration_minutes() {
let d = Duration::parse_str("PT30M").unwrap();
assert_eq!(
d,
Duration {
positive: true,
day: 0,
second: 1800,
microsecond: 0
}
);
assert_eq!(d.to_string(), "PT30M");
assert_eq!(d.signed_total_seconds(), 30 * 60);
assert_eq!(d.signed_microseconds(), 0);
}

param_tests! {
Duration,
duration_too_short1: err => "", TooShort;
Expand All @@ -1064,24 +1098,24 @@ param_tests! {
duration_1m: ok => "P1M", "P30D";
duration_1_5m: ok => "P1.5M", "P45D";
duration_1w: ok => "P1W", "P7D";
duration_1_1w: ok => "P1.1W", "P7DT60480S";
duration_1_123w: ok => "P1.123W", "P7DT74390.4S";
duration_1_1w: ok => "P1.1W", "P7DT16H48M";
duration_1_123w: ok => "P1.123W", "P7DT20H39M50.4S";
duration_simple_negative: ok => "-P1Y", "-P1Y";
duration_simple_positive: ok => "+P1Y", "P1Y";
duration_fraction1: ok => "PT0.555555S", "PT0.555555S";
duration_fraction2: ok => "P1Y1DT2H0.5S", "P1Y1DT7200.5S";
duration_fraction2: ok => "P1Y1DT2H0.5S", "P1Y1DT2H0.5S";
duration_1: ok => "P1DT1S", "P1DT1S";
duration_all: ok => "P1Y2M3DT4H5M6S", "P1Y63DT14706S";
duration_all: ok => "P1Y2M3DT4H5M6S", "P1Y63DT4H5M6S";
duration: err => "PD", DurationInvalidNumber;
duration: err => "P1DT1MT1S", DurationTRepeated;
duration: err => "P1DT1.1M1S", DurationInvalidFraction;
duration: err => "P1DT1X", DurationInvalidTimeUnit;
duration_invalid_day_unit1: err => "P1X", DurationInvalidDateUnit;
duration_invalid_day_unit2: err => "P1", DurationInvalidDateUnit;
duration_time_42s: ok => "00:00:42", "PT42S";
duration_time_1m: ok => "00:01", "PT60S";
duration_time_1h_2m_3s: ok => "01:02:03", "PT3723S";
duration_time_fraction: ok => "00:01:03.123", "PT63.123S";
duration_time_1m: ok => "00:01", "PT1M";
duration_time_1h_2m_3s: ok => "01:02:03", "PT1H2M3S";
duration_time_fraction: ok => "00:01:03.123", "PT1M3.123S";
duration_time_extra: err => "00:01:03.123x", ExtraCharacters;
duration_time_timezone: err => "00:01:03x", ExtraCharacters;
duration_time_invalid_hour: err => "24:01:03", OutOfRangeHour;
Expand Down Expand Up @@ -1109,7 +1143,7 @@ param_tests! {
duration_days_123days: ok => "123days", "P123D";
duration_days_time: ok => "1 day 00:00:42", "P1DT42S";
duration_days_time_neg: ok => "-1 day 00:00:42", "-P1DT42S";
duration_exceeds_day: ok => "PT86500S", "P1DT100S";
duration_exceeds_day: ok => "PT86500S", "P1DT1M40S";
duration_days_time_too_short: err => "1 day 00:", TooShort;
duration_days_time_wrong: err => "1 day 00:xx", InvalidCharMinute;
duration_days_time_extra: err => "1 day 00:00:00.123 ", InvalidCharTzSign;
Expand All @@ -1133,14 +1167,14 @@ fn duration_large() {
#[test]
fn duration_limit() {
let d = Duration::new(true, 999_999_999, 86399, 999_999).unwrap();
assert_eq!(d.to_string(), "P2739726Y9DT86399.999999S");
assert_eq!(d.to_string(), "P2739726Y9DT23H59M59.999999S");

match Duration::new(true, 999_999_999, 86399, 999_999 + 1) {
Ok(t) => panic!("unexpectedly valid -> {t:?}"),
Err(e) => assert_eq!(e, ParseError::DurationDaysTooLarge),
}
let d = Duration::new(false, 999_999_999, 86399, 999_999).unwrap();
assert_eq!(d.to_string(), "-P2739726Y9DT86399.999999S");
assert_eq!(d.to_string(), "-P2739726Y9DT23H59M59.999999S");

match Duration::new(false, 999_999_999, 86399, 999_999 + 1) {
Ok(t) => panic!("unexpectedly valid -> {t:?}"),
Expand Down

0 comments on commit 4ea725c

Please sign in to comment.