From Gregorian - jalalvandi/ParsiDate GitHub Wiki
from_gregorian
Static Method Converts a Gregorian calendar date (chrono::NaiveDate
) into its equivalent Persian (Solar Hijri / Jalali) calendar date (ParsiDate
).
Description
This static method takes a date represented in the standard Gregorian calendar system (using chrono::NaiveDate
) and calculates the corresponding year, month, and day in the Persian calendar system.
The conversion algorithm typically works by:
- Calculating the number of days that have elapsed between a known reference point (usually the start of the Persian epoch, which corresponds to Gregorian March 21st, 622 CE) and the input
gregorian_date
. - Mapping this total day count back into the structure of the Persian calendar (accounting for its specific month lengths and leap year rules) to determine the Persian year, month, and day.
This method ensures that the resulting ParsiDate
accurately reflects the same moment in time as the input gregorian_date
, just represented within a different calendar system.
Arguments
gregorian_date
: Achrono::NaiveDate
instance representing the Gregorian date that needs to be converted.
Returns
Ok(ParsiDate)
: If the conversion is successful and the resulting Persian date falls within the supported range (typically years 1-9999), returns the equivalentParsiDate
wrapped inOk
.Err(DateError::GregorianConversionError)
: If the conversion fails for any of the reasons listed below.
Errors
The method returns Err(DateError::GregorianConversionError)
under the following conditions:
- Date Too Early: The input
gregorian_date
is chronologically earlier than the starting point of the Persian calendar epoch (approximately March 21st, 622 CE). Dates before this point cannot be represented in the standard Persian calendar. - Year Out of Range: The calculation results in a Persian year that is outside the range supported by the
ParsiDate
type (typically less than 1 or greater than 9999). This can happen with Gregorian dates very far in the past (before 622 CE) or very far in the future. - Internal Calculation Failure: An unexpected error occurs during the internal date computations. This could involve issues with the underlying
chrono
library (e.g., failure to represent an intermediate date) or potential arithmetic overflows during the day count calculation, though this is less common for dates withinchrono::NaiveDate
's standard range.
Examples (Rust)
use chrono::NaiveDate;
use parsidate::{ParsiDate, DateError}; // Assuming these types exist
// --- Standard Conversions ---
// Convert a typical Gregorian date in 2024
let g_date_1 = NaiveDate::from_ymd_opt(2024, 7, 23).unwrap();
let expected_p_date_1 = ParsiDate::new(1403, 5, 2).unwrap(); // Mordad 2nd, 1403
assert_eq!(ParsiDate::from_gregorian(g_date_1), Ok(expected_p_date_1));
// Convert a Gregorian date just before Nowruz
let g_date_2 = NaiveDate::from_ymd_opt(2023, 3, 20).unwrap();
// This corresponds to the last day of 1401 (a common year)
let expected_p_date_2 = ParsiDate::new(1401, 12, 29).unwrap();
assert_eq!(ParsiDate::from_gregorian(g_date_2), Ok(expected_p_date_2));
// Convert the Gregorian date for Nowruz (Persian New Year)
let g_date_nowruz = NaiveDate::from_ymd_opt(2023, 3, 21).unwrap();
let expected_p_date_nowruz = ParsiDate::new(1402, 1, 1).unwrap(); // Farvardin 1st, 1402
assert_eq!(ParsiDate::from_gregorian(g_date_nowruz), Ok(expected_p_date_nowruz));
// --- Epoch Conversion ---
// Convert the approximate Gregorian start date of the Persian epoch
// Note: Precise epoch definitions can vary slightly, use the one consistent with the library's implementation.
// Assuming March 21, 622 CE corresponds to 1/1/1 Persian.
let epoch_gregorian = NaiveDate::from_ymd_opt(622, 3, 21).unwrap();
let expected_epoch_persian = ParsiDate::new(1, 1, 1).unwrap();
assert_eq!(ParsiDate::from_gregorian(epoch_gregorian), Ok(expected_epoch_persian));
// --- Error Cases ---
// Error: Gregorian date before the Persian epoch start
let before_epoch = NaiveDate::from_ymd_opt(622, 3, 20).unwrap();
assert_eq!(ParsiDate::from_gregorian(before_epoch), Err(DateError::GregorianConversionError));
let much_before_epoch = NaiveDate::from_ymd_opt(1, 1, 1).unwrap();
assert_eq!(ParsiDate::from_gregorian(much_before_epoch), Err(DateError::GregorianConversionError));
// Error: Gregorian date potentially too far in the future
// The exact behavior depends on NaiveDate::MAX and the ParsiDate year limit (e.g., 9999)
let far_future_g = NaiveDate::MAX; // Chrono's maximum representable date
match ParsiDate::from_gregorian(far_future_g) {
Ok(pd) => {
// This would only happen if NaiveDate::MAX corresponds to a Persian year <= 9999
println!("Conversion unexpectedly succeeded for NaiveDate::MAX: {}", pd);
assert!(pd.year() <= 9999); // Check if assumption holds
}
Err(e) => {
// This is the expected outcome if NaiveDate::MAX converts to a Persian year > 9999
println!("Correctly failed for NaiveDate::MAX: {:?}", e);
assert!(matches!(e, DateError::GregorianConversionError));
}
}