Ejemplo n.º 1
0
 public function testMinDateEpochSec()
 {
     $offset = ZoneOffset::ofTotalSeconds(0);
     $seconds = LocalDateTime::MIN()->toEpochSecond($offset);
     $new = LocalDateTime::ofEpochSecond($seconds, 0, $offset);
     $this->assertEquals(LocalDateTime::MIN(), $new);
 }
Ejemplo n.º 2
0
 /**
  * Converts this to a transition.
  *
  * @param ZoneOffset $standardOffset the active standard offset, not null
  * @param int $savingsBeforeSecs the active savings in seconds
  * @return ZoneOffsetTransition the transition, not null
  */
 function toTransition(ZoneOffset $standardOffset, $savingsBeforeSecs)
 {
     // copy of code in ZoneOffsetTransitionRule to avoid infinite loop
     $date = $this->toLocalDate();
     $date = ZoneRulesBuilder::deduplicate($date);
     $ldt = ZoneRulesBuilder::deduplicate(LocalDateTime::ofDateAndTime($date, $this->time));
     /** @var ZoneOffset $wallOffset */
     $wallOffset = ZoneRulesBuilder::deduplicate(ZoneOffset::ofTotalSeconds($standardOffset->getTotalSeconds() + $savingsBeforeSecs));
     $dt = ZoneRulesBuilder::deduplicate($this->timeDefinition->createDateTime($ldt, $standardOffset, $wallOffset));
     $offsetAfter = ZoneRulesBuilder::deduplicate(ZoneOffset::ofTotalSeconds($standardOffset->getTotalSeconds() + $this->savingAmountSecs));
     return new ZoneOffsetTransition($dt, $wallOffset, $offsetAfter);
 }
Ejemplo n.º 3
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);
 }
Ejemplo n.º 4
0
 /**
  * @dataProvider data_withFieldLong
  */
 public function test_with_adjuster_ensureZoneOffsetConsistent(ZonedDateTime $base, TemporalField $setField, $setValue, ZonedDateTime $expected)
 {
     if ($setField == CF::OFFSET_SECONDS()) {
         $this->assertEquals($base->adjust(ZoneOffset::ofTotalSeconds($setValue)), $expected);
     }
 }
Ejemplo n.º 5
0
 /**
  * A query for {@code ZoneOffset} returning null if not found.
  * @param TemporalAccessor $temporal
  * @return null|ZoneOffset
  * @throws DateTimeException
  */
 public static function _offset(TemporalAccessor $temporal)
 {
     if ($temporal->isSupported(ChronoField::OFFSET_SECONDS())) {
         return ZoneOffset::ofTotalSeconds($temporal->get(ChronoField::OFFSET_SECONDS()));
     }
     return null;
 }
Ejemplo n.º 6
0
 /**
  * Creates the wall offset for the local date-time at the end of the window.
  *
  * @param int $savingsSecs the amount of savings in use in seconds
  * @return ZoneOffset the created date-time epoch second in the wall offset, not null
  */
 function createWallOffset($savingsSecs)
 {
     return ZoneOffset::ofTotalSeconds($this->standardOffset->getTotalSeconds() + $savingsSecs);
 }
Ejemplo n.º 7
0
 /**
  * Parse an offset following a prefix and set the ZoneId if it is valid.
  * To matching the parsing of ZoneId.of the values are not normalized
  * to ZoneOffsets.
  *
  * @param DateTimeParseContext $context the parse context
  * @param string $text the input text
  * @param int $prefixPos start of the prefix
  * @param int $position start of text after the prefix
  * @param OffsetIdPrinterParser $parser parser for the value after the prefix
  * @return int the position after the parse
  */
 private function parseOffsetBased(DateTimeParseContext $context, $text, $prefixPos, $position, OffsetIdPrinterParser $parser)
 {
     $prefix = strtoupper(substr($text, $prefixPos, $position - $prefixPos));
     if ($position >= strlen($text)) {
         $context->setParsedZone(ZoneId::of($prefix));
         return $position;
     }
     // '0' or 'Z' after prefix is not part of a valid ZoneId; use bare prefix
     if ($text[$position] === '0' || $context->charEquals($text[$position], 'Z')) {
         $context->setParsedZone(ZoneId::of($prefix));
         return $position;
     }
     $newContext = $context->copy();
     $endPos = $parser->parse($newContext, $text, $position);
     try {
         if ($endPos < 0) {
             if ($parser == OffsetIdPrinterParser::INSTANCE_ID_Z()) {
                 return ~$prefixPos;
             }
             $context->setParsedZone(ZoneId::of($prefix));
             return $position;
         }
         $offset = $newContext->getParsed(ChronoField::OFFSET_SECONDS());
         $zoneOffset = ZoneOffset::ofTotalSeconds($offset);
         $context->setParsedZone(ZoneId::ofOffset($prefix, $zoneOffset));
         return $endPos;
     } catch (DateTimeException $dte) {
         return ~$prefixPos;
     }
 }
