diff --git a/src/items/builder.rs b/src/items/builder.rs index 942c5ee..021c23a 100644 --- a/src/items/builder.rs +++ b/src/items/builder.rs @@ -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::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::(rel.try_into()?)?; + if desired_day != dt.day() { + dt = dt.checked_add((desired_day - dt.day()).days())?; + } + dt } - dt - } else { - dt.checked_add::(rel.try_into()?)? - }; + _ => dt.checked_add::(rel.try_into()?)?, + } } // 4e. Apply final fixed offset. diff --git a/tests/time.rs b/tests/time.rs index 2c18a7e..3718ce3 100644 --- a/tests/time.rs +++ b/tests/time.rs @@ -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::() + .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")]