ParsiDate: Comprehensive Persian Calendar for Rust
parsidate provides comprehensive functionality for working with the Persian (Jalali/Shamsi) calendar system in Rust. It allows for seamless conversion between Gregorian and Persian dates, validation, formatting, parsing, date arithmetic, and more, leveraging the chrono crate for some underlying operations.
✨ Features
- Conversion: Easily convert dates between
chrono::NaiveDate(Gregorian) andParsiDate. - Validation: Check if a year, month, and day combination forms a valid Persian date.
- Formatting: Display Persian dates in various predefined formats (
"short","long","iso") and using customstrftime-like patterns with Persian names. - Parsing: Parse strings into
ParsiDateobjects based on specified formats, including Persian month names. - Arithmetic: Add or subtract days, months, and years, correctly handling month lengths and leap years (including day clamping).
- Leap Year Calculation: Determine if a Persian year is leap (using a 33-year cycle approximation) or if a Gregorian year is leap.
- Date Information: Get the Persian weekday name (شنبه-جمعه), weekday number (0-6), and ordinal day of the year (1-366).
- Helpers: Get the first/last day of the month/year, or create modified dates easily (
with_year,with_month,with_day). - Current Date: Get the current system date as a
ParsiDate. - Serde Support: Optional serialization/deserialization via the
serdefeature flag. - Range: Supports Persian years from 1 to 9999.
⚙️ Installation
Add parsidate to your Cargo.toml:
[]
= "1.3.3"
If you need serialization/deserialization support, enable the serde feature:
[]
= { = "1.3.3", = ["serde"] }
= { = "1.0", = ["derive"] } # Required for derive
🚀 Usage Examples
use NaiveDate;
use ;
// --- Basic Creation & Accessors ---
// Create a ParsiDate (validates on creation)
let pd = new.unwrap; // 2 Mordad 1403
assert_eq!;
assert_eq!; // 5 = Mordad
assert_eq!;
// Check validity
assert!;
let invalid_date_res = new; // 1404 is not leap
assert_eq!;
// --- Conversion ---
// Gregorian to Persian
let g_date = from_ymd_opt.unwrap;
let pd_from_g = from_gregorian.unwrap;
assert_eq!;
// Persian to Gregorian
let g_date_conv = pd.to_gregorian.unwrap;
assert_eq!;
// --- Formatting ---
// Predefined formats
assert_eq!;
assert_eq!; // Day not padded in long format
assert_eq!;
// Default Display uses "short"
assert_eq!;
// Custom strftime-like format
assert_eq!;
assert_eq!; // Custom format (%d) pads day
assert_eq!;
// --- Parsing ---
// Requires exact match including padding and separators
let parsed_short = parse.unwrap;
assert_eq!;
// Parsing with Persian month name (%B) - requires padded day (%d)
let parsed_long = parse.unwrap;
assert_eq!;
let parsed_fail = parse; // Fails: %d requires '02'
assert!;
// --- Arithmetic ---
// Add/Subtract Days
let next_day = pd.add_days.unwrap;
assert_eq!;
let prev_day = pd.sub_days.unwrap; // Equivalent to add_days(-1)
assert_eq!;
let next_year_day = new.unwrap.add_days.unwrap; // Cross leap year end
assert_eq!;
// Add/Subtract Months (handles clamping)
let end_of_farvardin = new.unwrap;
let end_of_ordibehesht = end_of_farvardin.add_months.unwrap; // 31 -> 31 days
assert_eq!;
let end_of_mehr = end_of_farvardin.add_months.unwrap; // 31 -> 30 days (Mehr), clamps day
assert_eq!;
let start_of_mehr = new.unwrap;
let start_of_farvardin = start_of_mehr.sub_months.unwrap;
assert_eq!;
// Add/Subtract Years (handles leap day Esfand 30)
let leap_day = new.unwrap; // 1403 is leap
let next_year_clamped = leap_day.add_years.unwrap; // To 1404 (common), clamps day
assert_eq!;
let prev_year_clamped = leap_day.sub_years.unwrap; // To 1402 (common), clamps day
assert_eq!;
let leap_to_leap = leap_day.add_years.unwrap; // To 1407 (leap)
assert_eq!;
// --- Validation & Leap Year ---
assert!;
assert!;
assert_eq!; // Esfand in leap year
assert_eq!; // Esfand in common year
// --- Date Information ---
assert_eq!; // Tuesday
assert_eq!; // Saturday (Weekday 0)
assert_eq!; // Day number 126 in the year
// --- Helpers ---
assert_eq!;
assert_eq!; // Mordad has 31 days
assert_eq!; // Esfand common year
assert_eq!;
assert_eq!; // 1403 is leap
assert_eq!;
assert_eq!;
assert_eq!;
// --- Today ---
match today
serialization/deserialization support:
// --- Serde (Requires 'serde' feature) ---
Formatting and Parsing Specifiers
Formatting (format, format_strftime)
| Specifier | Description | Example (for 1403-05-02) |
|---|---|---|
%Y |
Year with century | 1403 |
%m |
Month as zero-padded number | 05 |
%d |
Day of month as zero-padded number | 02 |
%B |
Full Persian month name | مرداد |
%A |
Full Persian weekday name | سهشنبه |
%w |
Weekday as number (Saturday=0) | 3 |
%j |
Day of year as zero-padded number | 126 |
%% |
A literal % character |
% |
Parsing (parse)
| Specifier | Description | Notes |
|---|---|---|
%Y |
Parses a 4-digit year | Requires exactly 4 digits. |
%m |
Parses a 2-digit month (01-12) | Requires exactly 2 digits. |
%d |
Parses a 2-digit day (01-31) | Requires exactly 2 digits. |
%B |
Parses a full Persian month name | Case-sensitive, matches names like "فروردین", "مرداد". |
%% |
Matches a literal % character |
Note: Parsing requires the input string to exactly match the format string, including separators and the number of digits specified (e.g., %d requires 02, not 2). %A, %w, %j are not supported for parsing.
⚠️ Error Handling
Most methods that can fail (creation, conversion, parsing, arithmetic) return a Result<T, DateError>. The DateError enum indicates the type of error:
InvalidDate: Components do not form a valid Persian date.GregorianConversionError: Error during Gregorian <=> Persian conversion (e.g., out of range).ParseError(ParseErrorKind): Input string failed to parse according to the format.ParseErrorKindgives specific details.ArithmeticOverflow: Date arithmetic resulted in a date outside the supported range (1-9999) or internal overflow.InvalidOrdinal: Invalid day-of-year number provided (e.g., 0 or > 366).
Contributing
Contributions (bug reports, feature requests, pull requests) are welcome! Please open an issue to discuss significant changes first.
📄 License
Licensed under either of Apache License, Version 2.0.
128558ad-c066-4c4a-9b93-bca896bf4465