Add Duration (ParsiDateTime) - jalalvandi/ParsiDate GitHub Wiki

Method add_duration (on ParsiDateTime)

Adds a chrono::Duration to this ParsiDateTime, returning a new ParsiDateTime.

Description

This method calculates a new ParsiDateTime by adding a specified chrono::Duration to the current instance. It handles both date and time adjustments, including rollovers across seconds, minutes, hours, and days, leveraging the robust arithmetic capabilities of the chrono library.

The operation follows these steps:

  1. Validates the starting ParsiDateTime instance.
  2. Converts the ParsiDateTime to its equivalent Gregorian chrono::NaiveDateTime.
  3. Adds the provided chrono::Duration to the chrono::NaiveDateTime.
  4. Converts the resulting chrono::NaiveDateTime back to a ParsiDateTime.

This conversion strategy ensures accurate handling of complex time arithmetic across calendar systems.

Arguments

  • duration: The chrono::Duration to add. This can be positive to move time forward or negative to move time backward. It can represent durations from nanoseconds up to days or weeks (though typically used for sub-day durations when combined with add_days).

Returns

  • Ok(ParsiDateTime): If the starting ParsiDateTime is valid, the conversions succeed, and the arithmetic results in a valid ParsiDateTime within the supported range, returns the new ParsiDateTime wrapped in Ok.
  • Err(DateError::...): Returns an error if any step fails:
    • DateError::InvalidDate or DateError::InvalidTime: If the starting ParsiDateTime instance (self) fails its internal validity check.
    • DateError::GregorianConversionError: If either the initial conversion to chrono::NaiveDateTime or the final conversion back to ParsiDateTime fails (e.g., due to dates outside supported epochs).
    • DateError::ArithmeticOverflow: If the addition of the duration results in a chrono::NaiveDateTime outside the range supported by chrono, or if the final converted ParsiDateTime falls outside the library's supported year range [1, 9999].

Examples (Rust)

use parsidate::{ParsiDateTime, ParsiDate, DateError};
use chrono::Duration; // Make sure chrono is a dependency

// Example 1: Adding seconds that cross midnight
let dt1 = ParsiDateTime::new(1403, 1, 1, 23, 59, 58).unwrap(); // Near end of Farvardin 1st
// Add 3 seconds
let dt_next_day = dt1.add_duration(Duration::seconds(3));
assert!(dt_next_day.is_ok());
let dt_next_day_unwrapped = dt_next_day.unwrap();
// Date should advance to Farvardin 2nd
assert_eq!(dt_next_day_unwrapped.date(), ParsiDate::new(1403, 1, 2).unwrap());
// Time should roll over to 00:00:01
assert_eq!(dt_next_day_unwrapped.hour(), 0);
assert_eq!(dt_next_day_unwrapped.minute(), 0);
assert_eq!(dt_next_day_unwrapped.second(), 1);

// Example 2: Adding hours that cross midnight and advance the day
let dt2 = ParsiDateTime::new(1403, 5, 10, 22, 30, 0).unwrap(); // Mordad 10th, 22:30:00
// Add 4 hours
let dt_plus_4h = dt2.add_duration(Duration::hours(4));
assert!(dt_plus_4h.is_ok());
let dt_plus_4h_unwrapped = dt_plus_4h.unwrap();
// Date should advance to Mordad 11th
assert_eq!(dt_plus_4h_unwrapped.date(), ParsiDate::new(1403, 5, 11).unwrap());
// Time should be 02:30:00 (22:30 + 4h = 26:30 -> 02:30 next day)
assert_eq!(dt_plus_4h_unwrapped.hour(), 2);
assert_eq!(dt_plus_4h_unwrapped.minute(), 30);
assert_eq!(dt_plus_4h_unwrapped.second(), 0);

// Example 3: Adding a larger duration (25 hours)
let dt3 = ParsiDateTime::new(1403, 1, 1, 23, 59, 58).unwrap(); // Use dt1 again for comparison
// Add 25 hours (1 day + 1 hour)
let dt_plus_25h = dt3.add_duration(Duration::hours(25));
assert!(dt_plus_25h.is_ok());
let dt_plus_25h_unwrapped = dt_plus_25h.unwrap();
// Date should advance by 1 day (to Farvardin 2nd) PLUS the day rollover from the time part. -> Farvardin 3rd
assert_eq!(dt_plus_25h_unwrapped.date(), ParsiDate::new(1403, 1, 3).unwrap());
// Time should be 23:59:58 + 1 hour -> 00:59:58
assert_eq!(dt_plus_25h_unwrapped.hour(), 0);
assert_eq!(dt_plus_25h_unwrapped.minute(), 59);
assert_eq!(dt_plus_25h_unwrapped.second(), 58);

// Example 4: Subtracting a duration
let dt4 = ParsiDateTime::new(1403, 1, 2, 0, 0, 5).unwrap(); // Farvardin 2nd, 00:00:05
// Subtract 10 seconds
let dt_minus_10s = dt4.add_duration(Duration::seconds(-10));
assert!(dt_minus_10s.is_ok());
let dt_minus_10s_unwrapped = dt_minus_10s.unwrap();
// Date should go back to Farvardin 1st
assert_eq!(dt_minus_10s_unwrapped.date(), ParsiDate::new(1403, 1, 1).unwrap());
// Time should be 23:59:55 (00:00:05 - 10s)
assert_eq!(dt_minus_10s_unwrapped.hour(), 23);
assert_eq!(dt_minus_10s_unwrapped.minute(), 59);
assert_eq!(dt_minus_10s_unwrapped.second(), 55);

// Example 5: Error case (potential) - subtracting large duration
let earliest_dt = ParsiDateTime::new(1, 1, 1, 0, 0, 0).unwrap();
let very_long_ago = Duration::days(-500 * 365); // Approx 500 years
let result = earliest_dt.add_duration(very_long_ago);
// This will likely fail due to going before the supported epoch
assert!(result.is_err());
// assert!(matches!(result, Err(DateError::GregorianConversionError) | Err(DateError::ArithmeticOverflow)));