///|
fn month_days(year : Int, month : Int) -> Int {
if month == 2 {
if year % 400 == 0 || (year % 4 == 0 && year % 100 != 0) {
29
} else {
28
}
} else if [4, 6, 9, 11].contains(month) {
30
} else {
31
}
}
///|
fn year_start(year : Int) -> Int {
let previous = year - 1
365 * previous + previous / 4 - previous / 100 + previous / 400
}
///|
fn ordinal(text : String) -> Int raise FakeError {
let cs = text.to_array()
if cs.length() != 10 || cs[4] != '-' || cs[7] != '-' {
raise Invalid("ISO date YYYY-MM-DD required")
}
for i, c in cs {
if i != 4 && i != 7 && (c < '0' || c > '9') {
raise Invalid("date digits")
}
}
let fields = text.split("-").collect()
let year = @string.parse_int(fields[0]) catch { _ => raise Invalid("year") }
let month = @string.parse_int(fields[1]) catch { _ => raise Invalid("month") }
let day = @string.parse_int(fields[2]) catch { _ => raise Invalid("day") }
if year < 1 ||
year > 9999 ||
month < 1 ||
month > 12 ||
day < 1 ||
day > month_days(year, month) {
raise Invalid("invalid calendar date")
}
let mut days = year_start(year) + day - 1
for m in 1.. String {
let mut lo = 1
let mut hi = 10000
while lo + 1 < hi {
let mid = (lo + hi) / 2
if year_start(mid) <= days {
lo = mid
} else {
hi = mid
}
}
let mut day = days - year_start(lo)
let mut month = 1
while day >= month_days(lo, month) {
day -= month_days(lo, month)
month += 1
}
let mut year = lo.to_string()
while year.length() < 4 {
year = "0" + year
}
year + "-" + pad(month) + "-" + pad(day + 1)
}
///|
/// Samples Gregorian calendar days in the inclusive range.
pub fn Generator::date_between(
self : Generator,
first : String,
last : String,
) -> String raise FakeError {
from_ordinal(self.integer(ordinal(first), ordinal(last)))
}