public function format(DateTimePrintContext $context, &$buf) { $value = $context->getValueField($this->field); if ($value === null) { return false; } $decimalStyle = $context->getDecimalStyle(); $fraction = $this->convertToFraction($value); if (gmp_cmp($fraction, 0) === 0) { // scale is zero if value is zero if ($this->minWidth > 0) { if ($this->decimalPoint) { $buf .= $decimalStyle->getDecimalSeparator(); } for ($i = 0; $i < $this->minWidth; $i++) { $buf .= $decimalStyle->getZeroDigit(); } } } else { $outputScale = Math::min(Math::max(9, $this->minWidth), $this->maxWidth); if ($outputScale !== 9) { $div = 1 . str_repeat('0', 9 - $outputScale); $fraction = gmp_div($fraction, $div); } $str = gmp_strval($fraction); $pad = $outputScale - strlen($str); $str = str_repeat('0', $pad) . $str; // trim trailing zeros while (strlen($str) > $this->minWidth && $str[strlen($str) - 1] === '0') { $str = substr($str, 0, strlen($str) - 1); } $str = $decimalStyle->convertNumberToI18N($str); if ($this->decimalPoint) { $buf .= $decimalStyle->getDecimalSeparator(); } $buf .= $str; } return true; }
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); }
/** * Adds rules to make the last rules all start from the same year. * Also add one more year to avoid weird case where penultimate year has odd offset. * * @param int $windowStartYear the window start year * @throws \LogicException if there is only one rule defined as being forever */ function tidy($windowStartYear) { if (count($this->lastRuleList) === 1) { throw new \LogicException("Cannot have only one rule defined as being forever"); } // handle last rules if ($this->windowEnd->equals(LocalDateTime::MAX())) { // setup at least one real rule, which closes off other windows nicely $this->maxLastRuleStartYear = Math::max($this->maxLastRuleStartYear, $windowStartYear) + 1; foreach ($this->lastRuleList as $lastRule) { $this->addRule($lastRule->year, $this->maxLastRuleStartYear, $lastRule->month, $lastRule->dayOfMonthIndicator, $lastRule->dayOfWeek, $lastRule->time, $lastRule->timeEndOfDay, $lastRule->timeDefinition, $lastRule->savingAmountSecs); $lastRule->year = $this->maxLastRuleStartYear + 1; } if ($this->maxLastRuleStartYear == Year::MAX_VALUE) { $this->lastRuleList = []; } else { $this->maxLastRuleStartYear++; } } else { // convert all within the endYear limit $endYear = $this->windowEnd->getYear(); foreach ($this->lastRuleList as $lastRule) { $this->addRule($lastRule->year, $endYear + 1, $lastRule->month, $lastRule->dayOfMonthIndicator, $lastRule->dayOfWeek, $lastRule->time, $lastRule->timeEndOfDay, $lastRule->timeDefinition, $lastRule->savingAmountSecs); } $this->lastRuleList = []; $this->maxLastRuleStartYear = Year::MAX_VALUE; } // ensure lists are sorted usort($this->ruleList, [TZRule::class, 'compareTo']); usort($this->lastRuleList, [TZRule::class, 'compareTo']); // default fixed savings to zero if (count($this->ruleList) === 0 && $this->fixedSavingAmountSecs === null) { $this->fixedSavingAmountSecs = 0; } }