/** * Converts this instant to the number of milliseconds from the epoch * of 1970-01-01T00:00:00Z. * <p> * If this instant represents a point on the time-line too far in the future * or past to fit in a {@code long} milliseconds, then an exception is thrown. * <p> * If this instant has greater than millisecond precision, then the conversion * will drop any excess precision information as though the amount in nanoseconds * was subject to integer division by one million. * * @return int the number of milliseconds since the epoch of 1970-01-01T00:00:00Z * @throws ArithmeticException if numeric overflow occurs */ public function toEpochMilli() { $millis = Math::multiplyExact($this->seconds, 1000); return $millis + Math::div($this->nanos, 1000000); }
/** * Returns a copy of this {@code LocalDate} with the specified number of weeks added. * <p> * This method adds the specified amount in weeks to the days field incrementing * the month and year fields as necessary to ensure the result remains valid. * The result is only invalid if the maximum/minimum year is exceeded. * <p> * For example, 2008-12-31 plus one week would result in 2009-01-07. * <p> * This instance is immutable and unaffected by this method call. * * @param int $weeksToAdd the weeks to add, may be negative * @return LocalDate a {@code LocalDate} based on this date with the weeks added, not null * @throws DateTimeException if the result exceeds the supported date range */ public function plusWeeks($weeksToAdd) { return $this->plusDays(Math::multiplyExact($weeksToAdd, 7)); }
/** * Returns a copy of this year with the specified amount added. * <p> * This returns a {@code Year}, based on this one, with the amount * in terms of the unit added. If it is not possible to add the amount, because the * unit is not supported or for some other reason, an exception is thrown. * <p> * If the field is a {@link ChronoUnit} then the addition is implemented here. * The supported fields behave as follows: * <ul> * <li>{@code YEARS} - * Returns a {@code Year} with the specified number of years added. * This is equivalent to {@link #plusYears(long)}. * <li>{@code DECADES} - * Returns a {@code Year} with the specified number of decades added. * This is equivalent to calling {@link #plusYears(long)} with the amount * multiplied by 10. * <li>{@code CENTURIES} - * Returns a {@code Year} with the specified number of centuries added. * This is equivalent to calling {@link #plusYears(long)} with the amount * multiplied by 100. * <li>{@code MILLENNIA} - * Returns a {@code Year} with the specified number of millennia added. * This is equivalent to calling {@link #plusYears(long)} with the amount * multiplied by 1,000. * <li>{@code ERAS} - * Returns a {@code Year} with the specified number of eras added. * Only two eras are supported so the amount must be one, zero or minus one. * If the amount is non-zero then the year is changed such that the year-of-era * is unchanged. * </ul> * <p> * All other {@code ChronoUnit} instances will throw an {@code UnsupportedTemporalTypeException}. * <p> * If the field is not a {@code ChronoUnit}, then the result of this method * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)} * passing {@code this} as the argument. In this case, the unit determines * whether and how to perform the addition. * <p> * This instance is immutable and unaffected by this method call. * * @param int $amountToAdd the amount of the unit to add to the result, may be negative * @param TemporalUnit $unit the unit of the amount to add, not null * @return Year a {@code Year} based on this year with the specified amount added, not null * @throws DateTimeException if the addition cannot be made * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ public function plus($amountToAdd, TemporalUnit $unit) { if ($unit instanceof ChronoUnit) { switch ($unit) { case ChronoUnit::YEARS(): return $this->plusYears($amountToAdd); case ChronoUnit::DECADES(): return $this->plusYears(Math::multiplyExact($amountToAdd, 10)); case ChronoUnit::CENTURIES(): return $this->plusYears(Math::multiplyExact($amountToAdd, 100)); case ChronoUnit::MILLENNIA(): return $this->plusYears(Math::multiplyExact($amountToAdd, 1000)); case ChronoUnit::ERAS(): return $this->with(ChronoField::ERA(), Math::addExact($this->getLong(ChronoField::ERA()), $amountToAdd)); } throw new UnsupportedTemporalTypeException("Unsupported unit: " . $unit); } return $unit->addTo($this, $amountToAdd); }
/** * Converts this duration to the total length in nanoseconds expressed as a {@code long}. * <p> * If this duration is too large to fit in a {@code long} nanoseconds, then an * exception is thrown. * * @return int the total length of the duration in nanoseconds * @throws ArithmeticException if numeric overflow occurs */ public function toNanos() { $totalNanos = Math::multiplyExact($this->seconds, LocalTime::NANOS_PER_SECOND); $totalNanos = Math::addExact($totalNanos, $this->nanos); return $totalNanos; }
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()); } } }
public function resolve(FieldValues $fieldValues, TemporalAccessor $partialTemporal, ResolverStyle $resolverStyle) { $yearLong = $fieldValues->get(ChronoField::YEAR()); $qoyLong = $fieldValues->get(IsoFields::QUARTER_OF_YEAR()); if ($yearLong === null || $qoyLong === null) { return null; } $y = ChronoField::YEAR()->checkValidIntValue($yearLong); // always validate $doq = $fieldValues->get(IsoFields::DAY_OF_QUARTER()); IsoFields::ensureIso($partialTemporal); if ($resolverStyle == ResolverStyle::LENIENT()) { $date = LocalDate::of($y, 1, 1)->plusMonths(Math::multiplyExact(Math::subtractExact($qoyLong, 1), 3)); $doq = Math::subtractExact($doq, 1); } else { $qoy = IsoFields::QUARTER_OF_YEAR()->range()->checkValidIntValue($qoyLong, IsoFields::QUARTER_OF_YEAR()); // validated $date = LocalDate::of($y, ($qoy - 1) * 3 + 1, 1); if ($doq < 1 || $doq > 90) { if ($resolverStyle == ResolverStyle::STRICT()) { $this->rangeRefinedBy($date)->checkValidValue($doq, $this); // only allow exact range } else { // SMART $this->range()->checkValidValue($doq, $this); // allow 1-92 rolling into next quarter } } $doq--; } $fieldValues->remove($this); $fieldValues->remove(ChronoField::YEAR()); $fieldValues->remove(IsoFields::QUARTER_OF_YEAR()); return $date->plusDays($doq); }
/** * Calculates the amount of time until another date-time in terms of the specified unit. * <p> * This calculates the amount of time between two {@code LocalDateTime} * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified date-time. * The result will be negative if the end is before the start. * The {@code Temporal} passed to this method is converted to a * {@code LocalDateTime} using {@link #from(TemporalAccessor)}. * For example, the amount in days between two date-times can be calculated * using {@code startDateTime.until(endDateTime, DAYS)}. * <p> * The calculation returns a whole number, representing the number of * complete units between the two date-times. * For example, the amount in months between 2012-06-15T00:00 and 2012-08-14T23:59 * will only be one month as it is one minute short of two months. * <p> * There are two equivalent ways of using this method. * The first is to invoke this method. * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}: * <pre> * // these two lines are equivalent * amount = start.until(end, MONTHS); * amount = MONTHS.between(start, end); * </pre> * The choice should be made based on which makes the code more readable. * <p> * The calculation is implemented in this method for {@link ChronoUnit}. * The units {@code NANOS}, {@code MICROS}, {@code MILLIS}, {@code SECONDS}, * {@code MINUTES}, {@code HOURS} and {@code HALF_DAYS}, {@code DAYS}, * {@code WEEKS}, {@code MONTHS}, {@code YEARS}, {@code DECADES}, * {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS} are supported. * Other {@code ChronoUnit} values will throw an exception. * <p> * If the unit is not a {@code ChronoUnit}, then the result of this method * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} * passing {@code this} as the first argument and the converted input temporal * as the second argument. * <p> * This instance is immutable and unaffected by this method call. * * @param Temporal $endExclusive the end date, exclusive, which is converted to a {@code LocalDateTime}, not null * @param TemporalUnit $unit the unit to measure the amount in, not null * @return int the amount of time between this date-time and the end date-time * @throws DateTimeException if the amount cannot be calculated, or the end * temporal cannot be converted to a {@code LocalDateTime} * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ public function until(Temporal $endExclusive, TemporalUnit $unit) { $end = LocalDateTime::from($endExclusive); if ($unit instanceof ChronoUnit) { if ($unit->isTimeBased()) { $amount = $this->date->daysUntil($end->date); if ($amount === 0) { return $this->time->until($end->time, $unit); } $timePart = $end->time->toNanoOfDay() - $this->time->toNanoOfDay(); if ($amount > 0) { $amount--; // safe $timePart += LocalTime::NANOS_PER_DAY; // safe } else { $amount++; // safe $timePart -= LocalTime::NANOS_PER_DAY; // safe } switch ($unit) { case ChronoUnit::NANOS(): $amount = Math::multiplyExact($amount, LocalTime::NANOS_PER_DAY); break; case ChronoUnit::MICROS(): $amount = Math::multiplyExact($amount, LocalTime::MICROS_PER_DAY); $timePart = Math::div($timePart, 1000); break; case ChronoUnit::MILLIS(): $amount = Math::multiplyExact($amount, LocalTime::MILLIS_PER_DAY); $timePart = Math::div($timePart, 1000000); break; case ChronoUnit::SECONDS(): $amount = Math::multiplyExact($amount, LocalTime::SECONDS_PER_DAY); $timePart = Math::div($timePart, LocalTime::NANOS_PER_SECOND); break; case ChronoUnit::MINUTES(): $amount = Math::multiplyExact($amount, LocalTime::MINUTES_PER_DAY); $timePart = Math::div($timePart, LocalTime::NANOS_PER_MINUTE); break; case ChronoUnit::HOURS(): $amount = Math::multiplyExact($amount, LocalTime::HOURS_PER_DAY); $timePart = Math::div($timePart, LocalTime::NANOS_PER_HOUR); break; case ChronoUnit::HALF_DAYS(): $amount = Math::multiplyExact($amount, 2); $timePart = Math::div($timePart, LocalTime::NANOS_PER_HOUR * 12); break; } return Math::addExact($amount, $timePart); } $endDate = $end->date; if ($endDate->isAfter($this->date) && $end->time->isBefore($this->time)) { $endDate = $endDate->minusDays(1); } else { if ($endDate->isBefore($this->date) && $end->time->isAfter($this->time)) { $endDate = $endDate->plusDays(1); } } return $this->date->until($endDate, $unit); } return $unit->between($this, $end); }
private function resolveWoY(FieldValues $fieldValues, Chronology $chrono, $year, $woy, $localDow, ResolverStyle $resolverStyle) { $date = $chrono->date($year, 1, 1); if ($resolverStyle == ResolverStyle::LENIENT()) { $weeks = Math::subtractExact($woy, $this->localizedWeekOfYear($date)); $days = $localDow - $this->localizedDayOfWeek($date); // safe from overflow $date = $date->plus(Math::addExact(Math::multiplyExact($weeks, 7), $days), ChronoUnit::DAYS()); } else { $womInt = $this->range->checkValidIntValue($woy, $this); // validate $weeks = (int) ($womInt - $this->localizedWeekOfYear($date)); // safe from overflow $days = $localDow - $this->localizedDayOfWeek($date); // safe from overflow $date = $date->plus($weeks * 7 + $days, ChronoUnit::DAYS()); if ($resolverStyle == ResolverStyle::STRICT() && $date->getLong(CF::YEAR()) !== $year) { throw new DateTimeException("Strict mode rejected resolved date as it is in a different year"); } } $fieldValues->remove($this); $fieldValues->remove(CF::YEAR()); $fieldValues->remove(CF::DAY_OF_WEEK()); return $date; }
public function parse(DateTimeParseContext $context, $text, $position) { // TODO cache formatter // new context to avoid overwriting fields like year/month/day $minDigits = $this->fractionalDigits < 0 ? 0 : $this->fractionalDigits; $maxDigits = $this->fractionalDigits < 0 ? 9 : $this->fractionalDigits; $parser = (new DateTimeFormatterBuilder())->append(DateTimeFormatter::ISO_LOCAL_DATE())->appendLiteral('T')->appendValue2(ChronoField::HOUR_OF_DAY(), 2)->appendLiteral(':')->appendValue2(ChronoField::MINUTE_OF_HOUR(), 2)->appendLiteral(':')->appendValue2(ChronoField::SECOND_OF_MINUTE(), 2)->appendFraction(ChronoField::NANO_OF_SECOND(), $minDigits, $maxDigits, true)->appendLiteral('Z')->toFormatter()->toPrinterParser(false); $newContext = $context->copy(); $pos = $parser->parse($newContext, $text, $position); if ($pos < 0) { return $pos; } // parser restricts most fields to 2 digits, so definitely int // correctly parsed nano is also guaranteed to be valid $yearParsed = $newContext->getParsed(ChronoField::YEAR()); $month = $newContext->getParsed(ChronoField::MONTH_OF_YEAR()); $day = $newContext->getParsed(ChronoField::DAY_OF_MONTH()); $hour = $newContext->getParsed(ChronoField::HOUR_OF_DAY()); $min = $newContext->getParsed(ChronoField::MINUTE_OF_HOUR()); $secVal = $newContext->getParsed(ChronoField::SECOND_OF_MINUTE()); $nanoVal = $newContext->getParsed(ChronoField::NANO_OF_SECOND()); $sec = $secVal !== null ? $secVal : 0; $nano = $nanoVal !== null ? $nanoVal : 0; $days = 0; if ($hour === 24 && $min === 0 && $sec === 0 && $nano === 0) { $hour = 0; $days = 1; } else { if ($hour === 23 && $min === 59 && $sec === 60) { $context->setParsedLeapSecond(); $sec = 59; } } $year = $yearParsed % 10000; try { $ldt = LocalDateTime::of($year, $month, $day, $hour, $min, $sec, 0)->plusDays($days); $instantSecs = $ldt->toEpochSecond(ZoneOffset::UTC()); $instantSecs += Math::multiplyExact(Math::div($yearParsed, 10000), self::SECONDS_PER_10000_YEARS); } catch (RuntimeException $ex) { // TODO What do we actually catch here and why return ~$position; } $successPos = $pos; $successPos = $context->setParsedField(ChronoField::INSTANT_SECONDS(), $instantSecs, $position, $successPos); return $context->setParsedField(ChronoField::NANO_OF_SECOND(), $nano, $position, $successPos); }
public function until(Temporal $endExclusive, TemporalUnit $unit) { $end = $this->getChronology()->localDateTime($endExclusive); if ($unit instanceof ChronoUnit) { if ($unit->isTimeBased()) { $amount = $end->getLong(ChronoField::EPOCH_DAY()) - $this->date->getLong(ChronoField::EPOCH_DAY()); switch ($unit) { case ChronoUnit::NANOS(): $amount = Math::multiplyExact($amount, self::NANOS_PER_DAY); break; case ChronoUnit::MICROS(): $amount = Math::multiplyExact($amount, self::MICROS_PER_DAY); break; case ChronoUnit::MILLIS(): $amount = Math::multiplyExact($amount, self::MILLIS_PER_DAY); break; case ChronoUnit::SECONDS(): $amount = Math::multiplyExact($amount, self::SECONDS_PER_DAY); break; case ChronoUnit::MINUTES(): $amount = Math::multiplyExact($amount, self::MINUTES_PER_DAY); break; case ChronoUnit::HOURS(): $amount = Math::multiplyExact($amount, self::HOURS_PER_DAY); break; case ChronoUnit::HALF_DAYS(): $amount = Math::multiplyExact($amount, 2); break; } return Math::addExact($amount, $this->time->until($end->toLocalTime(), $unit)); } $endDate = $end->toLocalDate(); if ($end->toLocalTime()->isBefore($this->time)) { $endDate = $endDate->minus(1, ChronoUnit::DAYS()); } return $this->date->until($endDate, $unit); } return $unit->between($this, $end); }
/** * Gets the total number of months in this period. * <p> * This returns the total number of months in the period by multiplying the * number of years by 12 and adding the number of months. * <p> * This instance is immutable and unaffected by this method call. * * @return int the total number of months in the period, may be negative */ public function toTotalMonths() { return Math::addExact(Math::multiplyExact($this->years, 12), $this->months); }