///|
pub struct Date {
  year : Int
  month : Int
  day : Int
} derive(Eq, Debug)

///|
pub fn Date::Date(year~ : Int, month~ : Int, day~ : Int) -> Date raise Failure {
  guard day >= 1 && day <= days_in_month(year, month) else {
    raise Failure("invalid date")
  }
  { year, month, day }
}

///|
fn days_in_month(year : Int, month : Int) -> Int {
  match month {
    1 | 3 | 5 | 7 | 8 | 10 | 12 => 31
    4 | 6 | 9 | 11 => 30
    2 if is_leap_year(year) => 29
    2 => 28
    _ => 0
  }
}

///|
fn is_leap_year(year : Int) -> Bool {
  year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)
}