public function parse(DateTimeParseContext $context, $text, $position)
 {
     $length = strlen($text);
     if ($position === $length) {
         return ~$position;
     }
     if ($position < 0 || $position >= $length) {
         throw new \OutOfRangeException();
     }
     $sign = $text[$position];
     $negative = false;
     $positive = false;
     if ($sign === $context->getDecimalStyle()->getPositiveSign()) {
         if ($this->signStyle->parse(true, $context->isStrict(), $this->minWidth === $this->maxWidth) === false) {
             return ~$position;
         }
         $positive = true;
         $position++;
     } else {
         if ($sign === $context->getDecimalStyle()->getNegativeSign()) {
             if ($this->signStyle->parse(false, $context->isStrict(), $this->minWidth === $this->maxWidth) === false) {
                 return ~$position;
             }
             $negative = true;
             $position++;
         } else {
             if ($this->signStyle == SignStyle::ALWAYS() && $context->isStrict()) {
                 return ~$position;
             }
         }
     }
     $effMinWidth = $context->isStrict() || $this->isFixedWidth($context) ? $this->minWidth : 1;
     $minEndPos = $position + $effMinWidth;
     if ($minEndPos > $length) {
         return ~$position;
     }
     $effMaxWidth = ($context->isStrict() || $this->isFixedWidth($context) ? $this->maxWidth : 9) + Math::max($this->subsequentWidth, 0);
     $total = 0;
     $totalBig = null;
     $pos = $position;
     for ($pass = 0; $pass < 2; $pass++) {
         $maxEndPos = Math::min($pos + $effMaxWidth, $length);
         while ($pos < $maxEndPos) {
             $ch = $text[$pos++];
             $digit = $context->getDecimalStyle()->convertToDigit($ch);
             if ($digit < 0) {
                 $pos--;
                 if ($pos < $minEndPos) {
                     return ~$position;
                     // need at least min width digits
                 }
                 break;
             }
             if ($pos - $position > 18) {
                 if ($totalBig === null) {
                     $totalBig = \gmp_init($total);
                 }
                 $totalBig = \gmp_add(\gmp_mul($totalBig, "10"), \gmp_init($digit));
             } else {
                 $total = $total * 10 + $digit;
             }
         }
         if ($this->subsequentWidth > 0 && $pass === 0) {
             // re-parse now we know the correct width
             $parseLen = $pos - $position;
             $effMaxWidth = Math::max($effMinWidth, $parseLen - $this->subsequentWidth);
             $pos = $position;
             $total = 0;
             $totalBig = null;
         } else {
             break;
         }
     }
     if ($negative) {
         if ($totalBig !== null) {
             if (\gmp_cmp($totalBig, "0") === 0 && $context->isStrict()) {
                 return ~($position - 1);
                 // minus zero not allowed
             }
             $totalBig = \gmp_neg($totalBig);
         } else {
             if ($total === 0 && $context->isStrict()) {
                 return ~($position - 1);
                 // minus zero not allowed
             }
             $total = -$total;
         }
     } else {
         if ($this->signStyle == SignStyle::EXCEEDS_PAD() && $context->isStrict()) {
             $parseLen = $pos - $position;
             if ($positive) {
                 if ($parseLen <= $this->minWidth) {
                     return ~($position - 1);
                     // '+' only parsed if minWidth exceeded
                 }
             } else {
                 if ($parseLen > $this->minWidth) {
                     return ~$position;
                     // '+' must be parsed if minWidth exceeded
                 }
             }
         }
     }
     if ($totalBig !== null) {
         if (gmp_cmp($totalBig, "-9223372036854775808") < 0 || gmp_cmp($totalBig, "9223372036854775807") > 0) {
             // overflow, parse 1 less digit
             $totalBig = gmp_div($totalBig, "10");
             $pos--;
         }
         return $this->setValue($context, gmp_intval($totalBig), $position, $pos);
     }
     return $this->setValue($context, $total, $position, $pos);
 }
