///|
fn calendar_function(
  ctx : QueryContext,
  name : String,
  part : String,
  date : @ast.Date,
) -> QueryValue {
  let day = ordinal(date)
  let weekday = (day - 1) % 7
  if name == "date_part" {
    match part {
      "year" => query_int(date.year)
      "month" => query_int(date.month)
      "quarter" => query_int((date.month - 1) / 3 + 1)
      "dow" => query_int(weekday)
      "isodow" => query_int(weekday + 1)
      "decade" => query_int(date.year / 10)
      "century" => query_int((date.year - 1) / 100 + 1)
      "millennium" => query_int((date.year - 1) / 1000 + 1)
      "epoch" =>
        QInt(
          @bigint.BigInt::from_int(day - 719163).mul(
            @bigint.BigInt::from_int(86400),
          ),
        )
      "week" => {
        let thursday = day + 3 - weekday
        let year = match ordinal_date(thursday) {
          QDate(d) => d.year
          _ => date.year
        }
        let jan4 = ordinal(@ast.Date::new(year, 1, 4).unwrap())
        query_int((day - (jan4 - (jan4 - 1) % 7)) / 7 + 1)
      }
      _ => QNull
    }
  } else {
    let (year, month, dom) = match part {
      "year" => (date.year, 1, 1)
      "month" => (date.year, date.month, 1)
      "quarter" => (date.year, (date.month - 1) / 3 * 3 + 1, 1)
      "decade" => (date.year / 10 * 10, 1, 1)
      "century" => ((date.year - 1) / 100 * 100 + 1, 1, 1)
      "millennium" => ((date.year - 1) / 1000 * 1000 + 1, 1, 1)
      "week" => return ordinal_date(day - weekday)
      _ => return QNull
    }
    match @ast.Date::new(year, month, dom) {
      Some(d) => QDate(d)
      None => {
        query_error(
          ctx, "query.oracle_type_error: ValueError date out of range",
        )
        QNull
      }
    }
  }
}