diff --git a/src/items/builder.rs b/src/items/builder.rs index 7fd6826..37f3cb3 100644 --- a/src/items/builder.rs +++ b/src/items/builder.rs @@ -245,8 +245,10 @@ impl DateTimeBuilder { .ok()?; } - if let Some(offset) = &self.timezone { - dt = dt.datetime().to_zoned(offset.try_into().ok()?).ok()?; + if let Some(offset) = self.timezone { + let (offset, hour_adjustment) = offset.normalize(); + dt = dt.checked_add(Span::new().hours(hour_adjustment)).ok()?; + dt = dt.datetime().to_zoned((&offset).try_into().ok()?).ok()?; } Some(dt) diff --git a/src/items/timezone.rs b/src/items/timezone.rs index 2dcec8d..dcd0604 100644 --- a/src/items/timezone.rs +++ b/src/items/timezone.rs @@ -84,6 +84,30 @@ impl Offset { minutes, } } + + /// Normalize the offset so that the hour field is within the accepted range. + /// + /// - If the hour field is less than 24, or exactly 24 with a zero minute, + /// the offset is already normalized, and the function returns the offset + /// itself along with a zero hour adjustment. + /// - Otherwise, the hour field is reduced to 23 while preserving the minute + /// field, and the function returns the normalized offset along with the + /// hour adjustment needed to reach the original offset. + pub(super) fn normalize(self) -> (Offset, i8) { + if self.hours < 24 || (self.hours == 24 && self.minutes == 0) { + return (self, 0); + } + + let hour_adjustment = (self.hours as i8 - 23) * if self.negative { 1 } else { -1 }; + ( + Offset { + negative: self.negative, + hours: 23, + minutes: self.minutes, + }, + hour_adjustment, + ) + } } impl TryFrom<(bool, u8, u8)> for Offset { diff --git a/src/lib.rs b/src/lib.rs index c31565a..9205d0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -278,7 +278,7 @@ mod tests { #[test] fn offset_overflow() { - assert!(parse_datetime("m+14").is_err()); + assert!(parse_datetime("m+25").is_err()); assert!(parse_datetime("24:00").is_err()); } } diff --git a/tests/time.rs b/tests/time.rs index 16dfd8c..36bd956 100644 --- a/tests/time.rs +++ b/tests/time.rs @@ -90,12 +90,12 @@ fn test_time_correction(#[case] input: &str, #[case] expected: &str) { #[case::minus_12("12:34:56-12:00", "2022-06-11 00:34:56")] #[case::plus_1259("12:34:56+12:59", "2022-06-09 23:35:56")] #[case::minus_1259("12:34:56-12:59", "2022-06-11 01:33:56")] -/* TODO: https://github.com/uutils/parse_datetime/issues/149 #[case::plus_24("12:34:56+24:00", "2022-06-09 12:34:56")] #[case::minus_24("12:34:56-24:00", "2022-06-11 12:34:56")] #[case::plus_13("11:34:56+13:00", "2022-06-09 22:34:56")] #[case::minus_13("12:34:56-13:00", "2022-06-11 01:34:56")] -*/ +#[case::plus_36("12:34:56 m+24", "2022-06-09 00:34:56")] +#[case::minus_36("12:34:56 y-24:00", "2022-06-12 00:34:56")] fn test_time_correction_with_overflow(#[case] input: &str, #[case] expected: &str) { let now = "2022-06-10 00:00:00" .parse::()