///|
priv enum LocalKind {
  Iana(Location)
  Fixed(FixedOffset)
  Posix(PosixTz)
}

///|
/// The OS-configured local time zone, resolved from `$TZ` or, when unset,
/// `/etc/localtime`. Only available on `native`: resolving it requires file
/// I/O that `js`/`wasm`/`wasm-gc` have no host-provided access to.
pub struct Local {
  priv kind : LocalKind
}

///|
/// Resolves `tz_env` (the `TZ` environment variable) and `localtime_bytes`
/// (raw TZif bytes, e.g. from `/etc/localtime`) into a `Local`, following
/// the same precedence as the POSIX `TZ` convention: an empty `TZ` means
/// UTC, a non-empty `TZ` is looked up as an IANA zone name and, failing
/// that, parsed as a bare POSIX rule (e.g. `JST-9`); `None` if it is
/// neither, regardless of `localtime_bytes`. `localtime_bytes` is only
/// consulted when `TZ` is unset entirely.
fn Local::resolve(tz_env : String?, localtime_bytes : Bytes?) -> Local? {
  match tz_env {
    Some("") => Some(Local::{ kind: Fixed(FixedOffset::east(0).unwrap()), })
    Some(name) =>
      match Location::load(name) {
        Some(loc) => Some(Local::{ kind: Iana(loc), })
        None => parse_posix_tz(name).map(p => Local::{ kind: Posix(p), })
      }
    None =>
      localtime_bytes
      .bind(bytes => Location::from_tzif_bytes(bytes))
      .map(loc => Local::{ kind: Iana(loc), })
  }
}

///|
pub impl TimeZone for Local with fn offset_from_utc(self, utc) {
  match self.kind {
    Iana(loc) => loc.offset_from_utc(utc)
    Fixed(off) => off.offset_from_utc(utc)
    Posix(p) => p.offset_from_utc(utc)
  }
}

///|
pub impl TimeZone for Local with fn offset_from_local(self, naive_local) {
  match self.kind {
    Iana(loc) => loc.offset_from_local(naive_local)
    Fixed(off) => off.offset_from_local(naive_local)
    Posix(p) => p.offset_from_local(naive_local)
  }
}

///|
pub impl TimeZone for Local with fn zone_name(self, utc) {
  match self.kind {
    Iana(loc) => loc.zone_name(utc)
    Fixed(off) => off.zone_name(utc)
    Posix(p) => p.zone_name(utc)
  }
}

///|
pub impl TimeZone for Local with fn is_dst(self, utc) {
  match self.kind {
    Iana(loc) => loc.is_dst(utc)
    Fixed(off) => off.is_dst(utc)
    Posix(p) => p.is_dst(utc)
  }
}

///|
pub impl TimeZone for Local with fn transition_bounds(self, utc) {
  match self.kind {
    Iana(loc) => loc.transition_bounds(utc)
    Fixed(off) => off.transition_bounds(utc)
    Posix(p) => p.transition_bounds(utc)
  }
}

///|
pub impl TimeZone for Local with fn offset_from_abbreviation(
  self,
  abbreviation,
  near,
) {
  match self.kind {
    Iana(loc) => loc.offset_from_abbreviation(abbreviation, near)
    Fixed(off) => off.offset_from_abbreviation(abbreviation, near)
    Posix(p) => p.offset_from_abbreviation(abbreviation, near)
  }
}

///|
pub extend Local with TimeZone::{
  offset_from_utc,
  offset_from_local,
  zone_name,
  is_dst,
  transition_bounds,
  offset_from_abbreviation,
}