public function addTo(Temporal $temporal, $amount) { switch ($this) { case IsoFields::WEEK_BASED_YEARS(): return $temporal->with(IsoFields::WEEK_BASED_YEAR(), Math::addExact($temporal->get(IsoFields::WEEK_BASED_YEAR()), $amount)); case IsoFields::QUARTER_YEARS(): // no overflow (256 is multiple of 4) return $temporal->plus($amount / 256, ChronoUnit::YEARS())->plus($amount % 256 * 3, ChronoUnit::MONTHS()); default: throw new IllegalStateException("Unreachable"); } }
/** * Returns a copy of this {@code LocalDate} with the specified number of days added. * <p> * This method adds the specified amount 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 day would result in 2009-01-01. * <p> * This instance is immutable and unaffected by this method call. * * @param int $daysToAdd the days to add, may be negative * @return LocalDate a {@code LocalDate} based on this date with the days added, not null * @throws DateTimeException if the result exceeds the supported date range */ public function plusDays($daysToAdd) { if ($daysToAdd === 0) { return $this; } try { $mjDay = Math::addExact($this->toEpochDay(), $daysToAdd); return self::ofEpochDay($mjDay); } catch (ArithmeticException $ex) { throw new DateTimeException('Value out of bounds', $ex); } }
private function nanosUntil(Instant $end) { $secsDiff = Math::subtractExact($end->seconds, $this->seconds); $totalNanos = Math::multiplyExact($secsDiff, LocalTime::NANOS_PER_SECOND); return Math::addExact($totalNanos, $end->nanos - $this->nanos); }
/** * @param int $amountToAdd * @param TemporalUnit $unit * @return static * @throws UnsupportedTemporalTypeException */ public function plus($amountToAdd, TemporalUnit $unit) { if ($unit instanceof CU) { switch ($unit) { case CU::DAYS(): return $this->plusDays($amountToAdd); case CU::WEEKS(): return $this->plusDays(Math::multiplyExact($amountToAdd, 7)); case CU::MONTHS(): return $this->plusMonths($amountToAdd); case CU::YEARS(): return $this->plusYears($amountToAdd); case CU::DECADES(): return $this->plusYears(Math::multiplyExact($amountToAdd, 10)); case CU::CENTURIES(): return $this->plusYears(Math::multiplyExact($amountToAdd, 100)); case CU::MILLENNIA(): return $this->plusYears(Math::multiplyExact($amountToAdd, 1000)); case CU::ERAS(): return $this->with(CF::ERA(), Math::addExact($this->getLong(CF::ERA()), $amountToAdd)); } throw new UnsupportedTemporalTypeException("Unsupported unit: " . $unit); } return parent::plus($amountToAdd, $unit); }
/** * 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; }
/** * 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); }
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()); } } }
/** * 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 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); }