Example #2
0
 /**
  * Resolves the date, resolving days past the end of month.
  *
  * @param int $year the year to represent, validated from MIN_YEAR to MAX_YEAR
  * @param int $month the month-of-year to represent, validated from 1 to 12
  * @param int $day the day-of-month to represent, validated from 1 to 31
  * @return LocalDate the resolved date, not null
  */
 private static function resolvePreviousValid($year, $month, $day)
 {
     switch ($month) {
         case 2:
             $day = Math::min($day, IsoChronology::INSTANCE()->isLeapYear($year) ? 29 : 28);
             break;
         case 4:
         case 6:
         case 9:
         case 11:
             $day = Math::min($day, 30);
             break;
     }
     return new LocalDate($year, $month, $day);
 }
Example #3
0
 function resolveYMD(FieldValues $fieldValues, ResolverStyle $resolverStyle)
 {
     $y = CF::YEAR()->checkValidIntValue($fieldValues->remove(CF::YEAR()));
     if ($resolverStyle == ResolverStyle::LENIENT()) {
         $months = Math::subtractExact($fieldValues->remove(CF::MONTH_OF_YEAR()), 1);
         $days = Math::subtractExact($fieldValues->remove(CF::DAY_OF_MONTH()), 1);
         return LocalDate::of($y, 1, 1)->plusMonths($months)->plusDays($days);
     }
     $moy = CF::MONTH_OF_YEAR()->checkValidIntValue($fieldValues->remove(CF::MONTH_OF_YEAR()));
     $dom = CF::DAY_OF_MONTH()->checkValidIntValue($fieldValues->remove(CF::DAY_OF_MONTH()));
     if ($resolverStyle == ResolverStyle::SMART()) {
         // previous valid
         if ($moy == 4 || $moy == 6 || $moy == 9 || $moy == 11) {
             $dom = Math::min($dom, 30);
         } else {
             if ($moy == 2) {
                 $dom = Math::min($dom, Month::FEBRUARY()->length(Year::isLeapYear($y)));
             }
         }
     }
     return LocalDate::of($y, $moy, $dom);
 }
 public function parse(DateTimeParseContext $context, $text, $position)
 {
     $effectiveMin = $context->isStrict() ? $this->minWidth : 0;
     $effectiveMax = $context->isStrict() ? $this->maxWidth : 9;
     $length = strlen($text);
     if ($position === $length) {
         // valid if whole field is optional, invalid if minimum width
         return $effectiveMin > 0 ? ~$position : $position;
     }
     if ($this->decimalPoint) {
         if ($text[$position] != $context->getDecimalStyle()->getDecimalSeparator()) {
             // valid if whole field is optional, invalid if minimum width
             return $effectiveMin > 0 ? ~$position : $position;
         }
         $position++;
     }
     $minEndPos = $position + $effectiveMin;
     if ($minEndPos > $length) {
         return ~$position;
         // need at least min width digits
     }
     $maxEndPos = Math::min($position + $effectiveMax, $length);
     $total = 0;
     // can use int because we are only parsing up to 9 digits
     $pos = $position;
     while ($pos < $maxEndPos) {
         $ch = $text[$pos++];
         $digit = $context->getDecimalStyle()->convertToDigit($ch);
         if ($digit < 0) {
             if ($pos < $minEndPos) {
                 return ~$position;
                 // need at least min width digits
             }
             $pos--;
             break;
         }
         $total = $total * 10 + $digit;
     }
     $div = 1 . str_repeat('0', 9 - ($pos - $position));
     $fraction = gmp_mul($total, $div);
     $value = $this->convertFromFraction($fraction);
     return $context->setParsedField($this->field, $value, $position, $pos);
 }
 /**
  * @dataProvider data_weekFields
  */
 public function test_withWeekOfWeekBasedYear(DayOfWeek $firstDayOfWeek, $minDays)
 {
     $day = LocalDate::of(2012, 12, 31);
     $week = WeekFields::of($firstDayOfWeek, $minDays);
     $dowField = $week->dayOfWeek();
     $wowbyField = $week->weekOfWeekBasedYear();
     $yowbyField = $week->weekBasedYear();
     $dowExpected = ($day->get($dowField) - 1) % 7 + 1;
     $dowDate = $day->with($dowField, $dowExpected);
     $dowResult = $dowDate->get($dowField);
     $this->assertEquals($dowResult, $dowExpected, "Localized DayOfWeek not correct; " . $day . " -->" . $dowDate);
     $weekExpected = $day->get($wowbyField) + 1;
     $range = $day->range($wowbyField);
     $weekExpected = ($weekExpected - 1) % (int) $range->getMaximum() + 1;
     $weekDate = $day->with($wowbyField, $weekExpected);
     $weekResult = $weekDate->get($wowbyField);
     $this->assertEquals($weekResult, $weekExpected, "Localized WeekOfWeekBasedYear not correct; " . $day . " -->" . $weekDate);
     $yearExpected = $day->get($yowbyField) + 1;
     $yearDate = $day->with($yowbyField, $yearExpected);
     $yearResult = $yearDate->get($yowbyField);
     $this->assertEquals($yearResult, $yearExpected, "Localized WeekBasedYear not correct; " . $day . " --> " . $yearDate);
     $range = $yearDate->range($wowbyField);
     $weekExpected = Math::min($day->get($wowbyField), $range->getMaximum());
     $weekActual = $yearDate->get($wowbyField);
     $this->assertEquals($weekActual, $weekExpected, "Localized WeekOfWeekBasedYear week should not change; " . $day . " --> " . $yearDate . ", actual: " . $weekActual . ", weekExpected: " . $weekExpected);
 }
