bool TimeZoneInfo::Load()

in absl/time/internal/cctz/src/time_zone_info.cc [392:577]


bool TimeZoneInfo::Load(ZoneInfoSource* zip) {
  // Read and validate the header.
  tzhead tzh;
  if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false;
  if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0)
    return false;
  Header hdr;
  if (!hdr.Build(tzh)) return false;
  std::size_t time_len = 4;
  if (tzh.tzh_version[0] != '\0') {
    // Skip the 4-byte data.
    if (zip->Skip(hdr.DataLength(time_len)) != 0) return false;
    // Read and validate the header for the 8-byte data.
    if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false;
    if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0)
      return false;
    if (tzh.tzh_version[0] == '\0') return false;
    if (!hdr.Build(tzh)) return false;
    time_len = 8;
  }
  if (hdr.typecnt == 0) return false;
  if (hdr.leapcnt != 0) {
    // This code assumes 60-second minutes so we do not want
    // the leap-second encoded zoneinfo. We could reverse the
    // compensation, but the "right" encoding is rarely used
    // so currently we simply reject such data.
    return false;
  }
  if (hdr.ttisstdcnt != 0 && hdr.ttisstdcnt != hdr.typecnt) return false;
  if (hdr.ttisutcnt != 0 && hdr.ttisutcnt != hdr.typecnt) return false;

  // Read the data into a local buffer.
  std::size_t len = hdr.DataLength(time_len);
  std::vector<char> tbuf(len);
  if (zip->Read(tbuf.data(), len) != len) return false;
  const char* bp = tbuf.data();

  // Decode and validate the transitions.
  transitions_.reserve(hdr.timecnt + 2);
  transitions_.resize(hdr.timecnt);
  for (std::size_t i = 0; i != hdr.timecnt; ++i) {
    transitions_[i].unix_time = (time_len == 4) ? Decode32(bp) : Decode64(bp);
    bp += time_len;
    if (i != 0) {
      // Check that the transitions are ordered by time (as zic guarantees).
      if (!Transition::ByUnixTime()(transitions_[i - 1], transitions_[i]))
        return false;  // out of order
    }
  }
  bool seen_type_0 = false;
  for (std::size_t i = 0; i != hdr.timecnt; ++i) {
    transitions_[i].type_index = Decode8(bp++);
    if (transitions_[i].type_index >= hdr.typecnt) return false;
    if (transitions_[i].type_index == 0) seen_type_0 = true;
  }

  // Decode and validate the transition types.
  transition_types_.reserve(hdr.typecnt + 2);
  transition_types_.resize(hdr.typecnt);
  for (std::size_t i = 0; i != hdr.typecnt; ++i) {
    transition_types_[i].utc_offset =
        static_cast<std::int_least32_t>(Decode32(bp));
    if (transition_types_[i].utc_offset >= kSecsPerDay ||
        transition_types_[i].utc_offset <= -kSecsPerDay)
      return false;
    bp += 4;
    transition_types_[i].is_dst = (Decode8(bp++) != 0);
    transition_types_[i].abbr_index = Decode8(bp++);
    if (transition_types_[i].abbr_index >= hdr.charcnt) return false;
  }

  // Determine the before-first-transition type.
  default_transition_type_ = 0;
  if (seen_type_0 && hdr.timecnt != 0) {
    std::uint_fast8_t index = 0;
    if (transition_types_[0].is_dst) {
      index = transitions_[0].type_index;
      while (index != 0 && transition_types_[index].is_dst) --index;
    }
    while (index != hdr.typecnt && transition_types_[index].is_dst) ++index;
    if (index != hdr.typecnt) default_transition_type_ = index;
  }

  // Copy all the abbreviations.
  abbreviations_.reserve(hdr.charcnt + 10);
  abbreviations_.assign(bp, hdr.charcnt);
  bp += hdr.charcnt;

  // Skip the unused portions. We've already dispensed with leap-second
  // encoded zoneinfo. The ttisstd/ttisgmt indicators only apply when
  // interpreting a POSIX spec that does not include start/end rules, and
  // that isn't the case here (see "zic -p").
  bp += (8 + 4) * hdr.leapcnt;  // leap-time + TAI-UTC
  bp += 1 * hdr.ttisstdcnt;     // UTC/local indicators
  bp += 1 * hdr.ttisutcnt;      // standard/wall indicators
  assert(bp == tbuf.data() + tbuf.size());

  future_spec_.clear();
  if (tzh.tzh_version[0] != '\0') {
    // Snarf up the NL-enclosed future POSIX spec. Note
    // that version '3' files utilize an extended format.
    auto get_char = [](ZoneInfoSource* azip) -> int {
      unsigned char ch;  // all non-EOF results are positive
      return (azip->Read(&ch, 1) == 1) ? ch : EOF;
    };
    if (get_char(zip) != '\n') return false;
    for (int c = get_char(zip); c != '\n'; c = get_char(zip)) {
      if (c == EOF) return false;
      future_spec_.push_back(static_cast<char>(c));
    }
  }

  // We don't check for EOF so that we're forwards compatible.

  // If we did not find version information during the standard loading
  // process (as of tzh_version '3' that is unsupported), then ask the
  // ZoneInfoSource for any out-of-bound version string it may be privy to.
  if (version_.empty()) {
    version_ = zip->Version();
  }

  // Trim redundant transitions. zic may have added these to work around
  // differences between the glibc and reference implementations (see
  // zic.c:dontmerge) and the Qt library (see zic.c:WORK_AROUND_QTBUG_53071).
  // For us, they just get in the way when we do future_spec_ extension.
  while (hdr.timecnt > 1) {
    if (!EquivTransitions(transitions_[hdr.timecnt - 1].type_index,
                          transitions_[hdr.timecnt - 2].type_index)) {
      break;
    }
    hdr.timecnt -= 1;
  }
  transitions_.resize(hdr.timecnt);

  // Ensure that there is always a transition in the first half of the
  // time line (the second half is handled below) so that the signed
  // difference between a civil_second and the civil_second of its
  // previous transition is always representable, without overflow.
  if (transitions_.empty() || transitions_.front().unix_time >= 0) {
    Transition& tr(*transitions_.emplace(transitions_.begin()));
    tr.unix_time = -(1LL << 59);  // -18267312070-10-26T17:01:52+00:00
    tr.type_index = default_transition_type_;
  }

  // Extend the transitions using the future specification.
  if (!ExtendTransitions()) return false;

  // Ensure that there is always a transition in the second half of the
  // time line (the first half is handled above) so that the signed
  // difference between a civil_second and the civil_second of its
  // previous transition is always representable, without overflow.
  const Transition& last(transitions_.back());
  if (last.unix_time < 0) {
    const std::uint_fast8_t type_index = last.type_index;
    Transition& tr(*transitions_.emplace(transitions_.end()));
    tr.unix_time = 2147483647;  // 2038-01-19T03:14:07+00:00
    tr.type_index = type_index;
  }

  // Compute the local civil time for each transition and the preceding
  // second. These will be used for reverse conversions in MakeTime().
  const TransitionType* ttp = &transition_types_[default_transition_type_];
  for (std::size_t i = 0; i != transitions_.size(); ++i) {
    Transition& tr(transitions_[i]);
    tr.prev_civil_sec = LocalTime(tr.unix_time, *ttp).cs - 1;
    ttp = &transition_types_[tr.type_index];
    tr.civil_sec = LocalTime(tr.unix_time, *ttp).cs;
    if (i != 0) {
      // Check that the transitions are ordered by civil time. Essentially
      // this means that an offset change cannot cross another such change.
      // No one does this in practice, and we depend on it in MakeTime().
      if (!Transition::ByCivilTime()(transitions_[i - 1], tr))
        return false;  // out of order
    }
  }

  // Compute the maximum/minimum civil times that can be converted to a
  // time_point<seconds> for each of the zone's transition types.
  for (auto& tt : transition_types_) {
    tt.civil_max = LocalTime(seconds::max().count(), tt).cs;
    tt.civil_min = LocalTime(seconds::min().count(), tt).cs;
  }

  transitions_.shrink_to_fit();
  return true;
}