From Ordinal - jalalvandi/ParsiDate GitHub Wiki

Static Method from_ordinal

Creates a ParsiDate instance from a given Persian year and the ordinal day number within that year.

Description

This static method reconstructs a specific ParsiDate (year, month, day) based on the provided Persian year and the ordinal day count. The ordinal day represents the position of the day within the year, starting from 1 for Farvardin 1st, 2 for Farvardin 2nd, up to 365 for the last day of a common year or 366 for the last day of a leap year.

The method calculates the corresponding month and day by iteratively subtracting the lengths of the months from the ordinal value until the correct month and day are determined. It correctly accounts for the lengths of Persian months (31 days for months 1-6, 30 days for months 7-11) and the length of Esfand (month 12), which is 30 days in a leap year and 29 days in a common year.

The validity of the ordinal value is checked against the actual number of days in the provided year. The validity of the year itself is also implicitly checked during the final construction of the ParsiDate.

Arguments

  • year: The Persian year (e.g., 1403) as an integer type (e.g., i32). Must be within the range supported by ParsiDate (typically 1 to 9999).
  • ordinal: The day number within the specified year (e.g., 1, 100, 365, 366) as an integer type (e.g., u32). Must be between 1 and the total number of days in the given year (inclusive).

Returns

  • Ok(ParsiDate): If the year is valid and the ordinal day number is valid for that year, returns the corresponding ParsiDate (with calculated month and day) wrapped in Ok.
  • Err(DateError::InvalidOrdinal): If the ordinal value is less than 1 or greater than the total number of days in the specified year (365 or 366).
  • Err(DateError::InvalidDate): If the provided year is outside the valid range (e.g., 0 or 10000+). This error might be returned by the final internal validation when attempting to create the ParsiDate instance.

Examples (Rust)

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

// --- Successful Conversions ---

// First day of the year (ordinal 1)
assert_eq!(ParsiDate::from_ordinal(1403, 1), Ok(ParsiDate::new(1403, 1, 1).unwrap()));

// Day 32 (should be Ordibehesht 1st)
// Farvardin has 31 days. 32 - 31 = 1.
assert_eq!(ParsiDate::from_ordinal(1403, 32), Ok(ParsiDate::new(1403, 2, 1).unwrap()));

// A day mid-year (day 100)
// Month lengths: 31, 31, 31 (sum=93). 100 - 93 = 7. Day 100 is 7th of month 4 (Tir).
assert_eq!(ParsiDate::from_ordinal(1403, 100), Ok(ParsiDate::new(1403, 4, 7).unwrap()));

// Last day of a leap year (1403 is leap, 366 days)
assert_eq!(ParsiDate::from_ordinal(1403, 366), Ok(ParsiDate::new(1403, 12, 30).unwrap()));

// Last day of a common year (1404 is common, 365 days)
assert_eq!(ParsiDate::from_ordinal(1404, 365), Ok(ParsiDate::new(1404, 12, 29).unwrap()));


// --- Error Cases ---

// Error: Ordinal is zero (minimum is 1)
assert_eq!(ParsiDate::from_ordinal(1403, 0), Err(DateError::InvalidOrdinal));

// Error: Ordinal too large for a common year (1404 has 365 days)
assert_eq!(ParsiDate::from_ordinal(1404, 366), Err(DateError::InvalidOrdinal));

// Error: Ordinal too large for a leap year (1403 has 366 days)
assert_eq!(ParsiDate::from_ordinal(1403, 367), Err(DateError::InvalidOrdinal));

// Error: Invalid year (e.g., year 0)
// This might trigger InvalidDate during final ParsiDate::new validation
let result_invalid_year = ParsiDate::from_ordinal(0, 100);
assert!(result_invalid_year.is_err());
// The specific error could be InvalidDate or potentially InvalidOrdinal if year check happens first
// assert!(matches!(result_invalid_year, Err(DateError::InvalidDate))); // Example check

// Error: Invalid year (e.g., year 10000)
let result_invalid_year_high = ParsiDate::from_ordinal(10000, 100);
assert!(result_invalid_year_high.is_err());
// assert!(matches!(result_invalid_year_high, Err(DateError::InvalidDate))); // Example check