public function instant() { if ($this->tickNanos % 1000000 == 0) { $millis = $this->baseClock->millis(); return Instant::ofEpochMilli($millis - Math::floorMod($millis, $this->tickNanos / 1000000)); } $instant = $this->baseClock->instant(); $nanos = $instant->getNano(); $adjust = Math::floorMod($nanos, $this->tickNanos); return $instant->minusNanos($adjust); }
/** * Obtains the current time from the specified clock. * <p> * This will query the specified clock to obtain the current time. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@link Clock dependency injection}. * * @param Clock $clock the clock to use, not null * @return LocalTime the current time, not null */ public static function nowOf(Clock $clock) { // inline OffsetTime factory to avoid creating object and InstantProvider checks $now = $clock->instant(); // called once $offset = $clock->getZone()->getRules()->getOffset($now); $localSecond = $now->getEpochSecond() + $offset->getTotalSeconds(); // overflow caught later $secsOfDay = (int) Math::floorMod($localSecond, self::SECONDS_PER_DAY); return self::ofNanoOfDay($secsOfDay * self::NANOS_PER_SECOND + $now->getNano()); }
/** * Returns a copy of this {@code LocalDate} with the specified number of months added. * <p> * This method adds the specified amount to the months field in three steps: * <ol> * <li>Add the input months to the month-of-year field</li> * <li>Check if the resulting date would be invalid</li> * <li>Adjust the day-of-month to the last valid day if necessary</li> * </ol> * <p> * For example, 2007-03-31 plus one month would result in the invalid date * 2007-04-31. Instead of returning an invalid result, the last valid day * of the month, 2007-04-30, is selected instead. * <p> * This instance is immutable and unaffected by this method call. * * @param int $monthsToAdd the months to add, may be negative * @return LocalDate a {@code LocalDate} based on this date with the months added, not null * @throws DateTimeException if the result exceeds the supported date range */ public function plusMonths($monthsToAdd) { if ($monthsToAdd === 0) { return $this; } $monthCount = $this->year * 12 + ($this->month - 1); $calcMonths = $monthCount + $monthsToAdd; // safe overflow if (!is_int($calcMonths)) { throw new DateTimeException('Overflow'); // Todo better message } $newYear = ChronoField::YEAR()->checkValidIntValue(Math::floorDiv($calcMonths, 12)); $newMonth = Math::floorMod($calcMonths, 12) + 1; return self::resolvePreviousValid($newYear, $newMonth, $this->day); }
/** * Obtains an instance of {@code Instant} using milliseconds from the * epoch of 1970-01-01T00:00:00Z. * <p> * The seconds and nanoseconds are extracted from the specified milliseconds. * * @param int $epochMilli the number of milliseconds from 1970-01-01T00:00:00Z * @return Instant an instant, not null * @throws DateTimeException if the instant exceeds the maximum or minimum instant */ public static function ofEpochMilli($epochMilli) { $secs = Math::floorDiv($epochMilli, 1000); $mos = (int) Math::floorMod($epochMilli, 1000); return self::create($secs, $mos * 1000000); }
protected function resolveProlepticMonth(FieldValues $fieldValues, ResolverStyle $resolverStyle) { $pMonth = $fieldValues->remove(CF::PROLEPTIC_MONTH()); if ($pMonth !== null) { if ($resolverStyle != ResolverStyle::LENIENT()) { CF::PROLEPTIC_MONTH()->checkValidValue($pMonth); } $this->addFieldValue($fieldValues, CF::MONTH_OF_YEAR(), Math::floorMod($pMonth, 12) + 1); $this->addFieldValue($fieldValues, CF::YEAR(), Math::floorDiv($pMonth, 12)); } }
/** * @dataProvider data_weekFields */ public function test_weekOfWeekBasedYearField(DayOfWeek $firstDayOfWeek, $minDays) { $day = LocalDate::of(2012, 12, 31); // Known to be ISO Monday $weekDef = WeekFields::of($firstDayOfWeek, $minDays); $dowField = $weekDef->dayOfWeek(); $wowbyField = $weekDef->weekOfWeekBasedYear(); $yowbyField = $weekDef->weekBasedYear(); for ($i = 1; $i <= 15; $i++) { $actualDOW = $day->get($dowField); $actualWOWBY = $day->get($wowbyField); $actualYOWBY = $day->get($yowbyField); // Verify that the combination of $day of $week and $week of month can be used // to reconstruct the same $date. $day1 = LocalDate::of($actualYOWBY, 1, 1); $isoDOW = $day1->getDayOfWeek(); $dow = (7 + $isoDOW->getValue() - $firstDayOfWeek->getValue()) % 7 + 1; $weekStart = Math::floorMod(1 - $dow, 7); if ($weekStart + 1 > $weekDef->getMinimalDaysInFirstWeek()) { // The previous $week has the minimum days in the current month to be a '$week' $weekStart -= 7; } $weekStart += $actualDOW - 1; $weekStart += ($actualWOWBY - 1) * 7; $result = $day1->plusDays($weekStart); $this->assertEquals($result, $day, "Incorrect dayOfWeek or weekOfYear " . sprintf("%s, ISO Dow: %s, weekStart: %s, actualDOW: %s, actualWOWBY: %s, YearOfWBY: %d, expected day: %s, result: %s\n", $weekDef, $day->getDayOfWeek(), $weekStart, $actualDOW, $actualWOWBY, $actualYOWBY, $day, $result)); $day = $day->plusDays(1); } }
/** * Obtains a {@code Duration} representing a number of seconds and an * adjustment in nanoseconds. * <p> * This method allows an arbitrary number of nanoseconds to be passed in. * The factory will alter the values of the second and nanosecond in order * to ensure that the stored nanosecond is in the range 0 to 999,999,999. * For example, the following will result in the exactly the same duration: * <pre> * Duration.ofSeconds(3, 1); * Duration.ofSeconds(4, -999_999_999); * Duration.ofSeconds(2, 1000_000_001); * </pre> * * @param int $seconds the number of seconds, positive or negative * @param int $nanoAdjustment the nanosecond adjustment to the number of seconds, positive or negative * @return Duration a {@code Duration}, not null * @throws ArithmeticException if the adjustment causes the seconds to exceed the capacity of {@code Duration} */ public static function ofSeconds($seconds, $nanoAdjustment = 0) { $secs = Math::addExact($seconds, Math::floorDiv($nanoAdjustment, LocalTime::NANOS_PER_SECOND)); $nos = Math::floorMod($nanoAdjustment, LocalTime::NANOS_PER_SECOND); return self::create($secs, $nos); }
private function resolveTime($hod, $moh, $som, $nos) { if ($this->resolverStyle == ResolverStyle::LENIENT()) { $totalNanos = Math::multiplyExact($hod, 3600000000000); $totalNanos = Math::addExact($totalNanos, Math::multiplyExact($moh, 60000000000)); $totalNanos = Math::addExact($totalNanos, Math::multiplyExact($som, 1000000000)); $totalNanos = Math::addExact($totalNanos, $nos); $excessDays = (int) Math::floorDiv($totalNanos, 86400000000000); // safe int cast $nod = Math::floorMod($totalNanos, 86400000000000); $this->updateCheckConflict(LocalTime::ofNanoOfDay($nod), Period::ofDays($excessDays)); } else { // STRICT or SMART $mohVal = CF::MINUTE_OF_HOUR()->checkValidIntValue($moh); $nosVal = CF::NANO_OF_SECOND()->checkValidIntValue($nos); // handle 24:00 end of day if ($this->resolverStyle == ResolverStyle::SMART() && $hod == 24 && $mohVal == 0 && $som == 0 && $nosVal == 0) { $this->updateCheckConflict(LocalTime::MIDNIGHT(), Period::ofDays(1)); } else { $hodVal = CF::HOUR_OF_DAY()->checkValidIntValue($hod); $somVal = CF::SECOND_OF_MINUTE()->checkValidIntValue($som); $this->updateCheckConflict(LocalTime::of($hodVal, $mohVal, $somVal, $nosVal), Period::ZERO()); } } }
/** * Returns a copy of this {@code LocalDateTime} with the specified period added. * <p> * This instance is immutable and unaffected by this method call. * * @param LocalDate $newDate the new date to base the calculation on, not null * @param int $hours the hours to add, may be negative * @param int $minutes the minutes to add, may be negative * @param int $seconds the seconds to add, may be negative * @param int $nanos the nanos to add, may be negative * @param int $sign the sign to determine add or subtract * @return LocalDateTime the combined result, not null */ private function plusWithOverflow(LocalDate $newDate, $hours, $minutes, $seconds, $nanos, $sign) { // 9223372036854775808 long, 2147483648 int if (($hours | $minutes | $seconds | $nanos) == 0) { return $this->_with($newDate, $this->time); } $totDays = Math::div($nanos, LocalTime::NANOS_PER_DAY) + Math::div($seconds, LocalTime::SECONDS_PER_DAY) + Math::div($minutes, LocalTime::MINUTES_PER_DAY) + Math::div($hours, LocalTime::HOURS_PER_DAY); // max/24 $totDays *= $sign; // total max*0.4237... $totNanos = $nanos % LocalTime::NANOS_PER_DAY + $seconds % LocalTime::SECONDS_PER_DAY * LocalTime::NANOS_PER_SECOND + $minutes % LocalTime::MINUTES_PER_DAY * LocalTime::NANOS_PER_MINUTE + $hours % LocalTime::HOURS_PER_DAY * LocalTime::NANOS_PER_HOUR; // max 86400000000000 $curNoD = $this->time->toNanoOfDay(); // max 86400000000000 $totNanos = $totNanos * $sign + $curNoD; // total 432000000000000 $totDays += Math::floorDiv($totNanos, LocalTime::NANOS_PER_DAY); $newNoD = Math::floorMod($totNanos, LocalTime::NANOS_PER_DAY); $newTime = $newNoD === $curNoD ? $this->time : LocalTime::ofNanoOfDay($newNoD); return $this->_with($newDate->plusDays($totDays), $newTime); }
public function resolve(FieldValues $fieldValues, TemporalAccessor $partialTemporal, ResolverStyle $resolverStyle) { $value = $fieldValues->get($this); $newValue = Math::toIntExact($value); // broad limit makes overflow checking lighter // first convert localized day-of-week to ISO day-of-week // doing this first handles case where both ISO and localized were parsed and might mismatch // day-of-week is always strict as two different day-of-week values makes lenient complex if ($this->rangeUnit == ChronoUnit::WEEKS()) { // day-of-week $checkedValue = $this->range->checkValidIntValue($value, $this); // no leniency as too complex $startDow = $this->weekDef->getFirstDayOfWeek()->getValue(); $isoDow = Math::floorMod($startDow - 1 + ($checkedValue - 1), 7) + 1; $fieldValues->remove($this); $fieldValues->put(CF::DAY_OF_WEEK(), $isoDow); return null; } // can only build date if ISO day-of-week is present if (!$fieldValues->has(CF::DAY_OF_WEEK())) { return null; } $isoDow = CF::DAY_OF_WEEK()->checkValidIntValue($fieldValues->get(CF::DAY_OF_WEEK())); $dow = $this->localizedDayOfWeekNumerical($isoDow); // build date $chrono = AbstractChronology::from($partialTemporal); if ($fieldValues->has(CF::YEAR())) { $year = CF::YEAR()->checkValidIntValue($fieldValues->get(CF::YEAR())); // validate if ($this->rangeUnit == ChronoUnit::MONTHS() && $fieldValues->has(CF::MONTH_OF_YEAR())) { // week-of-month $month = $fieldValues->get(CF::MONTH_OF_YEAR()); // not validated yet return $this->resolveWoM($fieldValues, $chrono, $year, $month, $newValue, $dow, $resolverStyle); } if ($this->rangeUnit == ChronoUnit::YEARS()) { // week-of-year return $this->resolveWoY($fieldValues, $chrono, $year, $newValue, $dow, $resolverStyle); } } else { if (($this->rangeUnit == IsoFields::WEEK_BASED_YEARS() || $this->rangeUnit == ChronoUnit::FOREVER()) && $fieldValues->has($this->weekDef->weekBasedYear) && $fieldValues->has($this->weekDef->weekOfWeekBasedYear)) { // week-of-week-based-year and year-of-week-based-year return $this->resolveWBY($fieldValues, $chrono, $dow, $resolverStyle); } } return null; }
public function format(DateTimePrintContext $context, &$buf) { // use INSTANT_SECONDS, thus this code is not bound by Instant.MAX $inSecs = $context->getValueField(ChronoField::INSTANT_SECONDS()); $inNanos = null; if ($context->getTemporal()->isSupported(ChronoField::NANO_OF_SECOND())) { $inNanos = $context->getTemporal()->getLong(ChronoField::NANO_OF_SECOND()); } if ($inSecs === null) { return false; } $inSec = $inSecs; $inNano = ChronoField::NANO_OF_SECOND()->checkValidIntValue($inNanos !== null ? $inNanos : 0); // format mostly using LocalDateTime.toString if ($inSec >= -self::SECONDS_0000_TO_1970) { // current era $zeroSecs = $inSec - self::SECONDS_PER_10000_YEARS + self::SECONDS_0000_TO_1970; $hi = Math::floorDiv($zeroSecs, self::SECONDS_PER_10000_YEARS) + 1; $lo = Math::floorMod($zeroSecs, self::SECONDS_PER_10000_YEARS); $ldt = LocalDateTime::ofEpochSecond($lo - self::SECONDS_0000_TO_1970, 0, ZoneOffset::UTC()); if ($hi > 0) { $buf .= '+' . $hi; } $buf .= $ldt; if ($ldt->getSecond() === 0) { $buf .= ":00"; } } else { // before current era $zeroSecs = $inSec + self::SECONDS_0000_TO_1970; $hi = Math::div($zeroSecs, self::SECONDS_PER_10000_YEARS); $lo = $zeroSecs % self::SECONDS_PER_10000_YEARS; $ldt = LocalDateTime::ofEpochSecond($lo - self::SECONDS_0000_TO_1970, 0, ZoneOffset::UTC()); $pos = strlen($buf); $buf .= $ldt; if ($ldt->getSecond() === 0) { $buf .= ":00"; } if ($hi < 0) { if ($ldt->getYear() === -10000) { $buf = substr_replace($buf, $hi - 1, $pos, 2); } else { if ($lo === 0) { $buf = substr_replace($buf, $hi, $pos, 0); } else { $buf = substr_replace($buf, Math::abs($hi), $pos + 1, 0); } } } } // add fraction if ($this->fractionalDigits < 0 && $inNano > 0 || $this->fractionalDigits > 0) { $buf .= '.'; $div = 100000000; for ($i = 0; $this->fractionalDigits === -1 && $inNano > 0 || $this->fractionalDigits === -2 && ($inNano > 0 || $i % 3 !== 0) || $i < $this->fractionalDigits; $i++) { $digit = Math::div($inNano, $div); $buf .= $digit; $inNano = $inNano - $digit * $div; $div = Math::div($div, 10); } } $buf .= 'Z'; return true; }
/** * Obtains an instance of {@code OffsetTime} from an {@code Instant} and zone ID. * <p> * This creates an offset time with the same instant as that specified. * Finding the offset from UTC/Greenwich is simple as there is only one valid * offset for each instant. * <p> * The date component of the instant is dropped during the conversion. * This means that the conversion can never fail due to the instant being * out of the valid range of dates. * * @param Instant $instant the instant to create the time from, not null * @param ZoneId $zone the time-zone, which may be an offset, not null * @return OffsetTime the offset time, not null */ public static function ofInstant(Instant $instant, ZoneId $zone) { $rules = $zone->getRules(); $offset = $rules->getOffset($instant); $localSecond = $instant->getEpochSecond() + $offset->getTotalSeconds(); // overflow caught later $secsOfDay = (int) Math::floorMod($localSecond, LocalTime::SECONDS_PER_DAY); $time = LocalTime::ofNanoOfDay($secsOfDay * LocalTime::NANOS_PER_SECOND + $instant->getNano()); return new OffsetTime($time, $offset); }