Esempio n. 1
0
 /**
  * Completes the build converting the builder to a set of time-zone rules.
  * <p>
  * Calling this method alters the state of the builder.
  * Further rules should not be added to this builder once this method is called.
  *
  * @param string $zoneId the time-zone ID, not null
  * @param array $deduplicateMap a map for deduplicating the values, not null
  * @return ZoneRules the zone rules, not null
  * @throws \LogicException if no windows have been added
  * @throws \LogicException if there is only one rule defined as being forever for any given window
  */
 public function _toRules($zoneId, &$deduplicateMap)
 {
     $this->deduplicateMap = $deduplicateMap;
     if (empty($this->windowList)) {
         throw new \LogicException("No windows have been added to the builder");
     }
     /** @var ZoneOffsetTransition[] $standardTransitionList */
     $standardTransitionList = [];
     /** @var ZoneOffsetTransition[] */
     $transitionList = [];
     /** @var ZoneOffsetTransitionRule[] */
     $lastTransitionRuleList = [];
     // initialize the standard offset calculation
     $firstWindow = $this->windowList[0];
     $loopStandardOffset = $firstWindow->standardOffset;
     $loopSavings = 0;
     if ($firstWindow->fixedSavingAmountSecs !== null) {
         $loopSavings = $firstWindow->fixedSavingAmountSecs;
     }
     /** @var ZoneOffset $firstWallOffset */
     $firstWallOffset = $this->deduplicate(ZoneOffset::ofTotalSeconds($loopStandardOffset->getTotalSeconds() + $loopSavings));
     /** @var LocalDateTime $loopWindowStart */
     $loopWindowStart = $this->deduplicate(LocalDateTime::of(Year::MIN_VALUE, 1, 1, 0, 0));
     $loopWindowOffset = $firstWallOffset;
     // build the windows and rules to interesting data
     foreach ($this->windowList as $window) {
         // tidy the state
         $window->tidy($loopWindowStart->getYear());
         // calculate effective savings at the start of the window
         $effectiveSavings = $window->fixedSavingAmountSecs;
         if ($effectiveSavings === null) {
             // apply rules from this window together with the standard offset and
             // savings from the last window to find the savings amount applicable
             // at start of this window
             $effectiveSavings = 0;
             foreach ($window->ruleList as $rule) {
                 $trans = $rule->toTransition($loopStandardOffset, $loopSavings);
                 if ($trans->toEpochSecond() > $loopWindowStart->toEpochSecond($loopWindowOffset)) {
                     // previous savings amount found, which could be the savings amount at
                     // the instant that the window starts (hence isAfter)
                     break;
                 }
                 $effectiveSavings = $rule->savingAmountSecs;
             }
         }
         // check if standard offset changed, and update it
         if ($loopStandardOffset->equals($window->standardOffset) === false) {
             $standardTransitionList[] = $this->deduplicate(ZoneOffsetTransition::of(LocalDateTime::ofEpochSecond($loopWindowStart->toEpochSecond($loopWindowOffset), 0, $loopStandardOffset), $loopStandardOffset, $window->standardOffset));
             $loopStandardOffset = $this->deduplicate($window->standardOffset);
         }
         // check if the start of the window represents a transition
         $effectiveWallOffset = $this->deduplicate(ZoneOffset::ofTotalSeconds($loopStandardOffset->getTotalSeconds() + $effectiveSavings));
         if ($loopWindowOffset->equals($effectiveWallOffset) === false) {
             $trans = $this->deduplicate(ZoneOffsetTransition::of($loopWindowStart, $loopWindowOffset, $effectiveWallOffset));
             $transitionList[] = $trans;
         }
         $loopSavings = $effectiveSavings;
         // apply rules within the window
         foreach ($window->ruleList as $rule) {
             /** @var ZoneOffsetTransition $trans */
             $trans = $this->deduplicate($rule->toTransition($loopStandardOffset, $loopSavings));
             if ($trans !== null && $trans->toEpochSecond() < $loopWindowStart->toEpochSecond($loopWindowOffset) === false && $trans->toEpochSecond() < $window->createDateTimeEpochSecond($loopSavings) && $trans->getOffsetBefore()->equals($trans->getOffsetAfter()) === false) {
                 $transitionList[] = $trans;
                 $loopSavings = $rule->savingAmountSecs;
             }
         }
         // calculate last rules
         foreach ($window->lastRuleList as $lastRule) {
             $transitionRule = $this->deduplicate($lastRule->toTransitionRule($loopStandardOffset, $loopSavings));
             $lastTransitionRuleList[] = $transitionRule;
             $loopSavings = $lastRule->savingAmountSecs;
         }
         // finally we can calculate the true end of the window, passing it to the next window
         $loopWindowOffset = $this->deduplicate($window->createWallOffset($loopSavings));
         $loopWindowStart = $this->deduplicate(LocalDateTime::ofEpochSecond($window->createDateTimeEpochSecond($loopSavings), 0, $loopWindowOffset));
     }
     return ZoneRules::of($firstWindow->standardOffset, $firstWallOffset, $standardTransitionList, $transitionList, $lastTransitionRuleList);
 }
Esempio n. 2
0
 /**
  * Gets the associated time-zone rules.
  * <p>
  * The rules will always return this offset when queried.
  * The implementation class is immutable, thread-safe and serializable.
  *
  * @return ZoneRules the rules, not null
  */
 public function getRules()
 {
     return ZoneRules::ofOffset($this);
 }
Esempio n. 3
0
 private function checkOffset(ZoneRules $rules, LocalDateTime $dateTime, ZoneOffset $offset, $type)
 {
     $validOffsets = $rules->getValidOffsets($dateTime);
     $this->assertEquals(count($validOffsets), $type);
     $this->assertEquals($rules->getOffsetDateTime($dateTime), $offset);
     if ($type === 1) {
         $this->assertEquals($validOffsets[0], $offset);
         return null;
     } else {
         $zot = $rules->getTransition($dateTime);
         $this->assertNotNull($zot);
         $this->assertEquals($zot->isOverlap(), $type == 2);
         $this->assertEquals($zot->isGap(), $type == 0);
         $this->assertEquals($zot->isValidOffset($offset), $type == 2);
         return $zot;
     }
 }
 /**
  * SPI method to get the rules for the zone ID.
  * <p>
  * This loads the rules for the specified zone ID.
  * The provider implementation must validate that the zone ID is valid and
  * available, throwing a {@code ZoneRulesException} if it is not.
  * The result of the method in the valid case depends on the caching flag.
  * <p>
  * If the provider implementation is not dynamic, then the result of the
  * method must be the non-null set of rules selected by the ID.
  * <p>
  * If the provider implementation is dynamic, then the flag gives the option
  * of preventing the returned rules from being cached in {@link ZoneId}.
  * When the flag is true, the provider is permitted to return null, where
  * null will prevent the rules from being cached in {@code ZoneId}.
  * When the flag is false, the provider must return non-null rules.
  *
  * @param string $zoneId the zone ID as defined by {@code ZoneId}, not null
  * @param bool $forCaching whether the rules are being queried for caching,
  * true if the returned rules will be cached by {@code ZoneId},
  * false if they will be returned to the user without being cached in {@code ZoneId}
  * @return ZoneRules the rules, null if {@code forCaching} is true and this
  * is a dynamic provider that wants to prevent caching in {@code ZoneId},
  * otherwise not null
  * @throws ZoneRulesException if rules cannot be obtained for the zone ID
  */
 protected function provideRules($zoneId, $forCaching)
 {
     return ZoneRules::ofOffset(ZoneOffset::ofHours(1));
 }