Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-httpclient

Named HTTP clients for Pudu: a client factory, delegating handlers, pooled keep-alive connections, handler lifetimes, logging, and resilience

0.1.1Apache-2.01

InstallClose

Date.pudu

Pudu64 lines2.5 KB

GitHub ↗
1/** @Domain.Date.Module — HTTP dates read as Unix milliseconds */2module PuduLangHttpClient.Domain.Date34/// The month each three-letter name stands for.5const 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  ])910/// Milliseconds in one day.11const DAY_MILLIS: Int = 864000001213/// The instant an HTTP date names, in milliseconds since the Unix epoch, or `None`.14///15/// Reads the preferred `Sun, 06 Nov 1994 08:49:37 GMT`, the obsolete `Sunday, 06-Nov-94 08:49:3716/// GMT`, and the `Sun Nov  6 08:49:37 1994` forms; a two-digit year below 70 is in the 2000s.17export 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}3536/// The pieces of a date between spaces, commas, and dashes.37fn tokens(text: Str) -> Array[Str] {38  text.replace(",", " ").replace("-", " ").split(" ").filter(|piece: Str| !piece.isEmpty())39}4041/// A four-digit year from a written one, reading two digits as 1970 to 2069.42fn fullYear(number: Int, digits: Int) -> Int {43  if digits > 2 { number } else if number < 70 { number + 2000 } else { number + 1900 }44}4546/// The milliseconds since midnight an `hh:mm:ss` time names, or `None`.47fn 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}5354/// Days from 1970-01-01 to a date in the proleptic Gregorian calendar.55fn 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