Ejemplo n.º 8
0
 public function test_isBeforeIsAfterIsEqual2nanos()
 {
     $a = OffsetTime::of(11, 30, 59, 4, ZoneOffset::ofTotalSeconds(self::OFFSET_PONE()->getTotalSeconds() + 1));
     $b = OffsetTime::of(11, 30, 59, 3, self::OFFSET_PONE());
     // a is before b due to offset
     $this->assertEquals($a->isBefore($b), true);
     $this->assertEquals($a->isEqual($b), false);
     $this->assertEquals($a->isAfter($b), false);
     $this->assertEquals($b->isBefore($a), false);
     $this->assertEquals($b->isEqual($a), false);
     $this->assertEquals($b->isAfter($a), true);
     $this->assertEquals($a->isBefore($a), false);
     $this->assertEquals($b->isBefore($b), false);
     $this->assertEquals($a->isEqual($a), true);
     $this->assertEquals($b->isEqual($b), true);
     $this->assertEquals($a->isAfter($a), false);
     $this->assertEquals($b->isAfter($b), false);
 }
 private function parseOffset($str)
 {
     $secs = $this->parseSecs($str);
     return ZoneOffset::ofTotalSeconds($secs);
 }
Ejemplo n.º 10
0
 private function resolveInstant()
 {
     // add instant seconds if we have date, time and zone
     if ($this->date !== null && $this->time !== null) {
         if ($this->zone !== null) {
             $instant = $this->date->atTime($this->time)->atZone($this->zone)->getLong(CF::INSTANT_SECONDS());
             $this->fieldValues->put(CF::INSTANT_SECONDS(), $instant);
         } else {
             $offsetSecs = $this->fieldValues->get(CF::OFFSET_SECONDS());
             if ($offsetSecs !== null) {
                 $offset = ZoneOffset::ofTotalSeconds($offsetSecs);
                 $instant = $this->date->atTime($this->time)->atZone($offset)->getLong(CF::INSTANT_SECONDS());
                 $this->fieldValues->put(CF::INSTANT_SECONDS(), $instant);
             }
         }
     }
 }
 public function with(TemporalField $field, $newValue)
 {
     if ($field instanceof ChronoField) {
         $f = $field;
         switch ($f) {
             case ChronoField::INSTANT_SECONDS():
                 return $this->plus($newValue - $this->toEpochSecond(), ChronoUnit::SECONDS());
             case ChronoField::OFFSET_SECONDS():
                 $offset = ZoneOffset::ofTotalSeconds($f->checkValidIntValue($newValue));
                 return $this->create($this->dateTime->toInstant($offset), $this->zone);
         }
         return $this->ofBest($this->dateTime->with($field, $newValue), $this->zone, $this->offset);
     }
     return ChronoZonedDateTimeImpl::ensureValid($this->getChronology(), $field->adjustInto($this, $newValue));
 }
Ejemplo n.º 12
0
 public function test_nullPrefixOfOffset()
 {
     TestHelper::assertNullException($this, function () {
         ZoneId::ofOffset(null, ZoneOffset::ofTotalSeconds(1));
     });
 }
