To Gregorian - jalalvandi/ParsiDate GitHub Wiki

Method to_gregorian

Converts this Persian (Jalali) ParsiDate instance to its equivalent Gregorian date represented by chrono::NaiveDate.

Description

This method performs a conversion from the Persian (Hejri-Shamsi or Jalali) calendar system to the widely used Gregorian calendar system.

The process typically involves these steps:

  1. Validation: The method first checks if the current ParsiDate instance (self) represents a valid date using its internal is_valid() check. This guards against attempting conversion on potentially inconsistent data (e.g., created via unsafe ParsiDate::new_unchecked).
  2. Conversion Algorithm: If the ParsiDate is valid, it proceeds with the conversion. A common algorithm involves calculating the total number of days that have elapsed since the defined epoch start of the Persian calendar (usually considered Farvardin 1st of year 1, corresponding to March 21st, 622 CE in the Julian calendar, often adjusted slightly for Gregorian). This day count (or an equivalent like a Julian Day Number) is then used as a basis to calculate the corresponding year, month, and day in the Gregorian calendar, often leveraging functionalities provided by the chrono library itself.

The result is returned as a chrono::NaiveDate, which represents a Gregorian date without any timezone information.

Returns

  • Ok(chrono::NaiveDate): If the ParsiDate is valid and the conversion is successful, returns the equivalent Gregorian date wrapped in Ok.
  • Err(DateError::InvalidDate): If the ParsiDate instance (self) fails the initial is_valid() check (e.g., it contains month 0, day 32, or represents an impossible date like Esfand 30th in a common year, potentially created via unsafe).
  • Err(DateError::GregorianConversionError): If the conversion calculation itself encounters an error after the initial validation passes. This is generally rare for dates within the supported ParsiDate range but could theoretically occur due to:
    • Internal arithmetic overflows during the day count calculation (extremely unlikely with standard integer types for the supported year range).
    • The calculated Gregorian date falling outside the range supported by chrono::NaiveDate (also highly unlikely if ParsiDate's supported range is reasonable).

Examples (Rust)

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

// --- Successful Conversions ---

// Example 1: Convert a standard Persian date
let pd1 = ParsiDate::new(1403, 5, 2).unwrap(); // 2nd Mordad, 1403
// Expected Gregorian equivalent: July 23, 2024
let expected_g1 = NaiveDate::from_ymd_opt(2024, 7, 23).unwrap();
assert_eq!(pd1.to_gregorian(), Ok(expected_g1));

// Example 2: Convert the conventional start of the Persian epoch
let pd_epoch = ParsiDate::new(1, 1, 1).unwrap(); // 1st Farvardin, 1
// Expected Gregorian equivalent: March 21, 622 (Proleptic Gregorian)
// Note: The exact Gregorian date for the Hejri Shamsi epoch can vary slightly
// depending on the specific astronomical vs. algorithmic definition used.
// 622-03-21 is a common computational reference point.
let expected_epoch_gregorian = NaiveDate::from_ymd_opt(622, 3, 21).unwrap();
assert_eq!(pd_epoch.to_gregorian(), Ok(expected_epoch_gregorian));

// Example 3: Convert the last day of a Persian leap year
let pd_leap_end = ParsiDate::new(1403, 12, 30).unwrap(); // 30th Esfand, 1403 (leap year)
// Expected Gregorian equivalent: March 20, 2025
let expected_g_leap_end = NaiveDate::from_ymd_opt(2025, 3, 20).unwrap();
assert_eq!(pd_leap_end.to_gregorian(), Ok(expected_g_leap_end));

// Example 4: Convert the last day of a Persian common year
let pd_common_end = ParsiDate::new(1404, 12, 29).unwrap(); // 29th Esfand, 1404 (common year)
// Expected Gregorian equivalent: March 20, 2026
let expected_g_common_end = NaiveDate::from_ymd_opt(2026, 3, 20).unwrap();
assert_eq!(pd_common_end.to_gregorian(), Ok(expected_g_common_end));


// --- Error Case: Invalid Source Date ---

// Create an invalid ParsiDate (Esfand 30th in a common year 1404) unsafely
let invalid_pd = unsafe { ParsiDate::new_unchecked(1404, 12, 30) };
// Calling to_gregorian should detect the invalidity first
let result = invalid_pd.to_gregorian();
assert!(result.is_err());
assert_eq!(result, Err(DateError::InvalidDate));