///|
fn DateParserContext::naive_dst(
  self : DateParserContext,
  value : DateTime,
  snapshot : ParserLocalSnapshot,
  shift~ : Int32 = 0,
) -> Bool raise DateError {
  let (_, dst, _) = self.info(
    locator_seconds(value) - Int64::from_int(snapshot.standard + shift),
  )
  dst
}

///|
fn DateParserContext::local_info(
  self : DateParserContext,
  value : DateTime,
  snapshot : ParserLocalSnapshot,
  fold : Bool,
) -> (Int32, String) raise DateError {
  if snapshot.standard == snapshot.daylight {
    return (snapshot.standard, snapshot.standard_name)
  }
  let naive = self.naive_dst(value, snapshot)
  let saved = snapshot.daylight - snapshot.standard
  let ambiguous = !naive &&
    naive != self.naive_dst(value, snapshot, shift=saved)
  let dst = if ambiguous { !fold } else { naive }
  if dst {
    (snapshot.daylight, snapshot.daylight_name)
  } else {
    (snapshot.standard, snapshot.standard_name)
  }
}

///|
fn parser_default_fold(fallback : DateTime) -> Bool raise DateError {
  match fallback.timezone {
    Some(zone) =>
      zone.resolve_arithmetic_local(fallback).offset_seconds !=
      fallback.offset_seconds
    None => false
  }
}

///|
fn parser_set_offset(
  value : DateTime,
  offset : Int32,
  name : String?,
) -> DateTime raise DateError {
  validate_offset_seconds(offset)
  {
    ..value,
    offset_seconds: offset,
    offset_minutes: offset / 60,
    abbreviation: name,
    timezone: None,
  }
}

///|
// date2num's first-aware array branch calls astimezone on later naive values.
// Unlike tzlocal, this uses the actual host rules, with reversed gap preference.
fn DateParserContext::interpret_naive(
  self : DateParserContext,
  value : DateTime,
  fold : Bool,
) -> DateTime raise DateError {
  let mut seconds = self.local_to_seconds(value, fold)
  let other = self.local_to_seconds(value, !fold)
  if other != seconds && (other > seconds) == fold {
    seconds = other
  }
  let (offset, _, _) = self.info(seconds)
  parser_set_offset(value, offset, None)
}

///|
// CPython's local() constructs a year-1..9999 calendar result for each probe.
// The fold search can fail at either endpoint even when the final UTC date fits.
fn DateParserContext::local_seconds(
  self : DateParserContext,
  seconds : Int64,
) -> Int64 raise DateError {
  let (offset, _, _) = self.info(seconds)
  let wall = seconds + Int64::from_int(offset)
  if wall < -62135596800L || wall >= 253402300800L {
    raise DateError::InvalidDate(
      message="host local datetime probe is outside years 1..9999",
    )
  }
  wall
}

///|
fn DateParserContext::local_to_seconds(
  self : DateParserContext,
  value : DateTime,
  fold : Bool,
) -> Int64 raise DateError {
  let target = locator_seconds(value)
  let first_offset = self.local_seconds(target) - target
  let first = target - first_offset
  let first_wall = self.local_seconds(first)
  let second_offset = if first_wall == target {
    let probe = first + (if fold { 86400L } else { -86400L })
    let offset = self.local_seconds(probe) - probe
    if first_offset == offset {
      return first
    }
    offset
  } else {
    first_wall - first
  }
  let second = target - second_offset
  let second_wall = self.local_seconds(second)
  if second_wall == target {
    return second
  }
  if first_wall == target {
    return first
  }
  if fold {
    Int64::min(first, second)
  } else {
    Int64::max(first, second)
  }
}