Ejemplo n.º 13
0
 /**
  * Returns a copy of this date-time with the specified field set to a new value.
  * <p>
  * This returns a {@code ZonedDateTime}, based on this one, with the value
  * for the specified field changed.
  * This can be used to change any supported field, such as the year, month or day-of-month.
  * If it is not possible to set the value, because the field is not supported or for
  * some other reason, an exception is thrown.
  * <p>
  * In some cases, changing the specified field can cause the resulting date-time to become invalid,
  * such as changing the month from 31st January to February would make the day-of-month invalid.
  * In cases like this, the field is responsible for resolving the date. Typically it will choose
  * the previous valid date, which would be the last valid day of February in this example.
  * <p>
  * If the field is a {@link ChronoField} then the adjustment is implemented here.
  * <p>
  * The {@code INSTANT_SECONDS} field will return a date-time with the specified instant.
  * The zone and nano-of-second are unchanged.
  * The result will have an offset derived from the new instant and original zone.
  * If the new instant value is outside the valid range then a {@code DateTimeException} will be thrown.
  * <p>
  * The {@code OFFSET_SECONDS} field will typically be ignored.
  * The offset of a {@code ZonedDateTime} is controlled primarily by the time-zone.
  * As such, changing the offset does not generally make sense, because there is only
  * one valid offset for the local date-time and zone.
  * If the zoned date-time is in a daylight savings overlap, then the offset is used
  * to switch between the two valid offsets. In all other cases, the offset is ignored.
  * If the new offset value is outside the valid range then a {@code DateTimeException} will be thrown.
  * <p>
  * The other {@link #isSupported(TemporalField) supported fields} will behave as per
  * the matching method on {@link LocalDateTime#with(TemporalField, long) LocalDateTime}.
  * The zone is not part of the calculation and will be unchanged.
  * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
  * then the offset will be retained if possible, otherwise the earlier offset will be used.
  * If in a gap, the local date-time will be adjusted forward by the length of the gap.
  * <p>
  * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
  * <p>
  * If the field is not a {@code ChronoField}, then the result of this method
  * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
  * passing {@code this} as the argument. In this case, the field determines
  * whether and how to adjust the instant.
  * <p>
  * This instance is immutable and unaffected by this method call.
  *
  * @param TemporalField $field the field to set in the result, not null
  * @param int $newValue the new value of the field in the result
  * @return ZonedDateTime a {@code ZonedDateTime} based on {@code this} with the specified field set, not null
  * @throws DateTimeException if the field cannot be set
  * @throws UnsupportedTemporalTypeException if the field is not supported
  * @throws ArithmeticException if numeric overflow occurs
  */
 public function with(TemporalField $field, $newValue)
 {
     if ($field instanceof ChronoField) {
         $f = $field;
         switch ($f) {
             case ChronoField::INSTANT_SECONDS():
                 return self::create($newValue, $this->getNano(), $this->zone);
             case ChronoField::OFFSET_SECONDS():
                 $offset = ZoneOffset::ofTotalSeconds($f->checkValidIntValue($newValue));
                 return $this->resolveOffset($offset);
         }
         return $this->resolveLocal($this->dateTime->with($field, $newValue));
     }
     return $field->adjustInto($this, $newValue);
 }
