///|
/// Load caller-supplied IANA TZif v2/v3 bytes, retaining historical seconds,
/// designations and POSIX footer rules. The library does not search a system
/// database. TZif v1/v4, leap-second tables and offsets outside one day raise
/// `DateError`. With an empty footer, the final explicit type is retained.
fn date_tzif_data(data : Bytes) -> @tztypes.TzifData raise DateError {
  let parsed = match @tzif.load(data) {
    Ok(value) => value
    Err(_) =>
      raise DateError::InvalidDate(
        message="invalid or unsupported TZif v2/v3 data",
      )
  }
  if !parsed.leap_seconds.is_empty() {
    raise DateError::InvalidDate(
      message="TZif leap-second tables are unsupported for POSIX date ordinals",
    )
  }
  for time_type in parsed.time_types {
    validate_offset_seconds(time_type.utoff)
  }
  match parsed.posix_rule {
    Some(rule) => {
      validate_offset_seconds(rule.std_offset)
      match rule.dst_offset {
        Some(offset) => validate_offset_seconds(offset)
        None => ()
      }
    }
    None => ()
  }
  parsed
}

///|
/// Load caller-supplied TZif v2/v3 data into a transition-aware date zone.
pub fn tzif_timezone(data : Bytes) -> DateTimeZone raise DateError {
  let parsed = date_tzif_data(data)
  let mut initial_offset_seconds = parsed.time_types[0].utoff
  let mut initial_abbreviation = Some(parsed.time_types[0].abbr)
  let transitions : Array[TimeZoneTransition] = []
  for transition in parsed.transitions {
    let time_type = parsed.time_types[transition.type_index]
    // Discard out-of-calendar entries without losing the type active at year 1.
    if transition.utc_time < -62_135_596_800L {
      initial_offset_seconds = time_type.utoff
      initial_abbreviation = Some(time_type.abbr)
    } else if transition.utc_time < 253_402_300_800L {
      transitions.push({
        at_microseconds: transition.utc_time * 1_000_000L,
        offset_seconds: time_type.utoff,
        abbreviation: Some(time_type.abbr),
      })
    }
  }
  {
    initial_offset_seconds,
    initial_abbreviation,
    transitions,
    footer: parsed.posix_rule,
    dateutil_footer: false,
    footer_start_seconds: if parsed.transitions.is_empty() {
      None
    } else {
      Some(parsed.transitions[parsed.transitions.length() - 1].utc_time)
    },
  }
}