New Unchecked - parsicore/parsidate GitHub Wiki

Constructor (unsafe) ParsiDate::new_unchecked

Creates a new ParsiDate instance from year, month, and day components without performing any validation.

⚠️ Warning: Unsafe Operation ⚠️

This function is marked unsafe because it completely bypasses the validation checks normally performed by the safe ParsiDate::new constructor.

  • If you provide invalid components (e.g., month = 13, day = 32, day = 30 for Esfand in a common year, year = 0, year = 10000), this function will still create a ParsiDate instance containing that invalid data.
  • Subsequent operations on such an invalid ParsiDate instance (like formatting, comparison, arithmetic, or even simple getters if they rely on internal consistency) can lead to unpredictable behavior, incorrect results, logic errors, or runtime panics.

Do not use this function unless you meet the following conditions:

  1. You have already rigorously validated the year, month, and day components through external means before calling this function.
  2. You have an absolute performance requirement that necessitates skipping the validation overhead of ParsiDate::new.

In nearly all typical use cases, the safe ParsiDate::new constructor is the strongly preferred and recommended choice.

Safety Contract

The caller MUST guarantee that the provided year, month, and day combination:

  1. Represents a logically valid date within the rules of the Persian (Hejri-Shamsi) calendar. This includes:
    • month is between 1 and 12 (inclusive).
    • day is between 1 and the correct number of days for the given month and year (considering 31 days for months 1-6, 30 days for months 7-11, and 29 or 30 days for month 12 based on whether year is leap).
  2. The year is within the supported range of this library (e.g., [1, 9999]).

Failure to uphold this guarantee violates the safety contract and invokes undefined behavior from the perspective of this library's date logic, potentially leading to the issues mentioned in the warning above.

Arguments

  • year (i32 or similar integer type): The year component. Must be pre-validated by the caller.
  • month (u8 or similar integer type): The month component. Must be pre-validated by the caller.
  • day (u8 or similar integer type): The day component. Must be pre-validated by the caller.

Returns

  • A ParsiDate instance containing the provided year, month, and day, regardless of whether they form a valid date.

Examples (Rust)

use parsidate::{ParsiDate, DateError}; // Assuming ParsiDate struct and potentially helpers

// --- !!! Incorrect Usage Example !!! ---
// DO NOT DO THIS unless you are 100% certain of validity elsewhere.
// Creating an invalid date (Esfand 30 in a common year 1404)
// let invalid_date = unsafe { ParsiDate::new_unchecked(1404, 12, 30) };
// Even though ParsiDate::new(1404, 12, 30) would return Err,
// new_unchecked creates the object.
// assert!(!invalid_date.is_valid()); // Checking validity later confirms it's bad.
// Using invalid_date further could lead to panics or incorrect results.


// --- Correct Usage Example (with Prior Validation) ---

// Assume these components have been pre-validated by some robust external logic
let p_year = 1403; // A leap year
let p_month = 12; // Esfand
let p_day = 30;   // Valid day for Esfand in 1403


// Example of a hypothetical external validation function
// In reality, use a proper date validation library or ParsiDate::new itself
// if performance isn't the absolute bottleneck.
fn is_known_valid(y: i32, m: u8, d: u8) -> bool {
    // This is a simplified check; a real one needs ParsiDate::days_in_month logic
    // or use the result of ParsiDate::new(y, m, d).is_ok()
    const MIN_YEAR: i32 = 1;
    const MAX_YEAR: i32 = 9999; // Use the actual library constants if available
    if !(MIN_YEAR..=MAX_YEAR).contains(&y) || !(1..=12).contains(&m) || d < 1 {
       return false;
    }
    // A real check needs ParsiDate::days_in_month(y, m)
    let max_days = if m <= 6 { 31 } else if m <= 11 { 30 } else {
        if ParsiDate::is_persian_leap_year(y) { 30 } else { 29 }
    };
    d <= max_days
}


// Only call new_unchecked if the validation passed
if is_known_valid(p_year, p_month, p_day) {
    println!("Validation passed, proceeding with unsafe construction.");
    // It's now considered safe to use new_unchecked because the caller
    // has upheld the safety contract.
    let date = unsafe { ParsiDate::new_unchecked(p_year, p_month, p_day) };

    // Verify the components were stored
    assert_eq!(date.year(), 1403);
    assert_eq!(date.month(), 12);
    assert_eq!(date.day(), 30);
    assert!(date.is_valid()); // Should pass if is_known_valid is correct

} else {
    // If validation failed, DO NOT call new_unchecked.
    eprintln!("Validation failed for year={}, month={}, day={}. Cannot use new_unchecked.", p_year, p_month, p_day);
    // Handle the error appropriately (e.g., return Err, panic, log).
    // For demonstration, we just print. In real code, you'd likely return an error.
    // For instance: return Err(DateError::InvalidDate);
}

// Example with different valid data
let p_year2 = 1404; // Common year
let p_month2 = 7;  // Mehr
let p_day2 = 30;   // Last day of Mehr

if is_known_valid(p_year2, p_month2, p_day2) {
     let date2 = unsafe { ParsiDate::new_unchecked(p_year2, p_month2, p_day2) };
     assert_eq!(date2.year(), 1404);
     assert_eq!(date2.month(), 7);
     assert_eq!(date2.day(), 30);
     assert!(date2.is_valid());
} else {
     eprintln!("Validation failed for year={}, month={}, day={}.", p_year2, p_month2, p_day2);
}