Ejemplo n.º 14
0
 /**
  * Returns a copy of this date-time with the specified field set to a new value.
  * <p>
  * This returns an {@code OffsetDateTime}, based on this one, with the value
  * for the specified field changed.
  * This can be used to change any supported field, such as the year, month or day-of-month.
  * If it is not possible to set the value, because the field is not supported or for
  * some other reason, an exception is thrown.
  * <p>
  * In some cases, changing the specified field can cause the resulting date-time to become invalid,
  * such as changing the month from 31st January to February would make the day-of-month invalid.
  * In cases like this, the field is responsible for resolving the date. Typically it will choose
  * the previous valid date, which would be the last valid day of February in this example.
  * <p>
  * If the field is a {@link ChronoField} then the adjustment is implemented here.
  * <p>
  * The {@code INSTANT_SECONDS} field will return a date-time with the specified instant.
  * The offset and nano-of-second are unchanged.
  * If the new instant value is outside the valid range then a {@code DateTimeException} will be thrown.
  * <p>
  * The {@code OFFSET_SECONDS} field will return a date-time with the specified offset.
  * The local date-time is unaltered. If the new offset value is outside the valid range
  * then a {@code DateTimeException} will be thrown.
  * <p>
  * The other {@link #isSupported(TemporalField) supported fields} will behave as per
  * the matching method on {@link LocalDateTime#with(TemporalField, long) LocalDateTime}.
  * In this case, the offset is not part of the calculation and will be unchanged.
  * <p>
  * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
  * <p>
  * If the field is not a {@code ChronoField}, then the result of this method
  * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
  * passing {@code this} as the argument. In this case, the field determines
  * whether and how to adjust the instant.
  * <p>
  * This instance is immutable and unaffected by this method call.
  *
  * @param TemporalField $field the field to set in the result, not null
  * @param int $newValue the new value of the field in the result
  * @return OffsetDateTime an {@code OffsetDateTime} based on {@code this} with the specified field set, not null
  * @throws DateTimeException if the field cannot be set
  * @throws UnsupportedTemporalTypeException if the field is not supported
  * @throws ArithmeticException if numeric overflow occurs
  */
 public function with(TemporalField $field, $newValue)
 {
     if ($field instanceof ChronoField) {
         $f = $field;
         switch ($f) {
             case ChronoField::INSTANT_SECONDS():
                 return $this->ofInstant(Instant::ofEpochSecond($newValue, $this->getNano()), $this->offset);
             case ChronoField::OFFSET_SECONDS():
                 return $this->_with($this->dateTime, ZoneOffset::ofTotalSeconds($f->checkValidIntValue($newValue)));
         }
         return $this->_with($this->dateTime->with($field, $newValue), $this->offset);
     }
     return $field->adjustInto($this, $newValue);
 }
Ejemplo n.º 15
0
 public static function init()
 {
     self::$SECONDS_CACHE = [];
     self::$ID_CACHE = [];
     self::$UTC = ZoneOffset::ofTotalSeconds(0);
     self::$MIN = ZoneOffset::ofTotalSeconds(-self::MAX_SECONDS);
     self::$MAX = ZoneOffset::ofTotalSeconds(self::MAX_SECONDS);
 }
Ejemplo n.º 16
0
 public function test_getTotalSeconds()
 {
     $offset = ZoneOffset::ofTotalSeconds(60 * 60 + 1);
     $this->assertEquals($offset->getTotalSeconds(), 60 * 60 + 1);
 }
Ejemplo n.º 17
0
 /**
  * Returns a copy of this time with the specified field set to a new value.
  * <p>
  * This returns an {@code OffsetTime}, based on this one, with the value
  * for the specified field changed.
  * This can be used to change any supported field, such as the hour, minute or second.
  * If it is not possible to set the value, because the field is not supported or for
  * some other reason, an exception is thrown.
  * <p>
  * If the field is a {@link ChronoField} then the adjustment is implemented here.
  * <p>
  * The {@code OFFSET_SECONDS} field will return a time with the specified offset.
  * The local time is unaltered. If the new offset value is outside the valid range
  * then a {@code DateTimeException} will be thrown.
  * <p>
  * The other {@link #isSupported(TemporalField) supported fields} will behave as per
  * the matching method on {@link LocalTime#with(TemporalField, long)} LocalTime}.
  * In this case, the offset is not part of the calculation and will be unchanged.
  * <p>
  * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
  * <p>
  * If the field is not a {@code ChronoField}, then the result of this method
  * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
  * passing {@code this} as the argument. In this case, the field determines
  * whether and how to adjust the instant.
  * <p>
  * This instance is immutable and unaffected by this method call.
  *
  * @param TemporalField $field the field to set in the result, not null
  * @param int $newValue the new value of the field in the result
  * @return OffsetTime an {@code OffsetTime} based on {@code this} with the specified field set, not null
  * @throws DateTimeException if the field cannot be set
  * @throws UnsupportedTemporalTypeException if the field is not supported
  * @throws ArithmeticException if numeric overflow occurs
  */
 public function with(TemporalField $field, $newValue)
 {
     if ($field instanceof ChronoField) {
         if ($field == ChronoField::OFFSET_SECONDS()) {
             $f = $field;
             return $this->_with($this->time, ZoneOffset::ofTotalSeconds($f->checkValidIntValue($newValue)));
         }
         return $this->_with($this->time->with($field, $newValue), $this->offset);
     }
     return $field->adjustInto($this, $newValue);
 }