/** * @param LocalDateTime|null $dt * @return null|ZoneOffsetTransition|ZoneOffset * @throws IllegalArgumentException */ private function getOffsetInfo($dt) { if (empty($this->savingsInstantTransitions)) { return $this->standardOffsets[0]; } // check if using last rules if (!empty($this->lastRules) && $dt->isAfter($this->savingsLocalTransitions[count($this->savingsLocalTransitions) - 1])) { /** @var ZoneOffsetTransition[] $transArray */ $transArray = $this->findTransitionArray($dt->getYear()); $info = null; foreach ($transArray as $trans) { $info = $this->findOffsetInfo($dt, $trans); if ($info instanceof ZoneOffsetTransition || $info->equals($trans->getOffsetBefore())) { return $info; } } return $info; } // using historic rules $index = Math::binarySearch($this->savingsLocalTransitions, $dt); if ($index === -1) { // before first transition return $this->wallOffsets[0]; } if ($index < 0) { // switch negative insert position to start of matched range $index = -$index - 2; } else { if ($index < count($this->savingsLocalTransitions) - 1 && $this->savingsLocalTransitions[$index]->equals($this->savingsLocalTransitions[$index + 1])) { // handle overlap immediately following gap $index++; } } if (($index & 1) === 0) { // gap or overlap $dtBefore = $this->savingsLocalTransitions[$index]; $dtAfter = $this->savingsLocalTransitions[$index + 1]; $offsetBefore = $this->wallOffsets[(int) ($index / 2)]; $offsetAfter = $this->wallOffsets[(int) ($index / 2) + 1]; if ($offsetAfter->getTotalSeconds() > $offsetBefore->getTotalSeconds()) { // gap return ZoneOffsetTransition::of($dtBefore, $offsetBefore, $offsetAfter); } else { // overlap return ZoneOffsetTransition::of($dtAfter, $offsetBefore, $offsetAfter); } } else { // normal (neither gap or overlap) return $this->wallOffsets[(int) ($index / 2) + 1]; } }