///|
/// The calendar system a date is interpreted in.
///
/// This port implements the ISO 8601 calendar. The type exists as a distinct
/// value rather than being elided because calendar identity is observable
/// through parsing, `toString` annotations, and the cross-calendar mismatch
/// checks that arithmetic and comparison perform.
///
/// Reference:
pub(all) enum Calendar {
/// The proleptic Gregorian calendar, as ISO 8601 defines it.
ISO
} derive(Eq, Debug)
///|
pub impl Default for Calendar with fn default() {
ISO
}
///|
pub impl Show for Calendar with fn output(self, logger) {
logger.write_string(self.identifier())
}
///|
/// Returns the canonical calendar identifier.
pub fn Calendar::identifier(self : Calendar) -> String {
match self {
ISO => "iso8601"
}
}
///|
/// Parses a calendar identifier.
///
/// Identifiers are matched case-insensitively, as the Temporal specification
/// requires. Calendars other than `iso8601` are rejected: they are recognised
/// as valid Temporal calendars but are not implemented here.
///
/// ```mbt check
/// test {
/// inspect(@temporal.Calendar::of_string("ISO8601"), content="iso8601")
/// }
/// ```
pub fn Calendar::of_string(s : String) -> Calendar raise TemporalError {
match ascii_lowercase(s) {
"iso8601" => ISO
other =>
if is_known_calendar_identifier(other) {
raise RangeError(
"calendar '\{s}' is a valid Temporal calendar but is not implemented by this port",
)
} else {
raise RangeError("'\{s}' is not a recognized calendar identifier")
}
}
}
///|
/// Returns whether the identifier names a calendar the Temporal specification
/// recognises, whether or not this port implements it.
///
/// Distinguishing the two lets an unimplemented-but-valid calendar produce a
/// clearer message than an outright typo.
fn is_known_calendar_identifier(lowercase : String) -> Bool {
lowercase
is ("buddhist"
| "chinese"
| "coptic"
| "dangi"
| "ethioaa"
| "ethiopic"
| "ethiopic-amete-alem"
| "gregory"
| "hebrew"
| "indian"
| "islamic"
| "islamic-civil"
| "islamic-rgsa"
| "islamic-tbla"
| "islamic-umalqura"
| "islamicc"
| "japanese"
| "persian"
| "roc")
}
///|
/// Lowercases the ASCII letters in a string, leaving other characters alone.
fn ascii_lowercase(s : String) -> String {
let buf = StringBuilder::new()
for c in s {
if c >= 'A' && c <= 'Z' {
buf.write_char(Int::unsafe_to_char(c.to_int() + 32))
} else {
buf.write_char(c)
}
}
buf.to_string()
}