///|
fn parse_fixed_digits(
text : String,
start : Int,
end : Int,
) -> Result[Int, DateError] {
let mut index = start
let mut value = 0
while index < end {
let code = text.at(index).to_int()
if code < 48 || code > 57 {
return Err(InvalidNumber)
}
value = value * 10 + code - 48
index = index + 1
}
Ok(value)
}
///|
pub fn Date::parse(text : String) -> Result[Date, DateError] {
if text.length() != 10 ||
text.at(4).to_int() != 45 ||
text.at(7).to_int() != 45 {
return Err(InvalidFormat)
}
let year = parse_fixed_digits(text, 0, 4)
let month = parse_fixed_digits(text, 5, 7)
let day = parse_fixed_digits(text, 8, 10)
match (year, month, day) {
(Ok(year), Ok(month), Ok(day)) => Date::new(year~, month~, day~)
_ => Err(InvalidNumber)
}
}
///|
fn days_in_month(year : Int, month : Int) -> Int {
match month {
2 => if is_leap_year(year) { 29 } else { 28 }
4 | 6 | 9 | 11 => 30
_ => 31
}
}
///|
fn is_leap_year(year : Int) -> Bool {
year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
}