Example #6
0
 /**
  * Adjusts the specified temporal object to have this month-day.
  * <p>
  * This returns a temporal object of the same observable type as the input
  * with the month and day-of-month changed to be the same as this.
  * <p>
  * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
  * twice, passing {@link ChronoField#MONTH_OF_YEAR} and
  * {@link ChronoField#DAY_OF_MONTH} as the fields.
  * If the specified temporal object does not use the ISO calendar system then
  * a {@code DateTimeException} is thrown.
  * <p>
  * In most cases, it is clearer to reverse the calling pattern by using
  * {@link Temporal#with(TemporalAdjuster)}:
  * <pre>
  *   // these two lines are equivalent, but the second approach is recommended
  *   temporal = thisMonthDay.adjustInto(temporal);
  *   temporal = temporal.with(thisMonthDay);
  * </pre>
  * <p>
  * This instance is immutable and unaffected by this method call.
  *
  * @param Temporal $temporal the target object to be adjusted, not null
  * @return Temporal the adjusted object, not null
  * @throws DateTimeException if unable to make the adjustment
  * @throws ArithmeticException if numeric overflow occurs
  */
 public function adjustInto(Temporal $temporal)
 {
     if (AbstractChronology::from($temporal)->equals(IsoChronology::INSTANCE()) == false) {
         throw new DateTimeException("Adjustment only supported on ISO date-time");
     }
     $temporal = $temporal->with(ChronoField::MONTH_OF_YEAR(), $this->month);
     return $temporal->with(ChronoField::DAY_OF_MONTH(), Math::min($temporal->range(ChronoField::DAY_OF_MONTH())->getMaximum(), $this->day));
 }
 /**
  * Return a new week-based-year date of the Chronology, year, week-of-year,
  * and dow of week.
  * @param Chronology $chrono The chronology of the new date
  * @param int $yowby the year of the week-based-year
  * @param int $wowby the week of the week-based-year
  * @param int $dow the day of the week
  * @return ChronoLocalDate a ChronoLocalDate for the requested year, week of year, and day of week
  */
 private function ofWeekBasedYear(Chronology $chrono, $yowby, $wowby, $dow)
 {
     $date = $chrono->date($yowby, 1, 1);
     $ldow = $this->localizedDayOfWeek($date);
     $offset = $this->startOfWeekOffset(1, $ldow);
     // Clamp the week of year to keep it in the same year
     $yearLen = $date->lengthOfYear();
     $newYearWeek = $this->computeWeek($offset, $yearLen + $this->weekDef->getMinimalDaysInFirstWeek());
     $wowby = Math::min($wowby, $newYearWeek - 1);
     $days = -$offset + ($dow - 1) + ($wowby - 1) * 7;
     return $date->plus($days, ChronoUnit::DAYS());
 }