Sub Months - jalalvandi/ParsiDate GitHub Wiki

Method sub_months

Subtracts a specified number of months from this ParsiDate, returning a new ParsiDate.

Description

This method provides a convenient way to calculate the date that occurs a given number of months before the current ParsiDate instance.

It functions as a direct equivalent to calling the add_months method with a negative value for the number of months (i.e., self.add_months(-months_to_sub) if add_months accepted negative values, or more likely, it uses the same internal logic adapted for subtraction).

The calculation involves adjusting the month and potentially the year components. A key behavior is day clamping: if the original day number is greater than the number of days in the resulting target month (after subtraction), the day is adjusted downwards to the last valid day of that target month.

Arguments

  • months_to_sub: The non-negative number of months (u32 or similar unsigned integer type) to subtract from the current date.

Returns

  • Ok(ParsiDate): If the subtraction results in a valid date within the supported range of the library (typically years 1 to 9999), returns the new ParsiDate instance wrapped in Ok.
  • Err(DateError::...): Returns an error under the same conditions as the add_months method (when performing the equivalent addition of a negative number). This primarily occurs if:
    • The resulting date falls outside the supported year range (e.g., subtracting months from an early date results in a year less than 1).
    • An arithmetic overflow or other internal error occurs during the calculation. Refer to add_months documentation for specific error kinds like DateError::ArithmeticOverflow or DateError::InvalidDate.

Day Clamping Behavior

When subtracting months, if the day of the month in the original date (self.day()) is greater than the number of days in the calculated target month, the day in the resulting ParsiDate will be "clamped" (adjusted down) to the last valid day of that target month.

  • Example 1: Subtracting 1 month from 1403-01-31 (Farvardin 31st). The target month is Esfand of the previous year (1402-12). If 1402 is a common year, Esfand has 29 days. Since 31 > 29, the day is clamped to 29, resulting in 1402-12-29.
  • Example 2: Subtracting 1 month from 1403-08-30 (Aban 30th). The target month is Mehr (1403-07), which also has 30 days. No clamping is needed, resulting in 1403-07-30.
  • Example 3: Subtracting 3 months from 1403-03-31 (Khordad 31st). Target month is Esfand (1402-12). If 1402 is common (29 days in Esfand), day 31 is clamped to 29 -> 1402-12-29.

Examples (Rust)

use parsidate::{ParsiDate, DateError}; // Assuming these types exist

// --- Example with Day Clamping and Year Rollover ---
let date1 = ParsiDate::new(1403, 3, 31).unwrap(); // Khordad 31st, 1403
// Subtract 3 months. Target is 1402-12 (Esfand).
// Assume 1402 is a common year (Esfand has 29 days).
// Day 31 is invalid for Esfand, so it clamps to 29.
assert_eq!(
    date1.sub_months(3),
    Ok(ParsiDate::new(1402, 12, 29).unwrap())
);

// --- Example with Year Rollover, No Clamping ---
let date2 = ParsiDate::new(1403, 2, 15).unwrap(); // Ordibehesht 15th, 1403
// Subtract 2 months. Target is 1402-12 (Esfand). Day 15 is valid.
assert_eq!(
    date2.sub_months(2),
    Ok(ParsiDate::new(1402, 12, 15).unwrap())
);

// --- Simple Subtraction, No Clamping, Same Year ---
let date3 = ParsiDate::new(1403, 7, 10).unwrap(); // Mehr 10th, 1403
// Subtract 2 months. Target is 1403-05 (Mordad). Day 10 is valid.
assert_eq!(
    date3.sub_months(2),
    Ok(ParsiDate::new(1403, 5, 10).unwrap())
);

// --- Subtracting 12 months (exactly one year) ---
let date4 = ParsiDate::new(1403, 5, 5).unwrap(); // Mordad 5th, 1403
assert_eq!(
    date4.sub_months(12),
    Ok(ParsiDate::new(1402, 5, 5).unwrap())
);

// --- Subtracting more than 12 months ---
let date5 = ParsiDate::new(1404, 1, 1).unwrap(); // Farvardin 1st, 1404
// Subtract 13 months -> Target is 1402-12 (Esfand). Day 1 is valid.
assert_eq!(
    date5.sub_months(13),
    Ok(ParsiDate::new(1402, 12, 1).unwrap())
);

// --- Example Resulting in Error (Going Below Year 1) ---
let early_date = ParsiDate::new(1, 2, 1).unwrap(); // Ordibehesht 1st, Year 1
// Subtracting 2 months results in Year 0, Month 12, which is outside the supported range.
let result = early_date.sub_months(2);
assert!(result.is_err());
// assert_eq!(result, Err(DateError::InvalidDate)); // Or similar error

let result_large = ParsiDate::new(1, 1, 1).unwrap().sub_months(1); // Subtracting from 1/1/1
assert!(result_large.is_err()); // Should also fail