Parse - parsicore/parsidate GitHub Wiki

Function ParsiDate::parse

Parses a string representation of a Persian date into a ParsiDate instance, guided by a provided format pattern.

Description

This function attempts to interpret the input string s according to the structure defined by the format string. The parsing process involves several key steps:

  1. Structural Matching: It requires an exact match between literal characters (like /, -, spaces, etc.) present in the format string and the corresponding characters in the input string s.
  2. Specifier Extraction: It identifies format specifiers (like %Y, %m) in the format string and expects to find corresponding date components in the input string s. The format of these components in s must match the specifier's requirements (e.g., %Y expects exactly 4 digits).
  3. Validation: After successfully extracting the numerical or textual year, month, and day values based on the specifiers, it validates these values by internally attempting to create a ParsiDate using logic equivalent to ParsiDate::new. This ensures the extracted components form a logically valid date within the Persian calendar (e.g., checking month ranges, day ranges considering leap years).

Supported Format Specifiers for Parsing

Only the following format specifiers are supported when parsing a date string:

  • %Y: Parses exactly 4 digits as the Persian year (e.g., "1403").
  • %m: Parses exactly 2 digits as the Persian month (e.g., "01", "05", "12").
  • %d: Parses exactly 2 digits as the Persian day (e.g., "01", "09", "30").
  • %B: Parses a full Persian month name. This is case-sensitive and must exactly match one of the standard Persian month names used by the library (e.g., "فروردین", "اردیبهشت", ..., "اسفند").
  • %%: Matches a literal percent sign (%) character in the input string s.

Unsupported Specifiers for Parsing

Specifiers that represent calculated values derived from a date, rather than constituting the date itself, are not supported for parsing. Using any of the following specifiers in the format string will result in a ParseErrorKind::UnsupportedSpecifier error:

  • %A: Weekday name (e.g., "شنبه") - Cannot be used to construct a date.
  • %w: Weekday number (0-6) - Cannot be used to construct a date.
  • %j: Ordinal day of the year (001-366) - Cannot be used to construct a date.
  • %K: Season name (e.g., "بهار") - Cannot be used to construct a date.
  • (Any other specifiers not listed in the "Supported" section)

Arguments

  • s: The input string slice (&str) containing the date representation to be parsed.
  • format: The format string slice (&str) describing the expected structure of s, including literals and supported format specifiers.

Returns

  • Ok(ParsiDate): If the input string s successfully matches the format string and the extracted components form a valid Persian date, returns the resulting ParsiDate instance wrapped in Ok.
  • Err(DateError::ParseError(kind)): If parsing fails at any stage, returns an error wrapped in Err. The kind field (ParseErrorKind) provides specific details about the failure:
    • ParseErrorKind::FormatMismatch: The structure of the input string s does not match the literal characters or overall pattern of the format string. This includes cases like incorrect separators, missing components, or extra unexpected characters at the end of s.
    • ParseErrorKind::InvalidNumber: A numeric component expected by a specifier (%Y, %m, %d) could not be parsed as a valid number, or it did not contain the exact required number of digits (4 for %Y, 2 for %m, 2 for %d).
    • ParseErrorKind::InvalidMonthName: The input string s did not contain a valid, recognized Persian month name at the position where a %B specifier was expected in the format.
    • ParseErrorKind::UnsupportedSpecifier: The format string contained one or more specifiers (like %A, %j, %K) that are not supported for parsing dates.
    • ParseErrorKind::InvalidDateValue: The year, month, and day values were successfully extracted from s according to the format, but they fail the final validation check (internal ParsiDate::new). This occurs for logically impossible dates, such as "1404/12/30" (since 1404 is not a leap year) or "1403/07/31" (since Mehr only has 30 days).

Examples (Rust)

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

// --- Success Cases ---

// Standard YYYY/MM/DD format
assert_eq!(
    ParsiDate::parse("1403/05/02", "%Y/%m/%d"),
    Ok(ParsiDate::new(1403, 5, 2).unwrap()) // 2nd Mordad 1403
);

// Standard YYYY-MM-DD format (leap day)
assert_eq!(
    ParsiDate::parse("1399-12-30", "%Y-%m-%d"),
    Ok(ParsiDate::new(1399, 12, 30).unwrap()) // 30th Esfand 1399 (leap)
);

// Format with full month name (%B)
assert_eq!(
    ParsiDate::parse("02 مرداد 1403", "%d %B %Y"),
    Ok(ParsiDate::new(1403, 5, 2).unwrap()) // 2nd Mordad 1403
);

// Format with literal %
assert_eq!(
    ParsiDate::parse("Year: 1403 % Month: 01 % Day: 01", "Year: %Y %% Month: %m %% Day: %d"),
    Ok(ParsiDate::new(1403, 1, 1).unwrap())
);


// --- Error Cases ---

// FormatMismatch: Input uses '-' but format expects '/'
assert_eq!(
    ParsiDate::parse("1403-05-02", "%Y/%m/%d"),
    Err(DateError::ParseError(ParseErrorKind::FormatMismatch))
);

// FormatMismatch: Input has extra trailing characters not in format
assert_eq!(
    ParsiDate::parse("1403/05/02 extra data", "%Y/%m/%d"),
    Err(DateError::ParseError(ParseErrorKind::FormatMismatch))
);

// InvalidNumber: Day '2' has only 1 digit, but %d expects 2 ('02')
assert_eq!(
    ParsiDate::parse("1403/05/2", "%Y/%m/%d"),
    Err(DateError::ParseError(ParseErrorKind::InvalidNumber)) // Or could be FormatMismatch depending on impl.
);

// InvalidNumber: Year 'abcd' is not numeric, but %Y expects digits
assert_eq!(
    ParsiDate::parse("abcd/05/02", "%Y/%m/%d"),
    Err(DateError::ParseError(ParseErrorKind::InvalidNumber))
);

// InvalidMonthName: "Mordd" is not a valid month name for %B
assert_eq!(
    ParsiDate::parse("02 Mordd 1403", "%d %B %Y"), // Assuming "مرداد" is the correct name
    Err(DateError::ParseError(ParseErrorKind::InvalidMonthName))
);

// InvalidDateValue: Date components parse ok, but 1404/12/30 is invalid (1404 not leap)
assert_eq!(
    ParsiDate::parse("1404/12/30", "%Y/%m/%d"),
    Err(DateError::ParseError(ParseErrorKind::InvalidDateValue))
);

// InvalidDateValue: Date components parse ok, but 1403/07/31 is invalid (Mehr has 30 days)
assert_eq!(
    ParsiDate::parse("1403/07/31", "%Y/%m/%d"),
    Err(DateError::ParseError(ParseErrorKind::InvalidDateValue))
);

// UnsupportedSpecifier: Format string uses %A (weekday name) which is not allowed for parsing
assert_eq!(
    ParsiDate::parse("Saturday 1403", "%A %Y"), // Hypothetical input
    Err(DateError::ParseError(ParseErrorKind::UnsupportedSpecifier))
);

// UnsupportedSpecifier: Format string uses %K (season name) which is not allowed for parsing
assert_eq!(
    ParsiDate::parse("Summer 1403", "%K %Y"), // Hypothetical input
    Err(DateError::ParseError(ParseErrorKind::UnsupportedSpecifier))
);

// UnsupportedSpecifier: Format string uses %j (ordinal day)
assert_eq!(
    ParsiDate::parse("Day 180 of 1403", "Day %j of %Y"), // Hypothetical input
    Err(DateError::ParseError(ParseErrorKind::UnsupportedSpecifier))
);