parse_datetime: relative Year overflow for leap years

This commit is contained in:
cerdelen
2026-01-20 12:03:23 +01:00
committed by Sylvestre Ledru
parent dbfdea454f
commit 14bbd4e848
2 changed files with 28 additions and 15 deletions
+14 -13
View File
@@ -280,20 +280,21 @@ impl DateTimeBuilder {
// 4d. Apply relative adjustments.
for rel in self.relative {
let desired_day = dt.day();
dt = if let relative::Relative::Months(x) = rel {
// GNU way of calculating relative Months
// GNU changes the Month and then checks if the target Month has
// this day. If this day does not exist in the target month it overflows
// the difference
dt = dt.checked_add::<Span>(Span::new().try_months(x)?)?;
if desired_day != dt.day() {
dt = dt.checked_add((desired_day - dt.day()).days())?;
dt = match rel {
relative::Relative::Years(_) | relative::Relative::Months(_) => {
// GNU way of calculating relative months and years
// GNU changes the month and then checks if the target month has
// this day. If this day does not exist in the target month it overflows
// the difference
let desired_day = dt.day();
dt = dt.checked_add::<Span>(rel.try_into()?)?;
if desired_day != dt.day() {
dt = dt.checked_add((desired_day - dt.day()).days())?;
}
dt
}
dt
} else {
dt.checked_add::<Span>(rel.try_into()?)?
};
_ => dt.checked_add::<Span>(rel.try_into()?)?,
}
}
// 4e. Apply final fixed offset.
+14 -2
View File
@@ -158,8 +158,8 @@ fn test_time_months_ago(#[case] input: &str, #[case] expected: &str) {
}
#[rstest]
#[case::from_29_feb_in_1_month("2026-02-28 1 months", "2026-03-28")]
#[case::from_29_feb_1_month_ago("2026-02-28 1 months ago", "2026-01-28")]
#[case::from_28_feb_in_1_month("2026-02-28 1 months", "2026-03-28")]
#[case::from_28_feb_1_month_ago("2026-02-28 1 months ago", "2026-01-28")]
#[case::from_31_jan_in_1_month("2026-01-31 1 months", "2026-03-03")]
#[case::from_31_march_1_month_ago("2026-03-31 1 months ago", "2026-03-03")]
#[case::from_15_march_1_month_ago("2026-03-15 1 months ago", "2026-02-15")]
@@ -190,6 +190,18 @@ fn test_relative_month_time_dest_month_does_not_have_the_day(
check_time(input, expected, "%Y-%m-%d", Some(now));
}
#[rstest]
#[case::from_29_feb_in_1_year("2024-02-29 1 year", "2025-03-01")]
#[case::from_29_feb_1_year_ago("2024-02-29 1 year ago", "2023-03-01")]
fn test_relative_year_time_leap_year(#[case] input: &str, #[case] expected: &str) {
let now = "2026-01-12"
.parse::<DateTime>()
.unwrap()
.to_zoned(TimeZone::UTC)
.unwrap();
check_time(input, expected, "%Y-%m-%d", Some(now));
}
#[rstest]
#[case::from_29_feb_in_1_month("2024-02-29 1 months", "2024-03-29")]
#[case::from_29_feb_1_month_ago("2024-02-29 1 months ago", "2024-01-29")]