
Date.pudu
Pudu64 lines2.5 KB
1/** @Domain.Date.Module — HTTP dates read as Unix milliseconds */2module PuduLangHttpClient.Domain.Date345const MONTHS: Map[Str, Int] = mapOf([6 ("jan", 1), ("feb", 2), ("mar", 3), ("apr", 4), ("may", 5), ("jun", 6),7 ("jul", 7), ("aug", 8), ("sep", 9), ("oct", 10), ("nov", 11), ("dec", 12)8 ])91011const DAY_MILLIS: Int = 86400000121314151617export fn parse(text: Str) -> Option[Int] {18 var day: Option[Int] = None19 var month: Option[Int] = None20 var year: Option[Int] = None21 var clock: Option[Int] = None22 for token in tokens(text) {23 if token.contains(":") {24 clock = timeOf(token)25 } else if let Some(found) = MONTHS.get(token.take(3).toLower()) {26 month = Some(found)27 } else if let Some(number) = token.toInt() {28 if token.length() <= 2 && day == None { day = Some(number) } else { year = Some(fullYear(number, token.length())) }29 }30 }31 let (d, m, y, t) = (day ?, month ?, year ?, clock ?)32 if d < 1 || d > 31 || y < 1601 { return None }33 Some(daysFromCivil(y, m, d) * DAY_MILLIS + t)34}353637fn tokens(text: Str) -> Array[Str] {38 text.replace(",", " ").replace("-", " ").split(" ").filter(|piece: Str| !piece.isEmpty())39}404142fn fullYear(number: Int, digits: Int) -> Int {43 if digits > 2 { number } else if number < 70 { number + 2000 } else { number + 1900 }44}454647fn timeOf(token: Str) -> Option[Int] {48 let parts = token.split(":")49 if parts.length() != 3 { return None }50 let (h, m, s) = (parts[0].toInt() ?, parts[1].toInt() ?, parts[2].toInt() ?)51 if h > 23 || m > 59 || s > 60 { None } else { Some(((h * 60 + m) * 60 + s) * 1000) }52}535455fn daysFromCivil(year: Int, month: Int, day: Int) -> Int {56 let shifted = if month <= 2 { year - 1 } else { year }57 let era = shifted / 40058 let yearOfEra = shifted - era * 40059 let monthFromMarch = if month > 2 { month - 3 } else { month + 9 }60 let dayOfYear = (153 * monthFromMarch + 2) / 5 + day - 161 let dayOfEra = yearOfEra * 365 + yearOfEra / 4 - yearOfEra / 100 + dayOfYear62 era * 146097 + dayOfEra - 71946863}64