Add Months - jalalvandi/ParsiDate GitHub Wiki

Method add_months

Adds a specified number of months to this ParsiDate, returning a new ParsiDate instance, handling day clamping when necessary.

Description

This method calculates a new date by adding (or subtracting) a given number of months to the current ParsiDate instance (self). The operation correctly adjusts the month and, if necessary, the year.

A key feature of this method is day clamping. If the day component of the original date (self.day()) is greater than the number of days in the resulting month (after adding months_to_add), the day of the new ParsiDate will be adjusted ("clamped") down to the last valid day of that target month. For example, adding one month to Farvardin 31st (which has 31 days) results in Ordibehesht 31st (which also has 31 days, so no clamping occurs). However, adding six months to Farvardin 31st targets Mehr (which has 30 days), so the result will be clamped to Mehr 30th. This logic also correctly handles the varying length of Esfand (29 or 30 days) in leap years.

The months_to_add argument can be positive to advance the date forward or negative to move the date backward in time.

Arguments

  • months_to_add: The number of months to add to the current date (i32 or a similar signed integer type).
    • A positive value moves the date forward.
    • A negative value moves the date backward.
    • A value of zero results in a ParsiDate identical to the original.

Returns

  • Ok(ParsiDate): If the starting ParsiDate is valid and the resulting date (after potential day clamping and year adjustment) falls within the supported range of ParsiDate (typically years 1-9999), returns the new ParsiDate wrapped in Ok.
  • Err(DateError::InvalidDate): If the starting ParsiDate instance (self) represents an invalid date according to Persian calendar rules.
  • Err(DateError::ArithmeticOverflow): If adding months_to_add results in a year that falls outside the supported range (e.g., year 0 or year 10000+), or if an internal overflow occurs during the month/year calculation.

Examples (Rust)

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

// --- Addition Examples ---

let date1 = ParsiDate::new(1403, 1, 31).unwrap(); // Farvardin 31st (Month 1 has 31 days)

// Add 1 month -> Ordibehesht 31st (Month 2 has 31 days, no clamping)
assert_eq!(date1.add_months(1), Ok(ParsiDate::new(1403, 2, 31).unwrap()));

// Add 6 months -> Mehr 30th (Month 7 has 30 days, day clamped from 31)
assert_eq!(date1.add_months(6), Ok(ParsiDate::new(1403, 7, 30).unwrap()));

// Add 8 months -> Azar 30th (Month 9 has 30 days, day clamped from 31)
assert_eq!(date1.add_months(8), Ok(ParsiDate::new(1403, 9, 30).unwrap()));

// Add 11 months -> Esfand 30th (1403 is a leap year, Month 12 has 30 days, day clamped from 31)
assert_eq!(date1.add_months(11), Ok(ParsiDate::new(1403, 12, 30).unwrap()));

// Add 12 months -> Farvardin 31st of next year (Year rollover)
assert_eq!(date1.add_months(12), Ok(ParsiDate::new(1404, 1, 31).unwrap()));

// Add 13 months -> Ordibehesht 31st of next year (1404 Month 2 has 31 days)
assert_eq!(date1.add_months(13), Ok(ParsiDate::new(1404, 2, 31).unwrap()));

// Add 23 months -> Esfand 29th of 1404 (1404 is NOT leap, Month 12 has 29 days, day clamped from 31)
assert_eq!(date1.add_months(23), Ok(ParsiDate::new(1404, 12, 29).unwrap()));

// Add zero months
assert_eq!(date1.add_months(0), Ok(ParsiDate::new(1403, 1, 31).unwrap()));


// --- Subtraction Examples ---

let date2 = ParsiDate::new(1403, 5, 31).unwrap(); // Mordad 31st (Month 5 has 31 days)

// Subtract 1 month -> Tir 31st (Month 4 has 31 days, no clamping)
assert_eq!(date2.add_months(-1), Ok(ParsiDate::new(1403, 4, 31).unwrap()));

// Subtract 5 months -> Esfand 30th, 1402 (1402 NOT leap, Month 12 has 29 days, day clamped from 31)
// Let's recheck the date math: 1403/05/31 - 5 months = 1402/12/31 -> Clamp to 1402/12/29
assert_eq!(date2.add_months(-5), Ok(ParsiDate::new(1402, 12, 29).unwrap()));

// Subtract 12 months -> Mordad 31st of previous year
assert_eq!(date2.add_months(-12), Ok(ParsiDate::new(1402, 5, 31).unwrap()));

let date3 = ParsiDate::new(1403, 1, 15).unwrap(); // Farvardin 15th

// Subtract 1 month -> Esfand 15th, 1402 (1402 NOT leap)
assert_eq!(date3.add_months(-1), Ok(ParsiDate::new(1402, 12, 15).unwrap()));

// --- Error Case: Resulting Year Out of Range ---
let early_date = ParsiDate::new(1, 1, 1).unwrap();

// Try to subtract months to go before the supported year 1
let result_too_early = early_date.add_months(-1);
assert!(result_too_early.is_err());
// The specific error might be ArithmeticOverflow or InvalidDate depending on implementation
// assert_eq!(result_too_early, Err(DateError::ArithmeticOverflow)); // Example assertion

let late_date = ParsiDate::new(9999, 12, 1).unwrap();
// Try to add months resulting in a year beyond the supported range (e.g., > 9999)
let result_too_late = late_date.add_months(1);
assert!(result_too_late.is_err());
// assert_eq!(result_too_late, Err(DateError::ArithmeticOverflow)); // Example assertion