///|
/// `x-amz-date` formatting.
///
/// Kept here, pure and target-agnostic, so the format is testable and only
/// the clock read itself has to be provided per target.

///|
fn pad2(n : Int) -> String {
  if n < 10 {
    "0" + n.to_string()
  } else {
    n.to_string()
  }
}

///|
fn pad4(n : Int) -> String {
  if n < 10 {
    "000" + n.to_string()
  } else if n < 100 {
    "00" + n.to_string()
  } else if n < 1000 {
    "0" + n.to_string()
  } else {
    n.to_string()
  }
}

///|
/// Civil date from days since the Unix epoch (Howard Hinnant's algorithm).
///
/// Valid for the whole proleptic Gregorian range, so a clock that is badly
/// wrong produces a wrong date rather than nonsense.
pub fn civil_from_days(days : Int64) -> (Int, Int, Int) {
  let z = days + 719468L
  let era = (if z >= 0L { z } else { z - 146096L }) / 146097L
  let doe = z - era * 146097L
  let yoe = (doe - doe / 1460L + doe / 36524L - doe / 146096L) / 365L
  let y = yoe + era * 400L
  let doy = doe - (365L * yoe + yoe / 4L - yoe / 100L)
  let mp = (5L * doy + 2L) / 153L
  let d = doy - (153L * mp + 2L) / 5L + 1L
  let m = mp + (if mp < 10L { 3L } else { -9L })
  let year = if m <= 2L { y + 1L } else { y }
  (year.to_int(), m.to_int(), d.to_int())
}

///|
/// Format Unix epoch seconds as SigV4's `YYYYMMDDTHHMMSSZ`, in UTC.
pub fn format_amz_date(epoch_seconds : Int64) -> String {
  // Floor division, so times before 1970 do not round toward zero and land
  // on the wrong day.
  let mut days = epoch_seconds / 86400L
  let mut rem = epoch_seconds % 86400L
  if rem < 0L {
    rem += 86400L
    days -= 1L
  }
  let (year, month, day) = civil_from_days(days)
  let hour = (rem / 3600L).to_int()
  let minute = ((rem % 3600L) / 60L).to_int()
  let second = (rem % 60L).to_int()
  "\{pad4(year)}\{pad2(month)}\{pad2(day)}T\{pad2(hour)}\{pad2(minute)}\{pad2(second)}Z"
}