From a976c4508378bf12a7eb322a483ed26bf2d57e7d Mon Sep 17 00:00:00 2001 From: ignace nyamagana butera Date: Thu, 30 Jul 2026 08:15:30 +0200 Subject: [PATCH] Add Polyfill for Time\Duration class --- .gitignore | 2 + README.md | 2 + composer.json | 2 + splitsh.json | 1 + src/Time/LICENSE | 19 + src/Time/README.md | 21 + src/Time/Resources/stubs/Time/Duration.php | 489 ++++++++++++++++++ .../Resources/stubs/Time/TimeException.php | 18 + src/Time/composer.json | 36 ++ tests/Time/DurationTest.php | 467 +++++++++++++++++ tests/Time/PhptTest.php | 148 ++++++ tests/Time/phpt/clone.phpt | 24 + tests/Time/phpt/helper.inc | 32 ++ tests/Time/phpt/methods/absolute.phpt | 29 ++ tests/Time/phpt/methods/add.phpt | 371 +++++++++++++ tests/Time/phpt/methods/add_32.phpt | 23 + tests/Time/phpt/methods/add_64.phpt | 23 + tests/Time/phpt/methods/compare.phpt | 369 +++++++++++++ tests/Time/phpt/methods/divideBy.phpt | 133 +++++ tests/Time/phpt/methods/divideBy_64.phpt | 78 +++ tests/Time/phpt/methods/fromHours.phpt | 23 + tests/Time/phpt/methods/fromHours_32.phpt | 23 + tests/Time/phpt/methods/fromHours_64.phpt | 23 + .../methods/fromIso8601DurationString.phpt | 74 +++ .../methods/fromIso8601DurationString_64.phpt | 32 ++ tests/Time/phpt/methods/fromMicroseconds.phpt | 27 + .../phpt/methods/fromMicroseconds_64.phpt | 23 + tests/Time/phpt/methods/fromMilliseconds.phpt | 27 + .../phpt/methods/fromMilliseconds_64.phpt | 23 + tests/Time/phpt/methods/fromMinutes.phpt | 23 + tests/Time/phpt/methods/fromMinutes_32.phpt | 23 + tests/Time/phpt/methods/fromMinutes_64.phpt | 23 + tests/Time/phpt/methods/fromNanoseconds.phpt | 27 + .../Time/phpt/methods/fromNanoseconds_64.phpt | 23 + tests/Time/phpt/methods/fromSeconds.phpt | 46 ++ tests/Time/phpt/methods/fromSeconds_64.phpt | 25 + tests/Time/phpt/methods/multiplyBy.phpt | 206 ++++++++ tests/Time/phpt/methods/multiplyBy_32.phpt | 55 ++ tests/Time/phpt/methods/multiplyBy_64.phpt | 70 +++ tests/Time/phpt/methods/negate.phpt | 29 ++ tests/Time/phpt/methods/sub.phpt | 371 +++++++++++++ tests/Time/phpt/methods/sub_32.phpt | 23 + tests/Time/phpt/methods/sub_64.phpt | 23 + 43 files changed, 3529 insertions(+) create mode 100644 src/Time/LICENSE create mode 100644 src/Time/README.md create mode 100644 src/Time/Resources/stubs/Time/Duration.php create mode 100644 src/Time/Resources/stubs/Time/TimeException.php create mode 100644 src/Time/composer.json create mode 100644 tests/Time/DurationTest.php create mode 100644 tests/Time/PhptTest.php create mode 100644 tests/Time/phpt/clone.phpt create mode 100644 tests/Time/phpt/helper.inc create mode 100644 tests/Time/phpt/methods/absolute.phpt create mode 100644 tests/Time/phpt/methods/add.phpt create mode 100644 tests/Time/phpt/methods/add_32.phpt create mode 100644 tests/Time/phpt/methods/add_64.phpt create mode 100644 tests/Time/phpt/methods/compare.phpt create mode 100644 tests/Time/phpt/methods/divideBy.phpt create mode 100644 tests/Time/phpt/methods/divideBy_64.phpt create mode 100644 tests/Time/phpt/methods/fromHours.phpt create mode 100644 tests/Time/phpt/methods/fromHours_32.phpt create mode 100644 tests/Time/phpt/methods/fromHours_64.phpt create mode 100644 tests/Time/phpt/methods/fromIso8601DurationString.phpt create mode 100644 tests/Time/phpt/methods/fromIso8601DurationString_64.phpt create mode 100644 tests/Time/phpt/methods/fromMicroseconds.phpt create mode 100644 tests/Time/phpt/methods/fromMicroseconds_64.phpt create mode 100644 tests/Time/phpt/methods/fromMilliseconds.phpt create mode 100644 tests/Time/phpt/methods/fromMilliseconds_64.phpt create mode 100644 tests/Time/phpt/methods/fromMinutes.phpt create mode 100644 tests/Time/phpt/methods/fromMinutes_32.phpt create mode 100644 tests/Time/phpt/methods/fromMinutes_64.phpt create mode 100644 tests/Time/phpt/methods/fromNanoseconds.phpt create mode 100644 tests/Time/phpt/methods/fromNanoseconds_64.phpt create mode 100644 tests/Time/phpt/methods/fromSeconds.phpt create mode 100644 tests/Time/phpt/methods/fromSeconds_64.phpt create mode 100644 tests/Time/phpt/methods/multiplyBy.phpt create mode 100644 tests/Time/phpt/methods/multiplyBy_32.phpt create mode 100644 tests/Time/phpt/methods/multiplyBy_64.phpt create mode 100644 tests/Time/phpt/methods/negate.phpt create mode 100644 tests/Time/phpt/methods/sub.phpt create mode 100644 tests/Time/phpt/methods/sub_32.phpt create mode 100644 tests/Time/phpt/methods/sub_64.phpt diff --git a/.gitignore b/.gitignore index 2f223d91..cedd6871 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ phpunit.xml .phpunit vendor/ /tests/Io/Poll/phpt/*.php +/tests/Time/phpt/*.php +/tests/Time/phpt/methods/*.php /tests/unicode diff --git a/README.md b/README.md index 2cd8a8b6..bb17fe31 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,7 @@ Polyfills are provided for: - the `SortDirection` enum introduced in PHP 8.6; - the `grapheme_strrev` function introduced in PHP 8.6; - the `Io\Poll` API and `StreamPollHandle` introduced in PHP 8.6 (requires PHP >= 8.1; only the `Poll` backend is available); +- the `Time\Duration` class introduced in PHP 8.6 (requires PHP >= 8.1); It is strongly recommended to upgrade your PHP version and/or install the missing extensions whenever possible. This polyfill should be used only when there is no @@ -173,6 +174,7 @@ should **not** `require` the `symfony/polyfill` package, but the standalone ones - `symfony/polyfill-intl-normalizer` for using the intl normalizer, - `symfony/polyfill-io-poll` for using the `Io\Poll` API and `StreamPollHandle`, - `symfony/polyfill-mbstring` for using the mbstring functions, +- `symfony/polyfill-time` for using the `Time` API, - `symfony/polyfill-util` for using the polyfill utility helpers. - `symfony/polyfill-uuid` for using the `uuid_*` functions, diff --git a/composer.json b/composer.json index 0da25fe3..24a892dc 100644 --- a/composer.json +++ b/composer.json @@ -44,6 +44,7 @@ "symfony/polyfill-intl-normalizer": "self.version", "symfony/polyfill-io-poll": "self.version", "symfony/polyfill-mbstring": "self.version", + "symfony/polyfill-time": "self.version", "symfony/polyfill-util": "self.version", "symfony/polyfill-uuid": "self.version" }, @@ -69,6 +70,7 @@ "src/Intl/MessageFormatter/Resources/stubs", "src/Intl/Normalizer/Resources/stubs", "src/Io/Poll/Resources/stubs", + "src/Time/Resources/stubs", "src/Php86/Resources/stubs", "src/Php85/Resources/stubs", "src/Php84/Resources/stubs", diff --git a/splitsh.json b/splitsh.json index 57c9e56b..0bcda63a 100644 --- a/splitsh.json +++ b/splitsh.json @@ -20,6 +20,7 @@ "polyfill-intl-messageformatter": "src/Intl/MessageFormatter", "polyfill-intl-normalizer": "src/Intl/Normalizer", "polyfill-mbstring": "src/Mbstring", + "polyfill-time": "src/Time", "polyfill-util": "src/Util", "polyfill-uuid": "src/Uuid" }, diff --git a/src/Time/LICENSE b/src/Time/LICENSE new file mode 100644 index 00000000..36d6fdc3 --- /dev/null +++ b/src/Time/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2026-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/Time/README.md b/src/Time/README.md new file mode 100644 index 00000000..73efc265 --- /dev/null +++ b/src/Time/README.md @@ -0,0 +1,21 @@ +Symfony Polyfill / Time +======================= + +This component provides the [`Time` API](https://wiki.php.net/rfc/duration_class) +added to PHP 8.6 core, for PHP >= 8.1: + +- `Time\TimeException`, `Time\Duration` + +The phpt tests of the native implementation, borrowed from php-src, run against +the polyfill as part of the test suite. One divergence remains: `Duration` is a +plain final class with readonly properties instead of a `readonly` class, so +`ReflectionClass::newInstanceWithoutConstructor()` succeeds where the native +class rejects it. Dynamic properties are rejected as they are natively. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/src/Time/Resources/stubs/Time/Duration.php b/src/Time/Resources/stubs/Time/Duration.php new file mode 100644 index 00000000..47f01b10 --- /dev/null +++ b/src/Time/Resources/stubs/Time/Duration.php @@ -0,0 +1,489 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Time; + +if (\PHP_VERSION_ID < 80600) { + /** + * @see https://wiki.php.net/rfc/duration_class + */ + final class Duration + { + private const NANOS_PER_SECOND = 1_000_000_000; + + /** + * Maximum seconds accepted by Duration. + * + * One second is reserved to avoid overflowing when arithmetic operations + * temporarily require carrying nanoseconds into the seconds component. + */ + private const MAX_SECONDS = \PHP_INT_SIZE >= 8 ? 9_223_372_035 : \PHP_INT_MAX; + + private const RANGE_ERROR = \PHP_INT_SIZE >= 8 + ? 'The maximum representable range is 9_223_372_035 seconds (roughly 292 years)' + : 'The maximum representable range is 2_147_483_647 seconds (roughly 68 years)'; + + /** + * Regular expression to parse an ISO-8601 duration string. + * + * Year, Month, Week and Day components are intentionally not supported. + * The biggest supported component is Hour. + */ + private const REGEXP_ISO8601 = '@^ + PT + (?=(?:\d+H|\d+M|\d+S)) # look ahead to avoid PT without argument + (?:(?\d+)H)? + (?:(?\d+)M)? + (?:(?\d+)S)? + $@x'; + + /** + * Regular expression matching any ISO-8601 duration, including the + * date components that Duration does not support. + */ + private const REGEXP_ISO8601_ANY = '@^ + P + (?:(?\d+)Y)? + (?:(?\d+)M)? + (?:(?\d+)W)? + (?:(?\d+)D)? + (?:T(?:\d+H)?(?:\d+M)?(?:\d+S)?)? + $@x'; + + private function __construct( + public readonly int $seconds, + public readonly int $nanoseconds, + public readonly bool $negative, + ) {} + + /** + * Create a duration representing $seconds seconds and $nanoseconds nanoseconds. Neither parameter + * may be negative. $nanoseconds must be less than 1_000_000_000 (the number of nanoseconds in a + * second). + * + * This constructor creates a Duration from its “atomic” components. + * + * @throws TimeException + */ + public static function fromSeconds(int $seconds, int $nanoseconds = 0): self + { + if ($seconds < 0) { + throw new \ValueError(__METHOD__.'(): Argument #1 ($seconds) must be greater than or equal to 0'); + } + + if ($nanoseconds < 0) { + throw new \ValueError(__METHOD__.'(): Argument #2 ($nanoseconds) must be greater than or equal to 0'); + } + + if ($nanoseconds >= self::NANOS_PER_SECOND) { + throw new \ValueError(__METHOD__.'(): Argument #2 ($nanoseconds) must be less than 1_000_000_000'); + } + + return self::create($seconds, $nanoseconds, false); + } + + /** + * Create a duration representing $nanoseconds nanoseconds. $nanoseconds must not be negative. + * + * @throws TimeException + */ + public static function fromNanoseconds(int $nanoseconds): self + { + if ($nanoseconds < 0) { + throw new \ValueError(__METHOD__.'(): Argument #1 ($nanoseconds) must be greater than or equal to 0'); + } + + return self::create(intdiv($nanoseconds, self::NANOS_PER_SECOND), $nanoseconds % self::NANOS_PER_SECOND, false); + } + + /** + * Create a duration representing $microseconds microseconds. $microseconds must not be negative. + * + * @throws TimeException + */ + public static function fromMicroseconds(int $microseconds): self + { + if ($microseconds < 0) { + throw new \ValueError(__METHOD__.'(): Argument #1 ($microseconds) must be greater than or equal to 0'); + } + + return self::create(intdiv($microseconds, 1_000_000), ($microseconds % 1_000_000) * 1_000, false); + } + + /** + * Create a duration representing $milliseconds milliseconds. $milliseconds must not be negative. + * + * @throws TimeException + */ + public static function fromMilliseconds(int $milliseconds): self + { + if ($milliseconds < 0) { + throw new \ValueError(__METHOD__.'(): Argument #1 ($milliseconds) must be greater than or equal to 0'); + } + + return self::create(intdiv($milliseconds, 1_000), ($milliseconds % 1_000) * 1_000_000, false); + } + + /** + * Create a duration representing $minutes minutes. $minutes must not be negative. + * + * @throws TimeException + */ + public static function fromMinutes(int $minutes): self + { + if ($minutes < 0) { + throw new \ValueError(__METHOD__.'(): Argument #1 ($minutes) must be greater than or equal to 0'); + } + + if ($minutes > intdiv(self::MAX_SECONDS, 60)) { + throw new TimeException(self::RANGE_ERROR); + } + + return self::create($minutes * 60, 0, false); + } + + /** + * Create a duration representing $hours hours. $hours must not be negative. + * + * @throws TimeException + */ + public static function fromHours(int $hours): self + { + if ($hours < 0) { + throw new \ValueError(__METHOD__.'(): Argument #1 ($hours) must be greater than or equal to 0'); + } + + if ($hours > intdiv(self::MAX_SECONDS, 3_600)) { + throw new TimeException(self::RANGE_ERROR); + } + + return self::create($hours * 3_600, 0, false); + } + + /** + * Parse a ISO-8601 period. ISO-8601 periods with a date component will be rejected. + * The biggest allowed component is H. + * + * @throws TimeException + */ + public static function fromIso8601DurationString(string $specification): self + { + if (!preg_match(self::REGEXP_ISO8601, $specification, $parts)) { + throw new TimeException(self::iso8601Error($specification)); + } + + $hours = (int) ($parts['hour'] ?? 0); + $minutes = (int) ($parts['minute'] ?? 0); + $seconds = (int) ($parts['second'] ?? 0); + + // each step is checked against the maximum so that no intermediate value can overflow + if ($hours > intdiv(self::MAX_SECONDS, 3_600)) { + throw new TimeException(self::RANGE_ERROR); + } + + $total = $hours * 3_600; + + if ($minutes > intdiv(self::MAX_SECONDS - $total, 60)) { + throw new TimeException(self::RANGE_ERROR); + } + + $total += $minutes * 60; + + if ($seconds > self::MAX_SECONDS - $total) { + throw new TimeException(self::RANGE_ERROR); + } + + return self::create($total + $seconds, 0, false); + } + + /** + * Negates the duration. + * + * @return self -$this + */ + public function negate(): self + { + if (0 === $this->seconds && 0 === $this->nanoseconds) { + return $this; + } + + return new self($this->seconds, $this->nanoseconds, !$this->negative); + } + + /** + * Returns the absolute value of the duration. + * + * @return self abs($this) + */ + public function absolute(): self + { + if (!$this->negative) { + return $this; + } + + return new self($this->seconds, $this->nanoseconds, false); + } + + /** + * Add the given duration to the duration. + * + * @return self $this + $duration + * + * @throws TimeException + */ + public function add(self $duration): self + { + $seconds = $this->negative ? -$this->seconds : $this->seconds; + $nanoseconds = $this->negative ? -$this->nanoseconds : $this->nanoseconds; + $addSeconds = $duration->negative ? -$duration->seconds : $duration->seconds; + $addNanoseconds = $duration->negative ? -$duration->nanoseconds : $duration->nanoseconds; + + if ($this->negative === $duration->negative && $this->seconds > self::MAX_SECONDS - $duration->seconds) { + throw new TimeException(self::RANGE_ERROR); + } + + $seconds += $addSeconds; + $nanoseconds += $addNanoseconds; + + if ($nanoseconds >= self::NANOS_PER_SECOND || $nanoseconds <= -self::NANOS_PER_SECOND) { + $carry = intdiv($nanoseconds, self::NANOS_PER_SECOND); + $nanoseconds %= self::NANOS_PER_SECOND; + + if ($carry > 0 ? $seconds > self::MAX_SECONDS - $carry : $seconds < -self::MAX_SECONDS - $carry) { + throw new TimeException(self::RANGE_ERROR); + } + + $seconds += $carry; + } + + if (0 < $seconds && 0 > $nanoseconds) { + --$seconds; + $nanoseconds += self::NANOS_PER_SECOND; + } + + if (0 > $seconds && 0 < $nanoseconds) { + ++$seconds; + $nanoseconds -= self::NANOS_PER_SECOND; + } + + return self::create(abs($seconds), abs($nanoseconds), 0 > $seconds || 0 > $nanoseconds); + } + + /** + * Subtract the given duration from the duration. + * + * @return self $this - $duration + * + * @throws TimeException + */ + public function sub(self $duration): self + { + return $this->add($duration->negate()); + } + + /** + * Multiply the length of the duration by the given factor. $factor must not be negative. + * + * @return self $this * $factor + * + * @throws TimeException + */ + public function multiplyBy(int $factor): self + { + if ($factor < 0) { + throw new \ValueError(__METHOD__.'(): Argument #1 ($factor) must be greater than or equal to 0'); + } + + if (0 === $factor) { + return new self(0, 0, false); + } + + if (1 === $factor) { + return $this; + } + + // double-and-add: multiplying the nanoseconds directly would overflow + $seconds = 0; + $nanoseconds = 0; + $addSeconds = $this->seconds; + $addNanoseconds = $this->nanoseconds; + + while (true) { + if (1 & $factor) { + [$seconds, $nanoseconds] = self::addMagnitudes($seconds, $nanoseconds, $addSeconds, $addNanoseconds); + } + + if (0 === $factor >>= 1) { + break; + } + + // the remaining factor is not zero, so an out of range double is out of range for the result too + [$addSeconds, $addNanoseconds] = self::addMagnitudes($addSeconds, $addNanoseconds, $addSeconds, $addNanoseconds); + } + + return self::create($seconds, $nanoseconds, $this->negative); + } + + /** + * Divide the length of the duration by the given divisor. $divisor must be positive. + * + * Fractional nanoseconds will be truncated. + * + * @return self $this / $divisor + * + * @throws TimeException + */ + public function divideBy(int $divisor): self + { + if ($divisor < 0) { + throw new \ValueError(__METHOD__.'(): Argument #1 ($divisor) must be greater than or equal to 0'); + } + + if (0 === $divisor) { + throw new \DivisionByZeroError('Division by zero'); + } + + if (1 === $divisor) { + return $this; + } + + $remainder = $this->seconds % $divisor; + + $nanoseconds = $remainder > intdiv(\PHP_INT_MAX - $this->nanoseconds, self::NANOS_PER_SECOND) + ? self::divideNanoseconds($remainder, $this->nanoseconds, $divisor) + : intdiv($this->nanoseconds + $remainder * self::NANOS_PER_SECOND, $divisor); + + return self::create(intdiv($this->seconds, $divisor), $nanoseconds, $this->negative); + } + + /** + * Returns -1, 0, 1 if $a is less than, equal to, or greater than $b respectively. + */ + public static function compare(self $a, self $b): int + { + if ($a->negative !== $b->negative) { + return $a->negative ? -1 : 1; + } + + $comparison = $a->seconds <=> $b->seconds ?: $a->nanoseconds <=> $b->nanoseconds; + + return $a->negative ? -$comparison : $comparison; + } + + /** + * The native class is declared readonly, which forbids dynamic properties. + */ + public function __set(string $name, mixed $value): void + { + throw new \Error(\sprintf('Cannot create dynamic property %s::$%s', self::class, $name)); + } + + /** + * Adds two non-negative (seconds, nanoseconds) pairs. + * + * @return array{0: int, 1: int} + * + * @throws TimeException when the sum is out of the representable range + */ + private static function addMagnitudes(int $seconds, int $nanoseconds, int $addSeconds, int $addNanoseconds): array + { + if ($seconds > self::MAX_SECONDS - $addSeconds) { + throw new TimeException(self::RANGE_ERROR); + } + + $seconds += $addSeconds; + // the sum of two nanoseconds components is always below 2**31 + $nanoseconds += $addNanoseconds; + + if ($nanoseconds >= self::NANOS_PER_SECOND) { + if (self::MAX_SECONDS === $seconds) { + throw new TimeException(self::RANGE_ERROR); + } + + ++$seconds; + $nanoseconds -= self::NANOS_PER_SECOND; + } + + return [$seconds, $nanoseconds]; + } + + /** + * Returns intdiv($remainder * 1_000_000_000 + $nanoseconds, $divisor) for + * a $remainder that is lower than $divisor. + * + * The dividend does not fit in an integer on 32 bit platforms, so the + * division is done one decimal digit at a time. Every intermediate value + * stays below 2**53, where floats represent integers exactly. + */ + private static function divideNanoseconds(int $remainder, int $nanoseconds, int $divisor): int + { + $quotient = 0; + $rest = (float) $remainder; + + foreach (str_split(str_pad((string) $nanoseconds, 9, '0', \STR_PAD_LEFT)) as $digit) { + $rest = $rest * 10 + (int) $digit; + $digit = (int) ($rest / $divisor); + $rest -= $digit * $divisor; + + // the float division may be off by one in either direction + while (0 > $rest) { + --$digit; + $rest += $divisor; + } + while ($rest >= $divisor) { + ++$digit; + $rest -= $divisor; + } + + $quotient = $quotient * 10 + $digit; + } + + return $quotient; + } + + /** + * @throws TimeException when the duration is out of the representable range + */ + private static function create(int $seconds, int $nanoseconds, bool $negative): self + { + if ($seconds > self::MAX_SECONDS) { + throw new TimeException(self::RANGE_ERROR); + } + + // a zero duration is never negative + return new self($seconds, $nanoseconds, $negative && (0 !== $seconds || 0 !== $nanoseconds)); + } + + /** + * Mirrors the error reporting of timelib_duration_create_from_iso8601string(). + */ + private static function iso8601Error(string $specification): string + { + if (preg_match(self::REGEXP_ISO8601_ANY, $specification, $parts)) { + if ((int) ($parts['year'] ?? 0) || (int) ($parts['month'] ?? 0) || (int) ($parts['week'] ?? 0) || (int) ($parts['day'] ?? 0)) { + return 'The ISO 8601 duration string may only contain the time (T) aspect'; + } + + return 'The ISO 8601 duration string could not be parsed'; + } + + if (str_contains($specification, '/')) { + return 'The ISO 8601 duration string may only contain the period (P) aspect'; + } + + if (!str_contains($specification, 'P') && false !== @strtotime($specification)) { + return 'The ISO 8601 duration string is missing the period (P) aspect'; + } + + return 'The ISO 8601 duration string could not be parsed'; + } + } +} diff --git a/src/Time/Resources/stubs/Time/TimeException.php b/src/Time/Resources/stubs/Time/TimeException.php new file mode 100644 index 00000000..b6e328a7 --- /dev/null +++ b/src/Time/Resources/stubs/Time/TimeException.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Time; + +if (\PHP_VERSION_ID < 80600) { + class TimeException extends \Exception + { + } +} diff --git a/src/Time/composer.json b/src/Time/composer.json new file mode 100644 index 00000000..f6fcb642 --- /dev/null +++ b/src/Time/composer.json @@ -0,0 +1,36 @@ +{ + "name": "symfony/polyfill-time", + "type": "library", + "description": "Symfony polyfill for the Time API added to PHP 8.6", + "keywords": ["polyfill", "shim", "compatibility", "portable", "time", "duration"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Ignace Philippe Nyamagana Butera", + "email": "nyamsprod@gmail.com" + }, + { + "name": "Tim Düsterhus", + "email": "tim@tideways-gmbh.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=8.1" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Time\\": "" }, + "classmap": [ "Resources/stubs" ] + }, + "minimum-stability": "dev", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/tests/Time/DurationTest.php b/tests/Time/DurationTest.php new file mode 100644 index 00000000..6f49ec94 --- /dev/null +++ b/tests/Time/DurationTest.php @@ -0,0 +1,467 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Tests\Time; + +use PHPUnit\Framework\TestCase; +use Time\Duration; +use Time\TimeException; + +/** + * These tests run against the native implementation as soon as PHP provides the + * Time extension, so that any divergence of the polyfill is caught. + * + * @requires PHP >= 8.1 + */ +class DurationTest extends TestCase +{ + protected function setUp(): void + { + if (!class_exists(Duration::class)) { + $this->markTestSkipped('The Time extension is not available and the polyfill is disabled on PHP >= 8.6.'); + } + } + + public function testFromSeconds() + { + $duration = Duration::fromSeconds(12, 345); + + $this->assertSame(12, $duration->seconds); + $this->assertSame(345, $duration->nanoseconds); + $this->assertFalse($duration->negative); + } + + /** + * @dataProvider provideInvalidSeconds + */ + public function testFromSecondsRejectsInvalidSeconds(int $seconds, string $expectedException) + { + $this->expectException($expectedException); + + Duration::fromSeconds($seconds); + } + + public static function provideInvalidSeconds(): array + { + $cases = ['negative value' => [-1, \ValueError::class]]; + + if (8 <= \PHP_INT_SIZE) { + $cases['too large value'] = [9223372036, TimeException::class]; + } + + return $cases; + } + + /** + * @dataProvider provideInvalidNanoseconds + */ + public function testFromSecondsRejectsInvalidNanoseconds(int $nanoseconds) + { + $this->expectException(\ValueError::class); + + Duration::fromSeconds(1, $nanoseconds); + } + + public static function provideInvalidNanoseconds(): array + { + return [ + 'negative value' => [-1], + 'too large value' => [1000000000], + ]; + } + + public function testFromNanoseconds() + { + $duration = Duration::fromNanoseconds(3500000123); + + $this->assertSame(3, $duration->seconds); + $this->assertSame(500000123, $duration->nanoseconds); + } + + public function testFromMicroseconds() + { + $duration = Duration::fromMicroseconds(2500001); + + $this->assertSame(2, $duration->seconds); + $this->assertSame(500001000, $duration->nanoseconds); + } + + public function testFromMilliseconds() + { + $duration = Duration::fromMilliseconds(1500); + + $this->assertSame(1, $duration->seconds); + $this->assertSame(500000000, $duration->nanoseconds); + } + + public function testFromMinutes() + { + $this->assertSame(120, Duration::fromMinutes(2)->seconds); + } + + public function testFromHours() + { + $this->assertSame(7200, Duration::fromHours(2)->seconds); + } + + /** + * @dataProvider provideOutOfRangeFactories + */ + public function testFactoriesRejectOutOfRangeValues(string $method, int $value) + { + if (8 > \PHP_INT_SIZE) { + $this->markTestSkipped('These values are out of range on 32 bit platforms only.'); + } + + $this->expectException(TimeException::class); + + Duration::$method($value); + } + + public static function provideOutOfRangeFactories(): array + { + return [ + 'nanoseconds' => ['fromNanoseconds', \PHP_INT_MAX], + 'microseconds' => ['fromMicroseconds', \PHP_INT_MAX], + 'microseconds just above the limit' => ['fromMicroseconds', 9223372036000000], + 'milliseconds' => ['fromMilliseconds', \PHP_INT_MAX], + 'minutes' => ['fromMinutes', \PHP_INT_MAX], + 'hours' => ['fromHours', \PHP_INT_MAX], + ]; + } + + /** + * @dataProvider provideValidIso8601 + */ + public function testParseIso8601(string $specification, int $seconds) + { + $duration = Duration::fromIso8601DurationString($specification); + + $this->assertSame($seconds, $duration->seconds); + $this->assertSame(0, $duration->nanoseconds); + $this->assertFalse($duration->negative); + } + + public static function provideValidIso8601(): array + { + return [ + '1 second' => ['PT1S', 1], + '2 minutes 30 seconds' => ['PT2M30S', 150], + 'zero seconds' => ['PT0S', 0], + 'zero minutes' => ['PT0M', 0], + 'zero hours' => ['PT0H', 0], + 'hours are the biggest component' => ['PT1H1M1S', 3661], + 'components are not normalized' => ['PT1H60M60S', 7260], + ]; + } + + /** + * @dataProvider provideInvalidIso8601 + */ + public function testRejectsInvalidIso8601(string $specification) + { + $this->expectException(TimeException::class); + + Duration::fromIso8601DurationString($specification); + } + + public static function provideInvalidIso8601(): array + { + return [ + 'empty string' => [''], + 'period only' => ['P'], + 'time designator only' => ['PT'], + 'not an ISO 8601 duration' => ['foo'], + 'invalid prefix' => ['1H'], + 'missing time designator' => ['P1H'], + 'unsupported years component' => ['P1Y'], + 'unsupported months component' => ['P1M'], + 'unsupported weeks component' => ['P1W'], + 'unsupported days component' => ['P1D'], + 'days with a time component' => ['P1DT0S'], + 'unknown time unit' => ['PT1X'], + 'trailing unit without value' => ['PT1HS'], + 'fractional seconds (dot)' => ['PT1.5S'], + 'fractional seconds (comma)' => ['PT1,5S'], + 'duplicate seconds component' => ['PT1S2S'], + 'negative duration' => ['-PT5S'], + 'date and time' => ['2000-01-01T00:00:00Z'], + 'interval' => ['2000-01-01T00:00:00Z/PT1H'], + 'hours component out of range' => ['PT2562047789H'], + 'combined components out of range' => ['PT2562047788H59M59S'], + ]; + } + + public function testNegate() + { + $duration = Duration::fromSeconds(10)->negate(); + + $this->assertTrue($duration->negative); + $this->assertSame(10, $duration->seconds); + $this->assertFalse($duration->negate()->negative); + } + + public function testNegateZeroStaysPositive() + { + $this->assertFalse(Duration::fromSeconds(0)->negate()->negative); + } + + public function testAbsolute() + { + $duration = Duration::fromSeconds(10)->negate()->absolute(); + + $this->assertFalse($duration->negative); + $this->assertSame(10, $duration->seconds); + } + + public function testAddition() + { + $result = Duration::fromSeconds(2, 900000000) + ->add(Duration::fromSeconds(1, 200000000)); + + $this->assertSame(4, $result->seconds); + $this->assertSame(100000000, $result->nanoseconds); + } + + public function testAdditionWithNegativeOperand() + { + $result = Duration::fromSeconds(10)->add(Duration::fromSeconds(3)->negate()); + + $this->assertSame(7, $result->seconds); + $this->assertFalse($result->negative); + } + + public function testSubtraction() + { + $result = Duration::fromSeconds(10)->sub(Duration::fromSeconds(3)); + + $this->assertSame(7, $result->seconds); + $this->assertFalse($result->negative); + } + + public function testSubtractionBorrow() + { + $result = Duration::fromSeconds(5)->sub(Duration::fromSeconds(2, 500000000)); + + $this->assertSame(2, $result->seconds); + $this->assertSame(500000000, $result->nanoseconds); + } + + public function testSubtractionProducesNegativeDuration() + { + $result = Duration::fromSeconds(2)->sub(Duration::fromSeconds(5)); + + $this->assertSame(3, $result->seconds); + $this->assertTrue($result->negative); + } + + public function testMultiplyBy() + { + $result = Duration::fromSeconds(1, 600000000)->multiplyBy(3); + + $this->assertSame(4, $result->seconds); + $this->assertSame(800000000, $result->nanoseconds); + } + + public function testMultiplyByZeroIsNeverNegative() + { + $result = Duration::fromSeconds(1)->negate()->multiplyBy(0); + + $this->assertSame(0, $result->seconds); + $this->assertFalse($result->negative); + } + + public function testMultiplyByRejectsNegativeFactor() + { + $this->expectException(\ValueError::class); + + Duration::fromSeconds(1)->multiplyBy(-1); + } + + public function testDivideBy() + { + $result = Duration::fromSeconds(5)->divideBy(2); + + $this->assertSame(2, $result->seconds); + $this->assertSame(500000000, $result->nanoseconds); + } + + public function testDivideByTruncatesFractionalNanoseconds() + { + $result = Duration::fromSeconds(0, 5)->divideBy(2); + + $this->assertSame(0, $result->seconds); + $this->assertSame(2, $result->nanoseconds); + } + + public function testDivideByTruncatingToZeroIsNeverNegative() + { + $result = Duration::fromSeconds(0, 1)->negate()->divideBy(2); + + $this->assertSame(0, $result->seconds); + $this->assertSame(0, $result->nanoseconds); + $this->assertFalse($result->negative); + } + + public function testDivideByZero() + { + $this->expectException(\DivisionByZeroError::class); + + Duration::fromSeconds(1)->divideBy(0); + } + + public function testDivideByRejectsNegativeDivisor() + { + $this->expectException(\ValueError::class); + + Duration::fromSeconds(1)->divideBy(-1); + } + + /** + * @dataProvider provideOverflowingOperations + */ + public function testArithmeticRejectsOutOfRangeResults(\Closure $operation) + { + if (8 > \PHP_INT_SIZE) { + $this->markTestSkipped('These values are out of range on 32 bit platforms only.'); + } + + $this->expectException(TimeException::class); + + $operation(); + } + + public static function provideOverflowingOperations(): array + { + if (8 > \PHP_INT_SIZE) { + return ['skipped on 32 bit platforms' => [static function () {}]]; + } + + $max = static function () { return Duration::fromSeconds(9223372035, 999999999); }; + + return [ + 'add' => [static function () use ($max) { return $max()->add(Duration::fromSeconds(0, 1)); }], + 'sub' => [static function () use ($max) { return $max()->sub(Duration::fromSeconds(0, 1)->negate()); }], + 'multiplyBy' => [static function () use ($max) { return $max()->multiplyBy(2); }], + 'multiplyBy nanoseconds only' => [static function () { return Duration::fromSeconds(0, 1)->multiplyBy(9223372036000000000); }], + ]; + } + + /** + * The digit-wise division is only reached on 32 bit platforms, where the dividend + * does not fit in an integer. Check it here against the integer arithmetic of a + * 64 bit platform, since no CI job runs on 32 bit. + */ + public function testDivideNanosecondsMatchesIntegerArithmetic() + { + if ((new \ReflectionClass(Duration::class))->isInternal()) { + $this->markTestSkipped('This checks an implementation detail of the polyfill.'); + } + if (8 > \PHP_INT_SIZE) { + $this->markTestSkipped('The expected values are computed with 64 bit integers.'); + } + + $divideNanoseconds = new \ReflectionMethod(Duration::class, 'divideNanoseconds'); + + foreach ([2, 3, 7, 10, 999999999, 1000000000, 1073741823, 2147483647] as $divisor) { + foreach ([0, 1, 2, 3, intdiv($divisor, 2), $divisor - 1] as $remainder) { + foreach ([0, 1, 5, 999999998, 999999999] as $nanoseconds) { + $this->assertSame( + intdiv($remainder * 1000000000 + $nanoseconds, $divisor), + $divideNanoseconds->invoke(null, $remainder, $nanoseconds, $divisor), + "intdiv($remainder * 1e9 + $nanoseconds, $divisor)" + ); + } + } + } + } + + public function testCompare() + { + $this->assertSame(0, Duration::compare(Duration::fromSeconds(1), Duration::fromSeconds(1))); + $this->assertSame(-1, Duration::compare(Duration::fromSeconds(1), Duration::fromSeconds(2))); + $this->assertSame(1, Duration::compare(Duration::fromSeconds(2), Duration::fromSeconds(1))); + $this->assertSame(-1, Duration::compare(Duration::fromSeconds(1, 0), Duration::fromSeconds(1, 1))); + } + + public function testCompareNegativeDurations() + { + $this->assertSame(-1, Duration::compare( + Duration::fromSeconds(2)->negate(), + Duration::fromSeconds(1)->negate() + )); + + $this->assertSame(-1, Duration::compare( + Duration::fromSeconds(1)->negate(), + Duration::fromSeconds(1) + )); + } + + public function testTheConstructorIsPrivate() + { + $this->expectException(\Error::class); + + new Duration(); + } + + public function testTheConstructorIsNotReachableThroughReflection() + { + $this->expectException(\ReflectionException::class); + + (new \ReflectionClass(Duration::class))->newInstance(); + } + + public function testDynamicPropertiesAreForbidden() + { + $duration = Duration::fromSeconds(1); + + $this->expectException(\Error::class); + + $duration->undefinedProperty = 1; + } + + /** + * The readonly.phpt of php-src cannot run here: it ends on + * ReflectionProperty::isWritable(), which is PHP 8.6 only, while the polyfill + * is disabled from that version on. This covers the rest of it. + */ + public function testPropertiesAreReadonly() + { + $duration = Duration::fromSeconds(1); + + // "modification allowed during cloning" must have no lasting effect + foreach ([$duration, clone $duration] as $subject) { + try { + $subject->seconds = 2; + $this->fail('Writing a readonly property should throw.'); + } catch (\Error $e) { + $this->assertSame('Cannot modify readonly property Time\Duration::$seconds', $e->getMessage()); + } + + $property = new \ReflectionProperty($subject, 'seconds'); + + foreach (['setValue', 'setRawValue', 'setRawValueWithoutLazyInitialization'] as $method) { + if (!method_exists($property, $method)) { + continue; + } + + try { + $property->$method($subject, 2); + $this->fail(\sprintf('ReflectionProperty::%s() should throw on a readonly property.', $method)); + } catch (\Error $e) { + $this->assertSame('Cannot modify readonly property Time\Duration::$seconds', $e->getMessage()); + } + } + + $this->assertSame(1, $subject->seconds); + } + } +} diff --git a/tests/Time/PhptTest.php b/tests/Time/PhptTest.php new file mode 100644 index 00000000..41974b8c --- /dev/null +++ b/tests/Time/PhptTest.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Tests\Time; + +use PHPUnit\Framework\TestCase; + +/** + * Runs the phpt tests of the native implementation, borrowed verbatim from + * php-src (ext/date/tests/time/duration/), against the polyfill. + * + * Two of them are not taken: new.phpt, because a userland class cannot reject + * ReflectionClass::newInstanceWithoutConstructor(), and readonly.phpt, because + * it ends on ReflectionProperty::isWritable(), added in PHP 8.6, while the + * polyfill is disabled from that version on. DurationTest covers the rest of + * their assertions. + * + * @requires PHP >= 8.1 + */ +class PhptTest extends TestCase +{ + protected function setUp(): void + { + if (\PHP_VERSION_ID >= 80600) { + $this->markTestSkipped('The Time\Duration polyfill is only used on PHP < 8.6.'); + } + } + + /** + * @dataProvider providePhptFiles + */ + public function testPhpt(string $file) + { + $sections = self::parse($file); + + if (isset($sections['SKIPIF'])) { + $output = ltrim($this->execute($file, $sections['SKIPIF'])); + if (0 === strpos($output, 'skip')) { + $this->markTestSkipped(trim(substr($output, 4))); + } + } + + $output = rtrim(str_replace("\r\n", "\n", $this->execute($file, $sections['FILE']))); + + if (isset($sections['EXPECTF'])) { + $expected = rtrim(str_replace("\r\n", "\n", $sections['EXPECTF'])); + if (!preg_match(self::expectfToRegex($expected), $output)) { + $this->assertSame($expected, $output); + } + $this->addToAssertionCount(1); + } else { + $expected = rtrim(str_replace("\r\n", "\n", $sections['EXPECT'])); + // object handles differ between native and polyfill runs + $normalize = static function (string $s) { + return preg_replace('/(object\([^)]++\))#\d+/', '$1', $s); + }; + $this->assertSame($normalize($expected), $normalize($output)); + } + } + + public static function providePhptFiles(): array + { + $baseDir = __DIR__.'/phpt'; + $files = []; + + foreach (glob($baseDir.'/{,*/}*.phpt', \GLOB_BRACE) as $file) { + $files[substr($file, 1 + strlen($baseDir), -5)] = [$file]; + } + + ksort($files); + + return $files; + } + + private static function parse(string $file): array + { + $sections = []; + $current = null; + foreach (file($file) as $line) { + if (preg_match('/^--([A-Z_]+)--\s*$/', $line, $m)) { + $current = $m[1]; + $sections[$current] = ''; + } elseif (null !== $current) { + $sections[$current] .= $line; + } + } + + return $sections; + } + + /** + * The code runs from a temporary file next to the phpt so that __DIR__ + * resolves helper.inc; the autoloader is injected via auto_prepend_file + * since the phpt files are unmodified copies. + */ + private function execute(string $phptFile, string $code): string + { + $phpFile = substr($phptFile, 0, -1); + file_put_contents($phpFile, $code); + + $proc = proc_open([ + \PHP_BINARY, + '-n', + '-d', 'error_reporting=-1', + '-d', 'display_errors=1', + '-d', 'auto_prepend_file='.\dirname(__DIR__, 2).'/vendor/autoload.php', + $phpFile, + ], [1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes); + + try { + $stdout = stream_get_contents($pipes[1]); + $stderr = stream_get_contents($pipes[2]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($proc); + } finally { + unlink($phpFile); + } + + return '' !== $stdout ? $stdout : $stderr; + } + + private static function expectfToRegex(string $expected): string + { + return '~^'.strtr(preg_quote($expected, '~'), [ + '%%' => '%', + '%e' => preg_quote(\DIRECTORY_SEPARATOR, '~'), + '%s' => '[^\r\n]+', + '%S' => '[^\r\n]*', + '%a' => '.+', + '%A' => '.*', + '%w' => '\s*', + '%i' => '[+-]?\d+', + '%d' => '\d+', + '%x' => '[0-9a-fA-F]+', + '%f' => '[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[Ee][+-]?\d+)?', + '%c' => '.', + ]).'$~s'; + } +} diff --git a/tests/Time/phpt/clone.phpt b/tests/Time/phpt/clone.phpt new file mode 100644 index 00000000..06ad23df --- /dev/null +++ b/tests/Time/phpt/clone.phpt @@ -0,0 +1,24 @@ +--TEST-- +Time\Duration: clone +--FILE-- +negate()), PHP_EOL; +echo fc(Time\Duration::fromSeconds(1, 1)->negate()), PHP_EOL; + +?> +--EXPECT-- + +0.000000000 + +0.000000001 + +1.000000001 + -0.000000001 + -1.000000001 diff --git a/tests/Time/phpt/helper.inc b/tests/Time/phpt/helper.inc new file mode 100644 index 00000000..e95f8a6b --- /dev/null +++ b/tests/Time/phpt/helper.inc @@ -0,0 +1,32 @@ +negative ? "-" : ($plus_sign ? "+" : " ")), $d->seconds, $d->nanoseconds); + if ($pad) { + $result = str_pad($result, 21, pad_type: STR_PAD_LEFT); + } + + return $result; +} + +function as_int(Time\Duration $d): int { + return ($d->negative ? -1 : 1) * ($d->seconds * 1_000_000_000 + $d->nanoseconds); +} + +function negate(Time\Duration $d): Time\Duration { + return $d->negate(); +} + +function is_negative(Time\Duration $d): bool { + return $d->negative; +} + +/** @return Time\Duration[] */ +function negate_all( + array $d /** @param Time\Duration[] */, +): array { + $negated = array_map(negate(...), $d); + + /* Filter out values that were not successfully negated (i.e. 0-values). */ + return array_filter($negated, is_negative(...)); +} diff --git a/tests/Time/phpt/methods/absolute.phpt b/tests/Time/phpt/methods/absolute.phpt new file mode 100644 index 00000000..86578488 --- /dev/null +++ b/tests/Time/phpt/methods/absolute.phpt @@ -0,0 +1,29 @@ +--TEST-- +Time\Duration::absolute() +--FILE-- +absolute()), PHP_EOL; +echo f(Time\Duration::fromSeconds(0, 0)->negate()->absolute()), PHP_EOL; + +echo f(Time\Duration::fromSeconds(1, 0)->absolute()), PHP_EOL; +echo f(Time\Duration::fromSeconds(1, 0)->negate()->absolute()), PHP_EOL; + +echo f(Time\Duration::fromSeconds(0, 1)->absolute()), PHP_EOL; +echo f(Time\Duration::fromSeconds(0, 1)->negate()->absolute()), PHP_EOL; + +echo f(Time\Duration::fromSeconds(1, 1)->absolute()), PHP_EOL; +echo f(Time\Duration::fromSeconds(1, 1)->negate()->absolute()), PHP_EOL; + +?> +--EXPECT-- + +0.000000000 + +0.000000000 + +1.000000000 + +1.000000000 + +0.000000001 + +0.000000001 + +1.000000001 + +1.000000001 diff --git a/tests/Time/phpt/methods/add.phpt b/tests/Time/phpt/methods/add.phpt new file mode 100644 index 00000000..825bd45d --- /dev/null +++ b/tests/Time/phpt/methods/add.phpt @@ -0,0 +1,371 @@ +--TEST-- +Time\Duration::add() +--FILE-- +add($b); + echo f($a, pad: false, plus_sign: false), " + ", f($b, pad: false, plus_sign: false), " = ", f($result, pad: false), PHP_EOL; + if (PHP_INT_SIZE != 4 && (as_int($a) + as_int($b) !== as_int($result))) { + throw new \Exception('Verification failed'); + } + } +} + +?> +--EXPECT-- +======== + 0.000000000 + 0.000000000 = +0.000000000 + 0.000000000 + 0.000000001 = +0.000000001 + 0.000000000 + 0.000000002 = +0.000000002 + 0.000000000 + 1.000000000 = +1.000000000 + 0.000000000 + 1.000000001 = +1.000000001 + 0.000000000 + 1.000000002 = +1.000000002 + 0.000000000 + 2.000000000 = +2.000000000 + 0.000000000 + 2.000000001 = +2.000000001 + 0.000000000 + 2.000000002 = +2.000000002 +==== + 0.000000000 + -0.000000001 = -0.000000001 + 0.000000000 + -0.000000002 = -0.000000002 + 0.000000000 + -1.000000000 = -1.000000000 + 0.000000000 + -1.000000001 = -1.000000001 + 0.000000000 + -1.000000002 = -1.000000002 + 0.000000000 + -2.000000000 = -2.000000000 + 0.000000000 + -2.000000001 = -2.000000001 + 0.000000000 + -2.000000002 = -2.000000002 +======== + 0.000000001 + 0.000000000 = +0.000000001 + 0.000000001 + 0.000000001 = +0.000000002 + 0.000000001 + 0.000000002 = +0.000000003 + 0.000000001 + 1.000000000 = +1.000000001 + 0.000000001 + 1.000000001 = +1.000000002 + 0.000000001 + 1.000000002 = +1.000000003 + 0.000000001 + 2.000000000 = +2.000000001 + 0.000000001 + 2.000000001 = +2.000000002 + 0.000000001 + 2.000000002 = +2.000000003 +==== + 0.000000001 + -0.000000001 = +0.000000000 + 0.000000001 + -0.000000002 = -0.000000001 + 0.000000001 + -1.000000000 = -0.999999999 + 0.000000001 + -1.000000001 = -1.000000000 + 0.000000001 + -1.000000002 = -1.000000001 + 0.000000001 + -2.000000000 = -1.999999999 + 0.000000001 + -2.000000001 = -2.000000000 + 0.000000001 + -2.000000002 = -2.000000001 +======== + 0.000000002 + 0.000000000 = +0.000000002 + 0.000000002 + 0.000000001 = +0.000000003 + 0.000000002 + 0.000000002 = +0.000000004 + 0.000000002 + 1.000000000 = +1.000000002 + 0.000000002 + 1.000000001 = +1.000000003 + 0.000000002 + 1.000000002 = +1.000000004 + 0.000000002 + 2.000000000 = +2.000000002 + 0.000000002 + 2.000000001 = +2.000000003 + 0.000000002 + 2.000000002 = +2.000000004 +==== + 0.000000002 + -0.000000001 = +0.000000001 + 0.000000002 + -0.000000002 = +0.000000000 + 0.000000002 + -1.000000000 = -0.999999998 + 0.000000002 + -1.000000001 = -0.999999999 + 0.000000002 + -1.000000002 = -1.000000000 + 0.000000002 + -2.000000000 = -1.999999998 + 0.000000002 + -2.000000001 = -1.999999999 + 0.000000002 + -2.000000002 = -2.000000000 +======== + 1.000000000 + 0.000000000 = +1.000000000 + 1.000000000 + 0.000000001 = +1.000000001 + 1.000000000 + 0.000000002 = +1.000000002 + 1.000000000 + 1.000000000 = +2.000000000 + 1.000000000 + 1.000000001 = +2.000000001 + 1.000000000 + 1.000000002 = +2.000000002 + 1.000000000 + 2.000000000 = +3.000000000 + 1.000000000 + 2.000000001 = +3.000000001 + 1.000000000 + 2.000000002 = +3.000000002 +==== + 1.000000000 + -0.000000001 = +0.999999999 + 1.000000000 + -0.000000002 = +0.999999998 + 1.000000000 + -1.000000000 = +0.000000000 + 1.000000000 + -1.000000001 = -0.000000001 + 1.000000000 + -1.000000002 = -0.000000002 + 1.000000000 + -2.000000000 = -1.000000000 + 1.000000000 + -2.000000001 = -1.000000001 + 1.000000000 + -2.000000002 = -1.000000002 +======== + 1.000000001 + 0.000000000 = +1.000000001 + 1.000000001 + 0.000000001 = +1.000000002 + 1.000000001 + 0.000000002 = +1.000000003 + 1.000000001 + 1.000000000 = +2.000000001 + 1.000000001 + 1.000000001 = +2.000000002 + 1.000000001 + 1.000000002 = +2.000000003 + 1.000000001 + 2.000000000 = +3.000000001 + 1.000000001 + 2.000000001 = +3.000000002 + 1.000000001 + 2.000000002 = +3.000000003 +==== + 1.000000001 + -0.000000001 = +1.000000000 + 1.000000001 + -0.000000002 = +0.999999999 + 1.000000001 + -1.000000000 = +0.000000001 + 1.000000001 + -1.000000001 = +0.000000000 + 1.000000001 + -1.000000002 = -0.000000001 + 1.000000001 + -2.000000000 = -0.999999999 + 1.000000001 + -2.000000001 = -1.000000000 + 1.000000001 + -2.000000002 = -1.000000001 +======== + 1.000000002 + 0.000000000 = +1.000000002 + 1.000000002 + 0.000000001 = +1.000000003 + 1.000000002 + 0.000000002 = +1.000000004 + 1.000000002 + 1.000000000 = +2.000000002 + 1.000000002 + 1.000000001 = +2.000000003 + 1.000000002 + 1.000000002 = +2.000000004 + 1.000000002 + 2.000000000 = +3.000000002 + 1.000000002 + 2.000000001 = +3.000000003 + 1.000000002 + 2.000000002 = +3.000000004 +==== + 1.000000002 + -0.000000001 = +1.000000001 + 1.000000002 + -0.000000002 = +1.000000000 + 1.000000002 + -1.000000000 = +0.000000002 + 1.000000002 + -1.000000001 = +0.000000001 + 1.000000002 + -1.000000002 = +0.000000000 + 1.000000002 + -2.000000000 = -0.999999998 + 1.000000002 + -2.000000001 = -0.999999999 + 1.000000002 + -2.000000002 = -1.000000000 +======== + 2.000000000 + 0.000000000 = +2.000000000 + 2.000000000 + 0.000000001 = +2.000000001 + 2.000000000 + 0.000000002 = +2.000000002 + 2.000000000 + 1.000000000 = +3.000000000 + 2.000000000 + 1.000000001 = +3.000000001 + 2.000000000 + 1.000000002 = +3.000000002 + 2.000000000 + 2.000000000 = +4.000000000 + 2.000000000 + 2.000000001 = +4.000000001 + 2.000000000 + 2.000000002 = +4.000000002 +==== + 2.000000000 + -0.000000001 = +1.999999999 + 2.000000000 + -0.000000002 = +1.999999998 + 2.000000000 + -1.000000000 = +1.000000000 + 2.000000000 + -1.000000001 = +0.999999999 + 2.000000000 + -1.000000002 = +0.999999998 + 2.000000000 + -2.000000000 = +0.000000000 + 2.000000000 + -2.000000001 = -0.000000001 + 2.000000000 + -2.000000002 = -0.000000002 +======== + 2.000000001 + 0.000000000 = +2.000000001 + 2.000000001 + 0.000000001 = +2.000000002 + 2.000000001 + 0.000000002 = +2.000000003 + 2.000000001 + 1.000000000 = +3.000000001 + 2.000000001 + 1.000000001 = +3.000000002 + 2.000000001 + 1.000000002 = +3.000000003 + 2.000000001 + 2.000000000 = +4.000000001 + 2.000000001 + 2.000000001 = +4.000000002 + 2.000000001 + 2.000000002 = +4.000000003 +==== + 2.000000001 + -0.000000001 = +2.000000000 + 2.000000001 + -0.000000002 = +1.999999999 + 2.000000001 + -1.000000000 = +1.000000001 + 2.000000001 + -1.000000001 = +1.000000000 + 2.000000001 + -1.000000002 = +0.999999999 + 2.000000001 + -2.000000000 = +0.000000001 + 2.000000001 + -2.000000001 = +0.000000000 + 2.000000001 + -2.000000002 = -0.000000001 +======== + 2.000000002 + 0.000000000 = +2.000000002 + 2.000000002 + 0.000000001 = +2.000000003 + 2.000000002 + 0.000000002 = +2.000000004 + 2.000000002 + 1.000000000 = +3.000000002 + 2.000000002 + 1.000000001 = +3.000000003 + 2.000000002 + 1.000000002 = +3.000000004 + 2.000000002 + 2.000000000 = +4.000000002 + 2.000000002 + 2.000000001 = +4.000000003 + 2.000000002 + 2.000000002 = +4.000000004 +==== + 2.000000002 + -0.000000001 = +2.000000001 + 2.000000002 + -0.000000002 = +2.000000000 + 2.000000002 + -1.000000000 = +1.000000002 + 2.000000002 + -1.000000001 = +1.000000001 + 2.000000002 + -1.000000002 = +1.000000000 + 2.000000002 + -2.000000000 = +0.000000002 + 2.000000002 + -2.000000001 = +0.000000001 + 2.000000002 + -2.000000002 = +0.000000000 +======== +-0.000000001 + 0.000000000 = -0.000000001 +-0.000000001 + 0.000000001 = +0.000000000 +-0.000000001 + 0.000000002 = +0.000000001 +-0.000000001 + 1.000000000 = +0.999999999 +-0.000000001 + 1.000000001 = +1.000000000 +-0.000000001 + 1.000000002 = +1.000000001 +-0.000000001 + 2.000000000 = +1.999999999 +-0.000000001 + 2.000000001 = +2.000000000 +-0.000000001 + 2.000000002 = +2.000000001 +==== +-0.000000001 + -0.000000001 = -0.000000002 +-0.000000001 + -0.000000002 = -0.000000003 +-0.000000001 + -1.000000000 = -1.000000001 +-0.000000001 + -1.000000001 = -1.000000002 +-0.000000001 + -1.000000002 = -1.000000003 +-0.000000001 + -2.000000000 = -2.000000001 +-0.000000001 + -2.000000001 = -2.000000002 +-0.000000001 + -2.000000002 = -2.000000003 +======== +-0.000000002 + 0.000000000 = -0.000000002 +-0.000000002 + 0.000000001 = -0.000000001 +-0.000000002 + 0.000000002 = +0.000000000 +-0.000000002 + 1.000000000 = +0.999999998 +-0.000000002 + 1.000000001 = +0.999999999 +-0.000000002 + 1.000000002 = +1.000000000 +-0.000000002 + 2.000000000 = +1.999999998 +-0.000000002 + 2.000000001 = +1.999999999 +-0.000000002 + 2.000000002 = +2.000000000 +==== +-0.000000002 + -0.000000001 = -0.000000003 +-0.000000002 + -0.000000002 = -0.000000004 +-0.000000002 + -1.000000000 = -1.000000002 +-0.000000002 + -1.000000001 = -1.000000003 +-0.000000002 + -1.000000002 = -1.000000004 +-0.000000002 + -2.000000000 = -2.000000002 +-0.000000002 + -2.000000001 = -2.000000003 +-0.000000002 + -2.000000002 = -2.000000004 +======== +-1.000000000 + 0.000000000 = -1.000000000 +-1.000000000 + 0.000000001 = -0.999999999 +-1.000000000 + 0.000000002 = -0.999999998 +-1.000000000 + 1.000000000 = +0.000000000 +-1.000000000 + 1.000000001 = +0.000000001 +-1.000000000 + 1.000000002 = +0.000000002 +-1.000000000 + 2.000000000 = +1.000000000 +-1.000000000 + 2.000000001 = +1.000000001 +-1.000000000 + 2.000000002 = +1.000000002 +==== +-1.000000000 + -0.000000001 = -1.000000001 +-1.000000000 + -0.000000002 = -1.000000002 +-1.000000000 + -1.000000000 = -2.000000000 +-1.000000000 + -1.000000001 = -2.000000001 +-1.000000000 + -1.000000002 = -2.000000002 +-1.000000000 + -2.000000000 = -3.000000000 +-1.000000000 + -2.000000001 = -3.000000001 +-1.000000000 + -2.000000002 = -3.000000002 +======== +-1.000000001 + 0.000000000 = -1.000000001 +-1.000000001 + 0.000000001 = -1.000000000 +-1.000000001 + 0.000000002 = -0.999999999 +-1.000000001 + 1.000000000 = -0.000000001 +-1.000000001 + 1.000000001 = +0.000000000 +-1.000000001 + 1.000000002 = +0.000000001 +-1.000000001 + 2.000000000 = +0.999999999 +-1.000000001 + 2.000000001 = +1.000000000 +-1.000000001 + 2.000000002 = +1.000000001 +==== +-1.000000001 + -0.000000001 = -1.000000002 +-1.000000001 + -0.000000002 = -1.000000003 +-1.000000001 + -1.000000000 = -2.000000001 +-1.000000001 + -1.000000001 = -2.000000002 +-1.000000001 + -1.000000002 = -2.000000003 +-1.000000001 + -2.000000000 = -3.000000001 +-1.000000001 + -2.000000001 = -3.000000002 +-1.000000001 + -2.000000002 = -3.000000003 +======== +-1.000000002 + 0.000000000 = -1.000000002 +-1.000000002 + 0.000000001 = -1.000000001 +-1.000000002 + 0.000000002 = -1.000000000 +-1.000000002 + 1.000000000 = -0.000000002 +-1.000000002 + 1.000000001 = -0.000000001 +-1.000000002 + 1.000000002 = +0.000000000 +-1.000000002 + 2.000000000 = +0.999999998 +-1.000000002 + 2.000000001 = +0.999999999 +-1.000000002 + 2.000000002 = +1.000000000 +==== +-1.000000002 + -0.000000001 = -1.000000003 +-1.000000002 + -0.000000002 = -1.000000004 +-1.000000002 + -1.000000000 = -2.000000002 +-1.000000002 + -1.000000001 = -2.000000003 +-1.000000002 + -1.000000002 = -2.000000004 +-1.000000002 + -2.000000000 = -3.000000002 +-1.000000002 + -2.000000001 = -3.000000003 +-1.000000002 + -2.000000002 = -3.000000004 +======== +-2.000000000 + 0.000000000 = -2.000000000 +-2.000000000 + 0.000000001 = -1.999999999 +-2.000000000 + 0.000000002 = -1.999999998 +-2.000000000 + 1.000000000 = -1.000000000 +-2.000000000 + 1.000000001 = -0.999999999 +-2.000000000 + 1.000000002 = -0.999999998 +-2.000000000 + 2.000000000 = +0.000000000 +-2.000000000 + 2.000000001 = +0.000000001 +-2.000000000 + 2.000000002 = +0.000000002 +==== +-2.000000000 + -0.000000001 = -2.000000001 +-2.000000000 + -0.000000002 = -2.000000002 +-2.000000000 + -1.000000000 = -3.000000000 +-2.000000000 + -1.000000001 = -3.000000001 +-2.000000000 + -1.000000002 = -3.000000002 +-2.000000000 + -2.000000000 = -4.000000000 +-2.000000000 + -2.000000001 = -4.000000001 +-2.000000000 + -2.000000002 = -4.000000002 +======== +-2.000000001 + 0.000000000 = -2.000000001 +-2.000000001 + 0.000000001 = -2.000000000 +-2.000000001 + 0.000000002 = -1.999999999 +-2.000000001 + 1.000000000 = -1.000000001 +-2.000000001 + 1.000000001 = -1.000000000 +-2.000000001 + 1.000000002 = -0.999999999 +-2.000000001 + 2.000000000 = -0.000000001 +-2.000000001 + 2.000000001 = +0.000000000 +-2.000000001 + 2.000000002 = +0.000000001 +==== +-2.000000001 + -0.000000001 = -2.000000002 +-2.000000001 + -0.000000002 = -2.000000003 +-2.000000001 + -1.000000000 = -3.000000001 +-2.000000001 + -1.000000001 = -3.000000002 +-2.000000001 + -1.000000002 = -3.000000003 +-2.000000001 + -2.000000000 = -4.000000001 +-2.000000001 + -2.000000001 = -4.000000002 +-2.000000001 + -2.000000002 = -4.000000003 +======== +-2.000000002 + 0.000000000 = -2.000000002 +-2.000000002 + 0.000000001 = -2.000000001 +-2.000000002 + 0.000000002 = -2.000000000 +-2.000000002 + 1.000000000 = -1.000000002 +-2.000000002 + 1.000000001 = -1.000000001 +-2.000000002 + 1.000000002 = -1.000000000 +-2.000000002 + 2.000000000 = -0.000000002 +-2.000000002 + 2.000000001 = -0.000000001 +-2.000000002 + 2.000000002 = +0.000000000 +==== +-2.000000002 + -0.000000001 = -2.000000003 +-2.000000002 + -0.000000002 = -2.000000004 +-2.000000002 + -1.000000000 = -3.000000002 +-2.000000002 + -1.000000001 = -3.000000003 +-2.000000002 + -1.000000002 = -3.000000004 +-2.000000002 + -2.000000000 = -4.000000002 +-2.000000002 + -2.000000001 = -4.000000003 +-2.000000002 + -2.000000002 = -4.000000004 diff --git a/tests/Time/phpt/methods/add_32.phpt b/tests/Time/phpt/methods/add_32.phpt new file mode 100644 index 00000000..f436d0a6 --- /dev/null +++ b/tests/Time/phpt/methods/add_32.phpt @@ -0,0 +1,23 @@ +--TEST-- +Time\Duration::add() (32 bit variation) +--SKIPIF-- + +--FILE-- +add($b); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Time\TimeException: The maximum representable range is 2_147_483_647 seconds (roughly 68 years) diff --git a/tests/Time/phpt/methods/add_64.phpt b/tests/Time/phpt/methods/add_64.phpt new file mode 100644 index 00000000..98448409 --- /dev/null +++ b/tests/Time/phpt/methods/add_64.phpt @@ -0,0 +1,23 @@ +--TEST-- +Time\Duration::add() (64 bit variation) +--SKIPIF-- + +--FILE-- +add($b); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Time\TimeException: The maximum representable range is 9_223_372_035 seconds (roughly 292 years) diff --git a/tests/Time/phpt/methods/compare.phpt b/tests/Time/phpt/methods/compare.phpt new file mode 100644 index 00000000..488a39c2 --- /dev/null +++ b/tests/Time/phpt/methods/compare.phpt @@ -0,0 +1,369 @@ +--TEST-- +Time\Duration::absolute() +--FILE-- +')), " ", f($b, pad: false), PHP_EOL; + } +} + +?> +--EXPECT-- +======== ++0.000000000 = +0.000000000 ++0.000000000 < +0.000000001 ++0.000000000 < +0.000000002 ++0.000000000 < +1.000000000 ++0.000000000 < +1.000000001 ++0.000000000 < +1.000000002 ++0.000000000 < +2.000000000 ++0.000000000 < +2.000000001 ++0.000000000 < +2.000000002 +==== ++0.000000000 > -0.000000001 ++0.000000000 > -0.000000002 ++0.000000000 > -1.000000000 ++0.000000000 > -1.000000001 ++0.000000000 > -1.000000002 ++0.000000000 > -2.000000000 ++0.000000000 > -2.000000001 ++0.000000000 > -2.000000002 +======== ++0.000000001 > +0.000000000 ++0.000000001 = +0.000000001 ++0.000000001 < +0.000000002 ++0.000000001 < +1.000000000 ++0.000000001 < +1.000000001 ++0.000000001 < +1.000000002 ++0.000000001 < +2.000000000 ++0.000000001 < +2.000000001 ++0.000000001 < +2.000000002 +==== ++0.000000001 > -0.000000001 ++0.000000001 > -0.000000002 ++0.000000001 > -1.000000000 ++0.000000001 > -1.000000001 ++0.000000001 > -1.000000002 ++0.000000001 > -2.000000000 ++0.000000001 > -2.000000001 ++0.000000001 > -2.000000002 +======== ++0.000000002 > +0.000000000 ++0.000000002 > +0.000000001 ++0.000000002 = +0.000000002 ++0.000000002 < +1.000000000 ++0.000000002 < +1.000000001 ++0.000000002 < +1.000000002 ++0.000000002 < +2.000000000 ++0.000000002 < +2.000000001 ++0.000000002 < +2.000000002 +==== ++0.000000002 > -0.000000001 ++0.000000002 > -0.000000002 ++0.000000002 > -1.000000000 ++0.000000002 > -1.000000001 ++0.000000002 > -1.000000002 ++0.000000002 > -2.000000000 ++0.000000002 > -2.000000001 ++0.000000002 > -2.000000002 +======== ++1.000000000 > +0.000000000 ++1.000000000 > +0.000000001 ++1.000000000 > +0.000000002 ++1.000000000 = +1.000000000 ++1.000000000 < +1.000000001 ++1.000000000 < +1.000000002 ++1.000000000 < +2.000000000 ++1.000000000 < +2.000000001 ++1.000000000 < +2.000000002 +==== ++1.000000000 > -0.000000001 ++1.000000000 > -0.000000002 ++1.000000000 > -1.000000000 ++1.000000000 > -1.000000001 ++1.000000000 > -1.000000002 ++1.000000000 > -2.000000000 ++1.000000000 > -2.000000001 ++1.000000000 > -2.000000002 +======== ++1.000000001 > +0.000000000 ++1.000000001 > +0.000000001 ++1.000000001 > +0.000000002 ++1.000000001 > +1.000000000 ++1.000000001 = +1.000000001 ++1.000000001 < +1.000000002 ++1.000000001 < +2.000000000 ++1.000000001 < +2.000000001 ++1.000000001 < +2.000000002 +==== ++1.000000001 > -0.000000001 ++1.000000001 > -0.000000002 ++1.000000001 > -1.000000000 ++1.000000001 > -1.000000001 ++1.000000001 > -1.000000002 ++1.000000001 > -2.000000000 ++1.000000001 > -2.000000001 ++1.000000001 > -2.000000002 +======== ++1.000000002 > +0.000000000 ++1.000000002 > +0.000000001 ++1.000000002 > +0.000000002 ++1.000000002 > +1.000000000 ++1.000000002 > +1.000000001 ++1.000000002 = +1.000000002 ++1.000000002 < +2.000000000 ++1.000000002 < +2.000000001 ++1.000000002 < +2.000000002 +==== ++1.000000002 > -0.000000001 ++1.000000002 > -0.000000002 ++1.000000002 > -1.000000000 ++1.000000002 > -1.000000001 ++1.000000002 > -1.000000002 ++1.000000002 > -2.000000000 ++1.000000002 > -2.000000001 ++1.000000002 > -2.000000002 +======== ++2.000000000 > +0.000000000 ++2.000000000 > +0.000000001 ++2.000000000 > +0.000000002 ++2.000000000 > +1.000000000 ++2.000000000 > +1.000000001 ++2.000000000 > +1.000000002 ++2.000000000 = +2.000000000 ++2.000000000 < +2.000000001 ++2.000000000 < +2.000000002 +==== ++2.000000000 > -0.000000001 ++2.000000000 > -0.000000002 ++2.000000000 > -1.000000000 ++2.000000000 > -1.000000001 ++2.000000000 > -1.000000002 ++2.000000000 > -2.000000000 ++2.000000000 > -2.000000001 ++2.000000000 > -2.000000002 +======== ++2.000000001 > +0.000000000 ++2.000000001 > +0.000000001 ++2.000000001 > +0.000000002 ++2.000000001 > +1.000000000 ++2.000000001 > +1.000000001 ++2.000000001 > +1.000000002 ++2.000000001 > +2.000000000 ++2.000000001 = +2.000000001 ++2.000000001 < +2.000000002 +==== ++2.000000001 > -0.000000001 ++2.000000001 > -0.000000002 ++2.000000001 > -1.000000000 ++2.000000001 > -1.000000001 ++2.000000001 > -1.000000002 ++2.000000001 > -2.000000000 ++2.000000001 > -2.000000001 ++2.000000001 > -2.000000002 +======== ++2.000000002 > +0.000000000 ++2.000000002 > +0.000000001 ++2.000000002 > +0.000000002 ++2.000000002 > +1.000000000 ++2.000000002 > +1.000000001 ++2.000000002 > +1.000000002 ++2.000000002 > +2.000000000 ++2.000000002 > +2.000000001 ++2.000000002 = +2.000000002 +==== ++2.000000002 > -0.000000001 ++2.000000002 > -0.000000002 ++2.000000002 > -1.000000000 ++2.000000002 > -1.000000001 ++2.000000002 > -1.000000002 ++2.000000002 > -2.000000000 ++2.000000002 > -2.000000001 ++2.000000002 > -2.000000002 +======== +-0.000000001 < +0.000000000 +-0.000000001 < +0.000000001 +-0.000000001 < +0.000000002 +-0.000000001 < +1.000000000 +-0.000000001 < +1.000000001 +-0.000000001 < +1.000000002 +-0.000000001 < +2.000000000 +-0.000000001 < +2.000000001 +-0.000000001 < +2.000000002 +==== +-0.000000001 = -0.000000001 +-0.000000001 > -0.000000002 +-0.000000001 > -1.000000000 +-0.000000001 > -1.000000001 +-0.000000001 > -1.000000002 +-0.000000001 > -2.000000000 +-0.000000001 > -2.000000001 +-0.000000001 > -2.000000002 +======== +-0.000000002 < +0.000000000 +-0.000000002 < +0.000000001 +-0.000000002 < +0.000000002 +-0.000000002 < +1.000000000 +-0.000000002 < +1.000000001 +-0.000000002 < +1.000000002 +-0.000000002 < +2.000000000 +-0.000000002 < +2.000000001 +-0.000000002 < +2.000000002 +==== +-0.000000002 < -0.000000001 +-0.000000002 = -0.000000002 +-0.000000002 > -1.000000000 +-0.000000002 > -1.000000001 +-0.000000002 > -1.000000002 +-0.000000002 > -2.000000000 +-0.000000002 > -2.000000001 +-0.000000002 > -2.000000002 +======== +-1.000000000 < +0.000000000 +-1.000000000 < +0.000000001 +-1.000000000 < +0.000000002 +-1.000000000 < +1.000000000 +-1.000000000 < +1.000000001 +-1.000000000 < +1.000000002 +-1.000000000 < +2.000000000 +-1.000000000 < +2.000000001 +-1.000000000 < +2.000000002 +==== +-1.000000000 < -0.000000001 +-1.000000000 < -0.000000002 +-1.000000000 = -1.000000000 +-1.000000000 > -1.000000001 +-1.000000000 > -1.000000002 +-1.000000000 > -2.000000000 +-1.000000000 > -2.000000001 +-1.000000000 > -2.000000002 +======== +-1.000000001 < +0.000000000 +-1.000000001 < +0.000000001 +-1.000000001 < +0.000000002 +-1.000000001 < +1.000000000 +-1.000000001 < +1.000000001 +-1.000000001 < +1.000000002 +-1.000000001 < +2.000000000 +-1.000000001 < +2.000000001 +-1.000000001 < +2.000000002 +==== +-1.000000001 < -0.000000001 +-1.000000001 < -0.000000002 +-1.000000001 < -1.000000000 +-1.000000001 = -1.000000001 +-1.000000001 > -1.000000002 +-1.000000001 > -2.000000000 +-1.000000001 > -2.000000001 +-1.000000001 > -2.000000002 +======== +-1.000000002 < +0.000000000 +-1.000000002 < +0.000000001 +-1.000000002 < +0.000000002 +-1.000000002 < +1.000000000 +-1.000000002 < +1.000000001 +-1.000000002 < +1.000000002 +-1.000000002 < +2.000000000 +-1.000000002 < +2.000000001 +-1.000000002 < +2.000000002 +==== +-1.000000002 < -0.000000001 +-1.000000002 < -0.000000002 +-1.000000002 < -1.000000000 +-1.000000002 < -1.000000001 +-1.000000002 = -1.000000002 +-1.000000002 > -2.000000000 +-1.000000002 > -2.000000001 +-1.000000002 > -2.000000002 +======== +-2.000000000 < +0.000000000 +-2.000000000 < +0.000000001 +-2.000000000 < +0.000000002 +-2.000000000 < +1.000000000 +-2.000000000 < +1.000000001 +-2.000000000 < +1.000000002 +-2.000000000 < +2.000000000 +-2.000000000 < +2.000000001 +-2.000000000 < +2.000000002 +==== +-2.000000000 < -0.000000001 +-2.000000000 < -0.000000002 +-2.000000000 < -1.000000000 +-2.000000000 < -1.000000001 +-2.000000000 < -1.000000002 +-2.000000000 = -2.000000000 +-2.000000000 > -2.000000001 +-2.000000000 > -2.000000002 +======== +-2.000000001 < +0.000000000 +-2.000000001 < +0.000000001 +-2.000000001 < +0.000000002 +-2.000000001 < +1.000000000 +-2.000000001 < +1.000000001 +-2.000000001 < +1.000000002 +-2.000000001 < +2.000000000 +-2.000000001 < +2.000000001 +-2.000000001 < +2.000000002 +==== +-2.000000001 < -0.000000001 +-2.000000001 < -0.000000002 +-2.000000001 < -1.000000000 +-2.000000001 < -1.000000001 +-2.000000001 < -1.000000002 +-2.000000001 < -2.000000000 +-2.000000001 = -2.000000001 +-2.000000001 > -2.000000002 +======== +-2.000000002 < +0.000000000 +-2.000000002 < +0.000000001 +-2.000000002 < +0.000000002 +-2.000000002 < +1.000000000 +-2.000000002 < +1.000000001 +-2.000000002 < +1.000000002 +-2.000000002 < +2.000000000 +-2.000000002 < +2.000000001 +-2.000000002 < +2.000000002 +==== +-2.000000002 < -0.000000001 +-2.000000002 < -0.000000002 +-2.000000002 < -1.000000000 +-2.000000002 < -1.000000001 +-2.000000002 < -1.000000002 +-2.000000002 < -2.000000000 +-2.000000002 < -2.000000001 +-2.000000002 = -2.000000002 diff --git a/tests/Time/phpt/methods/divideBy.phpt b/tests/Time/phpt/methods/divideBy.phpt new file mode 100644 index 00000000..440107d3 --- /dev/null +++ b/tests/Time/phpt/methods/divideBy.phpt @@ -0,0 +1,133 @@ +--TEST-- +Time\Duration::divideBy() +--FILE-- +divideBy($divisor); + echo f($d), " / ", sprintf("%10d", $divisor), " = ", f($result), PHP_EOL; + + if (PHP_INT_SIZE != 4 && (intdiv(as_int($d), $divisor) !== as_int($result))) { + throw new \Exception('Verification failed'); + } + } +} + +echo "========", PHP_EOL; + +$d = Time\Duration::fromSeconds(1, 0); +try { + $d->divideBy(0); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +======== + +0.000000000 / 1 = +0.000000000 + +0.000000000 / 2 = +0.000000000 + +0.000000000 / 3 = +0.000000000 + +0.000000000 / 4 = +0.000000000 + +0.000000000 / 999999999 = +0.000000000 + +0.000000000 / 1000000000 = +0.000000000 + +0.000000000 / 2147483647 = +0.000000000 +======== + +0.000000001 / 1 = +0.000000001 + +0.000000001 / 2 = +0.000000000 + +0.000000001 / 3 = +0.000000000 + +0.000000001 / 4 = +0.000000000 + +0.000000001 / 999999999 = +0.000000000 + +0.000000001 / 1000000000 = +0.000000000 + +0.000000001 / 2147483647 = +0.000000000 +======== + +0.999999999 / 1 = +0.999999999 + +0.999999999 / 2 = +0.499999999 + +0.999999999 / 3 = +0.333333333 + +0.999999999 / 4 = +0.249999999 + +0.999999999 / 999999999 = +0.000000001 + +0.999999999 / 1000000000 = +0.000000000 + +0.999999999 / 2147483647 = +0.000000000 +======== + +1.000000000 / 1 = +1.000000000 + +1.000000000 / 2 = +0.500000000 + +1.000000000 / 3 = +0.333333333 + +1.000000000 / 4 = +0.250000000 + +1.000000000 / 999999999 = +0.000000001 + +1.000000000 / 1000000000 = +0.000000001 + +1.000000000 / 2147483647 = +0.000000000 +======== ++2147483647.999999999 / 1 = +2147483647.999999999 ++2147483647.999999999 / 2 = +1073741823.999999999 ++2147483647.999999999 / 3 = +715827882.666666666 ++2147483647.999999999 / 4 = +536870911.999999999 ++2147483647.999999999 / 999999999 = +2.147483650 ++2147483647.999999999 / 1000000000 = +2.147483647 ++2147483647.999999999 / 2147483647 = +1.000000000 +======== + -0.000000001 / 1 = -0.000000001 + -0.000000001 / 2 = +0.000000000 + -0.000000001 / 3 = +0.000000000 + -0.000000001 / 4 = +0.000000000 + -0.000000001 / 999999999 = +0.000000000 + -0.000000001 / 1000000000 = +0.000000000 + -0.000000001 / 2147483647 = +0.000000000 +======== + -0.999999999 / 1 = -0.999999999 + -0.999999999 / 2 = -0.499999999 + -0.999999999 / 3 = -0.333333333 + -0.999999999 / 4 = -0.249999999 + -0.999999999 / 999999999 = -0.000000001 + -0.999999999 / 1000000000 = +0.000000000 + -0.999999999 / 2147483647 = +0.000000000 +======== + -1.000000000 / 1 = -1.000000000 + -1.000000000 / 2 = -0.500000000 + -1.000000000 / 3 = -0.333333333 + -1.000000000 / 4 = -0.250000000 + -1.000000000 / 999999999 = -0.000000001 + -1.000000000 / 1000000000 = -0.000000001 + -1.000000000 / 2147483647 = +0.000000000 +======== +-2147483647.999999999 / 1 = -2147483647.999999999 +-2147483647.999999999 / 2 = -1073741823.999999999 +-2147483647.999999999 / 3 = -715827882.666666666 +-2147483647.999999999 / 4 = -536870911.999999999 +-2147483647.999999999 / 999999999 = -2.147483650 +-2147483647.999999999 / 1000000000 = -2.147483647 +-2147483647.999999999 / 2147483647 = -1.000000000 +======== +DivisionByZeroError: Division by zero diff --git a/tests/Time/phpt/methods/divideBy_64.phpt b/tests/Time/phpt/methods/divideBy_64.phpt new file mode 100644 index 00000000..21c54538 --- /dev/null +++ b/tests/Time/phpt/methods/divideBy_64.phpt @@ -0,0 +1,78 @@ +--TEST-- +Time\Duration::divideBy() (64 bit variation) +--SKIPIF-- + +--FILE-- +divideBy($factor)), PHP_EOL; + } +} + +?> +--EXPECT-- +======== ++2147483648.000000000 / 1 = +2147483648.000000000 ++2147483648.000000000 / 2 = +1073741824.000000000 ++2147483648.000000000 / 3 = +715827882.666666666 ++2147483648.000000000 / 4 = +536870912.000000000 ++2147483648.000000000 / 999999999 = +2.147483650 ++2147483648.000000000 / 1000000000 = +2.147483648 ++2147483648.000000000 / 9223372035999999999 = +0.000000000 +======== ++9223372035.999999999 / 1 = +9223372035.999999999 ++9223372035.999999999 / 2 = +4611686017.999999999 ++9223372035.999999999 / 3 = +3074457345.333333333 ++9223372035.999999999 / 4 = +2305843008.999999999 ++9223372035.999999999 / 999999999 = +9.223372045 ++9223372035.999999999 / 1000000000 = +9.223372035 ++9223372035.999999999 / 9223372035999999999 = +0.000000001 +======== +-2147483648.000000000 / 1 = -2147483648.000000000 +-2147483648.000000000 / 2 = -1073741824.000000000 +-2147483648.000000000 / 3 = -715827882.666666666 +-2147483648.000000000 / 4 = -536870912.000000000 +-2147483648.000000000 / 999999999 = -2.147483650 +-2147483648.000000000 / 1000000000 = -2.147483648 +-2147483648.000000000 / 9223372035999999999 = +0.000000000 +======== +-9223372035.999999999 / 1 = -9223372035.999999999 +-9223372035.999999999 / 2 = -4611686017.999999999 +-9223372035.999999999 / 3 = -3074457345.333333333 +-9223372035.999999999 / 4 = -2305843008.999999999 +-9223372035.999999999 / 999999999 = -9.223372045 +-9223372035.999999999 / 1000000000 = -9.223372035 +-9223372035.999999999 / 9223372035999999999 = -0.000000001 diff --git a/tests/Time/phpt/methods/fromHours.phpt b/tests/Time/phpt/methods/fromHours.phpt new file mode 100644 index 00000000..65828803 --- /dev/null +++ b/tests/Time/phpt/methods/fromHours.phpt @@ -0,0 +1,23 @@ +--TEST-- +Time\Duration::fromHours() +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- + +0.000000000 + +3600.000000000 ++2147482800.000000000 +ValueError: Time\Duration::fromHours(): Argument #1 ($hours) must be greater than or equal to 0 diff --git a/tests/Time/phpt/methods/fromHours_32.phpt b/tests/Time/phpt/methods/fromHours_32.phpt new file mode 100644 index 00000000..998ab5d9 --- /dev/null +++ b/tests/Time/phpt/methods/fromHours_32.phpt @@ -0,0 +1,23 @@ +--TEST-- +Time\Duration::fromHours() (32 bit variation) +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- ++2147482800.000000000 +Time\TimeException: The maximum representable range is 2_147_483_647 seconds (roughly 68 years) diff --git a/tests/Time/phpt/methods/fromHours_64.phpt b/tests/Time/phpt/methods/fromHours_64.phpt new file mode 100644 index 00000000..719da0e8 --- /dev/null +++ b/tests/Time/phpt/methods/fromHours_64.phpt @@ -0,0 +1,23 @@ +--TEST-- +Time\Duration::fromHours() (64 bit variation) +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- ++9223369200.000000000 +Time\TimeException: The maximum representable range is 9_223_372_035 seconds (roughly 292 years) diff --git a/tests/Time/phpt/methods/fromIso8601DurationString.phpt b/tests/Time/phpt/methods/fromIso8601DurationString.phpt new file mode 100644 index 00000000..109a1dd1 --- /dev/null +++ b/tests/Time/phpt/methods/fromIso8601DurationString.phpt @@ -0,0 +1,74 @@ +--TEST-- +Time\Duration::fromIso8601DurationString() +--FILE-- +getMessage(), PHP_EOL; + } +} + +?> +--EXPECT-- +PT0S : +0.000000000 +PT1S : +1.000000000 +PT60S : +60.000000000 +PT2147483647S : +2147483647.000000000 +PT0.1S : Time\TimeException: The ISO 8601 duration string could not be parsed +PT0M : +0.000000000 +PT1M : +60.000000000 +PT1M1S : +61.000000000 +PT1M60S : +120.000000000 +PT0H : +0.000000000 +PT1H : +3600.000000000 +PT1H1M : +3660.000000000 +PT1H1M1S : +3661.000000000 +PT1H60M : +7200.000000000 +PT1H60M60S : +7260.000000000 + : Time\TimeException: The ISO 8601 duration string could not be parsed +P : Time\TimeException: The ISO 8601 duration string could not be parsed +PT : Time\TimeException: The ISO 8601 duration string could not be parsed +P1D : Time\TimeException: The ISO 8601 duration string may only contain the time (T) aspect +P1W : Time\TimeException: The ISO 8601 duration string may only contain the time (T) aspect +P1M : Time\TimeException: The ISO 8601 duration string may only contain the time (T) aspect +P1DT0S : Time\TimeException: The ISO 8601 duration string may only contain the time (T) aspect +2000-01-01T00:00:00Z : Time\TimeException: The ISO 8601 duration string is missing the period (P) aspect +2000-01-01T00:00:00Z/PT1H: Time\TimeException: The ISO 8601 duration string may only contain the period (P) aspect diff --git a/tests/Time/phpt/methods/fromIso8601DurationString_64.phpt b/tests/Time/phpt/methods/fromIso8601DurationString_64.phpt new file mode 100644 index 00000000..04ee27db --- /dev/null +++ b/tests/Time/phpt/methods/fromIso8601DurationString_64.phpt @@ -0,0 +1,32 @@ +--TEST-- +Time\Duration::fromIso8601DurationString() (64 bit variation) +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; + } +} + +?> +--EXPECT-- +PT2147483648S : +2147483648.000000000 +PT9223372035S : +9223372035.000000000 +PT9223372036S : Time\TimeException: The maximum representable range is 9_223_372_035 seconds (roughly 292 years) diff --git a/tests/Time/phpt/methods/fromMicroseconds.phpt b/tests/Time/phpt/methods/fromMicroseconds.phpt new file mode 100644 index 00000000..aef4092b --- /dev/null +++ b/tests/Time/phpt/methods/fromMicroseconds.phpt @@ -0,0 +1,27 @@ +--TEST-- +Time\Duration::fromMicroseconds() +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- + +0.000000000 + +0.000001000 + +0.999999000 + +1.000000000 + +2147.483647000 +ValueError: Time\Duration::fromMicroseconds(): Argument #1 ($microseconds) must be greater than or equal to 0 diff --git a/tests/Time/phpt/methods/fromMicroseconds_64.phpt b/tests/Time/phpt/methods/fromMicroseconds_64.phpt new file mode 100644 index 00000000..84f31742 --- /dev/null +++ b/tests/Time/phpt/methods/fromMicroseconds_64.phpt @@ -0,0 +1,23 @@ +--TEST-- +Time\Duration::fromMicroseconds() (64 bit variation) +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- ++9223372035.999999000 +Time\TimeException: The maximum representable range is 9_223_372_035 seconds (roughly 292 years) diff --git a/tests/Time/phpt/methods/fromMilliseconds.phpt b/tests/Time/phpt/methods/fromMilliseconds.phpt new file mode 100644 index 00000000..d63f2053 --- /dev/null +++ b/tests/Time/phpt/methods/fromMilliseconds.phpt @@ -0,0 +1,27 @@ +--TEST-- +Time\Duration::fromMilliseconds() +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- + +0.000000000 + +0.001000000 + +0.999000000 + +1.000000000 + +2147483.647000000 +ValueError: Time\Duration::fromMilliseconds(): Argument #1 ($milliseconds) must be greater than or equal to 0 diff --git a/tests/Time/phpt/methods/fromMilliseconds_64.phpt b/tests/Time/phpt/methods/fromMilliseconds_64.phpt new file mode 100644 index 00000000..4ed7250f --- /dev/null +++ b/tests/Time/phpt/methods/fromMilliseconds_64.phpt @@ -0,0 +1,23 @@ +--TEST-- +Time\Duration::fromMilliseconds() (64 bit variation) +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- ++9223372035.999000000 +Time\TimeException: The maximum representable range is 9_223_372_035 seconds (roughly 292 years) diff --git a/tests/Time/phpt/methods/fromMinutes.phpt b/tests/Time/phpt/methods/fromMinutes.phpt new file mode 100644 index 00000000..7674ea95 --- /dev/null +++ b/tests/Time/phpt/methods/fromMinutes.phpt @@ -0,0 +1,23 @@ +--TEST-- +Time\Duration::fromMinutes() +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- + +0.000000000 + +60.000000000 ++2147483640.000000000 +ValueError: Time\Duration::fromMinutes(): Argument #1 ($minutes) must be greater than or equal to 0 diff --git a/tests/Time/phpt/methods/fromMinutes_32.phpt b/tests/Time/phpt/methods/fromMinutes_32.phpt new file mode 100644 index 00000000..70582316 --- /dev/null +++ b/tests/Time/phpt/methods/fromMinutes_32.phpt @@ -0,0 +1,23 @@ +--TEST-- +Time\Duration::fromMinutes() (32 bit variation) +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- ++2147483640.000000000 +Time\TimeException: The maximum representable range is 2_147_483_647 seconds (roughly 68 years) diff --git a/tests/Time/phpt/methods/fromMinutes_64.phpt b/tests/Time/phpt/methods/fromMinutes_64.phpt new file mode 100644 index 00000000..4b3eaf4a --- /dev/null +++ b/tests/Time/phpt/methods/fromMinutes_64.phpt @@ -0,0 +1,23 @@ +--TEST-- +Time\Duration::fromMinutes() (64 bit variation) +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- ++9223372020.000000000 +Time\TimeException: The maximum representable range is 9_223_372_035 seconds (roughly 292 years) diff --git a/tests/Time/phpt/methods/fromNanoseconds.phpt b/tests/Time/phpt/methods/fromNanoseconds.phpt new file mode 100644 index 00000000..7b425d2e --- /dev/null +++ b/tests/Time/phpt/methods/fromNanoseconds.phpt @@ -0,0 +1,27 @@ +--TEST-- +Time\Duration::fromNanoseconds() +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- + +0.000000000 + +0.000000001 + +0.999999999 + +1.000000000 + +2.147483647 +ValueError: Time\Duration::fromNanoseconds(): Argument #1 ($nanoseconds) must be greater than or equal to 0 diff --git a/tests/Time/phpt/methods/fromNanoseconds_64.phpt b/tests/Time/phpt/methods/fromNanoseconds_64.phpt new file mode 100644 index 00000000..e68adc28 --- /dev/null +++ b/tests/Time/phpt/methods/fromNanoseconds_64.phpt @@ -0,0 +1,23 @@ +--TEST-- +Time\Duration::fromNanoseconds() (64 bit variation) +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- ++9223372035.999999999 +Time\TimeException: The maximum representable range is 9_223_372_035 seconds (roughly 292 years) diff --git a/tests/Time/phpt/methods/fromSeconds.phpt b/tests/Time/phpt/methods/fromSeconds.phpt new file mode 100644 index 00000000..8cd26ce4 --- /dev/null +++ b/tests/Time/phpt/methods/fromSeconds.phpt @@ -0,0 +1,46 @@ +--TEST-- +Time\Duration::fromSeconds() +--FILE-- +getMessage(), PHP_EOL; +} + +try { + Time\Duration::fromSeconds(0, -1); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + Time\Duration::fromSeconds(0, 1000000000); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- + +0.000000000 + +1.000000000 ++2147483647.000000000 + +0.000000000 + +0.000000001 + +1.000000001 ++2147483647.999999999 +ValueError: Time\Duration::fromSeconds(): Argument #1 ($seconds) must be greater than or equal to 0 +ValueError: Time\Duration::fromSeconds(): Argument #2 ($nanoseconds) must be greater than or equal to 0 +ValueError: Time\Duration::fromSeconds(): Argument #2 ($nanoseconds) must be less than 1_000_000_000 diff --git a/tests/Time/phpt/methods/fromSeconds_64.phpt b/tests/Time/phpt/methods/fromSeconds_64.phpt new file mode 100644 index 00000000..2deed85e --- /dev/null +++ b/tests/Time/phpt/methods/fromSeconds_64.phpt @@ -0,0 +1,25 @@ +--TEST-- +Time\Duration::fromSeconds() (64 bit variation) +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- ++9223372035.000000000 ++9223372035.999999999 +Time\TimeException: The maximum representable range is 9_223_372_035 seconds (roughly 292 years) diff --git a/tests/Time/phpt/methods/multiplyBy.phpt b/tests/Time/phpt/methods/multiplyBy.phpt new file mode 100644 index 00000000..70b374d2 --- /dev/null +++ b/tests/Time/phpt/methods/multiplyBy.phpt @@ -0,0 +1,206 @@ +--TEST-- +Time\Duration::multiplyBy() +--FILE-- +multiplyBy($factor); + echo f($d), " * ", sprintf("%10d", $factor), " = ", f($result), PHP_EOL; + + if (PHP_INT_SIZE != 4 && (as_int($d) * $factor !== as_int($result))) { + throw new \Exception('Verification failed'); + } + } +} + +echo "========", PHP_EOL; +$d = Time\Duration::fromSeconds(0, 1); +$factor = 2_147_483_647; +$result = $d->multiplyBy($factor); +echo f($d), " * ", sprintf("%10d", $factor), " = ", f($result), PHP_EOL; + +echo "========", PHP_EOL; +$d = Time\Duration::fromSeconds(1, 0); +$factor = 2_147_483_647; +$result = $d->multiplyBy($factor); +echo f($d), " * ", sprintf("%10d", $factor), " = ", f($result), PHP_EOL; + +?> +--EXPECT-- +======== + +0.000000000 * 0 = +0.000000000 + +0.000000000 * 1 = +0.000000000 + +0.000000000 * 2 = +0.000000000 + +0.000000000 * 3 = +0.000000000 + +0.000000000 * 4 = +0.000000000 + +0.000000000 * 999999999 = +0.000000000 + +0.000000000 * 1000000000 = +0.000000000 +======== + +0.000000001 * 0 = +0.000000000 + +0.000000001 * 1 = +0.000000001 + +0.000000001 * 2 = +0.000000002 + +0.000000001 * 3 = +0.000000003 + +0.000000001 * 4 = +0.000000004 + +0.000000001 * 999999999 = +0.999999999 + +0.000000001 * 1000000000 = +1.000000000 +======== + +0.000000002 * 0 = +0.000000000 + +0.000000002 * 1 = +0.000000002 + +0.000000002 * 2 = +0.000000004 + +0.000000002 * 3 = +0.000000006 + +0.000000002 * 4 = +0.000000008 + +0.000000002 * 999999999 = +1.999999998 + +0.000000002 * 1000000000 = +2.000000000 +======== + +1.000000000 * 0 = +0.000000000 + +1.000000000 * 1 = +1.000000000 + +1.000000000 * 2 = +2.000000000 + +1.000000000 * 3 = +3.000000000 + +1.000000000 * 4 = +4.000000000 + +1.000000000 * 999999999 = +999999999.000000000 + +1.000000000 * 1000000000 = +1000000000.000000000 +======== + +1.000000001 * 0 = +0.000000000 + +1.000000001 * 1 = +1.000000001 + +1.000000001 * 2 = +2.000000002 + +1.000000001 * 3 = +3.000000003 + +1.000000001 * 4 = +4.000000004 + +1.000000001 * 999999999 = +999999999.999999999 + +1.000000001 * 1000000000 = +1000000001.000000000 +======== + +1.000000002 * 0 = +0.000000000 + +1.000000002 * 1 = +1.000000002 + +1.000000002 * 2 = +2.000000004 + +1.000000002 * 3 = +3.000000006 + +1.000000002 * 4 = +4.000000008 + +1.000000002 * 999999999 = +1000000000.999999998 + +1.000000002 * 1000000000 = +1000000002.000000000 +======== + +2.000000000 * 0 = +0.000000000 + +2.000000000 * 1 = +2.000000000 + +2.000000000 * 2 = +4.000000000 + +2.000000000 * 3 = +6.000000000 + +2.000000000 * 4 = +8.000000000 + +2.000000000 * 999999999 = +1999999998.000000000 + +2.000000000 * 1000000000 = +2000000000.000000000 +======== + +2.000000001 * 0 = +0.000000000 + +2.000000001 * 1 = +2.000000001 + +2.000000001 * 2 = +4.000000002 + +2.000000001 * 3 = +6.000000003 + +2.000000001 * 4 = +8.000000004 + +2.000000001 * 999999999 = +1999999998.999999999 + +2.000000001 * 1000000000 = +2000000001.000000000 +======== + +2.000000002 * 0 = +0.000000000 + +2.000000002 * 1 = +2.000000002 + +2.000000002 * 2 = +4.000000004 + +2.000000002 * 3 = +6.000000006 + +2.000000002 * 4 = +8.000000008 + +2.000000002 * 999999999 = +1999999999.999999998 + +2.000000002 * 1000000000 = +2000000002.000000000 +======== + -0.000000001 * 0 = +0.000000000 + -0.000000001 * 1 = -0.000000001 + -0.000000001 * 2 = -0.000000002 + -0.000000001 * 3 = -0.000000003 + -0.000000001 * 4 = -0.000000004 + -0.000000001 * 999999999 = -0.999999999 + -0.000000001 * 1000000000 = -1.000000000 +======== + -0.000000002 * 0 = +0.000000000 + -0.000000002 * 1 = -0.000000002 + -0.000000002 * 2 = -0.000000004 + -0.000000002 * 3 = -0.000000006 + -0.000000002 * 4 = -0.000000008 + -0.000000002 * 999999999 = -1.999999998 + -0.000000002 * 1000000000 = -2.000000000 +======== + -1.000000000 * 0 = +0.000000000 + -1.000000000 * 1 = -1.000000000 + -1.000000000 * 2 = -2.000000000 + -1.000000000 * 3 = -3.000000000 + -1.000000000 * 4 = -4.000000000 + -1.000000000 * 999999999 = -999999999.000000000 + -1.000000000 * 1000000000 = -1000000000.000000000 +======== + -1.000000001 * 0 = +0.000000000 + -1.000000001 * 1 = -1.000000001 + -1.000000001 * 2 = -2.000000002 + -1.000000001 * 3 = -3.000000003 + -1.000000001 * 4 = -4.000000004 + -1.000000001 * 999999999 = -999999999.999999999 + -1.000000001 * 1000000000 = -1000000001.000000000 +======== + -1.000000002 * 0 = +0.000000000 + -1.000000002 * 1 = -1.000000002 + -1.000000002 * 2 = -2.000000004 + -1.000000002 * 3 = -3.000000006 + -1.000000002 * 4 = -4.000000008 + -1.000000002 * 999999999 = -1000000000.999999998 + -1.000000002 * 1000000000 = -1000000002.000000000 +======== + -2.000000000 * 0 = +0.000000000 + -2.000000000 * 1 = -2.000000000 + -2.000000000 * 2 = -4.000000000 + -2.000000000 * 3 = -6.000000000 + -2.000000000 * 4 = -8.000000000 + -2.000000000 * 999999999 = -1999999998.000000000 + -2.000000000 * 1000000000 = -2000000000.000000000 +======== + -2.000000001 * 0 = +0.000000000 + -2.000000001 * 1 = -2.000000001 + -2.000000001 * 2 = -4.000000002 + -2.000000001 * 3 = -6.000000003 + -2.000000001 * 4 = -8.000000004 + -2.000000001 * 999999999 = -1999999998.999999999 + -2.000000001 * 1000000000 = -2000000001.000000000 +======== + -2.000000002 * 0 = +0.000000000 + -2.000000002 * 1 = -2.000000002 + -2.000000002 * 2 = -4.000000004 + -2.000000002 * 3 = -6.000000006 + -2.000000002 * 4 = -8.000000008 + -2.000000002 * 999999999 = -1999999999.999999998 + -2.000000002 * 1000000000 = -2000000002.000000000 +======== + +0.000000001 * 2147483647 = +2.147483647 +======== + +1.000000000 * 2147483647 = +2147483647.000000000 diff --git a/tests/Time/phpt/methods/multiplyBy_32.phpt b/tests/Time/phpt/methods/multiplyBy_32.phpt new file mode 100644 index 00000000..7a1cbcb5 --- /dev/null +++ b/tests/Time/phpt/methods/multiplyBy_32.phpt @@ -0,0 +1,55 @@ +--TEST-- +Time\Duration::multiplyBy() (32 bit variation) +--SKIPIF-- + +--FILE-- +multiplyBy(1)), PHP_EOL; + +try { + $d->multiplyBy(2); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +echo "====", PHP_EOL; + +$d = Time\Duration::fromSeconds(1_073_741_823, 999999999); + +echo f($d->multiplyBy(2)), PHP_EOL; + +try { + $d->multiplyBy(3); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +echo "====", PHP_EOL; + +$d = Time\Duration::fromSeconds(715_827_882, 666666666); + +echo f($d->multiplyBy(3)), PHP_EOL; + +try { + $d->multiplyBy(4); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- ++2147483647.999999999 +Time\TimeException: The maximum representable range is 2_147_483_647 seconds (roughly 68 years) +==== ++2147483647.999999998 +Time\TimeException: The maximum representable range is 2_147_483_647 seconds (roughly 68 years) +==== ++2147483647.999999998 +Time\TimeException: The maximum representable range is 2_147_483_647 seconds (roughly 68 years) diff --git a/tests/Time/phpt/methods/multiplyBy_64.phpt b/tests/Time/phpt/methods/multiplyBy_64.phpt new file mode 100644 index 00000000..04b220fe --- /dev/null +++ b/tests/Time/phpt/methods/multiplyBy_64.phpt @@ -0,0 +1,70 @@ +--TEST-- +Time\Duration::multiplyBy() (64 bit variation) +--SKIPIF-- + +--FILE-- +multiplyBy(1)), PHP_EOL; + +try { + $d->multiplyBy(2); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +echo "====", PHP_EOL; + +$d = Time\Duration::fromSeconds(4_611_686_017, 999999999); + +echo f($d->multiplyBy(2)), PHP_EOL; + +try { + $d->multiplyBy(3); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +echo "====", PHP_EOL; + +$d = Time\Duration::fromSeconds(3_074_457_345, 333333333); + +echo f($d->multiplyBy(3)), PHP_EOL; + +try { + $d->multiplyBy(4); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +echo "====", PHP_EOL; + +$d = Time\Duration::fromSeconds(0, 1); + +echo f($d->multiplyBy(9_223_372_035_999999999)), PHP_EOL; + +try { + $d->multiplyBy(9_223_372_036_000000000); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- ++9223372035.999999999 +Time\TimeException: The maximum representable range is 9_223_372_035 seconds (roughly 292 years) +==== ++9223372035.999999998 +Time\TimeException: The maximum representable range is 9_223_372_035 seconds (roughly 292 years) +==== ++9223372035.999999999 +Time\TimeException: The maximum representable range is 9_223_372_035 seconds (roughly 292 years) +==== ++9223372035.999999999 +Time\TimeException: The maximum representable range is 9_223_372_035 seconds (roughly 292 years) diff --git a/tests/Time/phpt/methods/negate.phpt b/tests/Time/phpt/methods/negate.phpt new file mode 100644 index 00000000..ce156cd2 --- /dev/null +++ b/tests/Time/phpt/methods/negate.phpt @@ -0,0 +1,29 @@ +--TEST-- +Time\Duration::negate() +--FILE-- +negate()), PHP_EOL; +echo f(Time\Duration::fromSeconds(0, 0)->negate()->negate()), PHP_EOL; + +echo f(Time\Duration::fromSeconds(1, 0)->negate()), PHP_EOL; +echo f(Time\Duration::fromSeconds(1, 0)->negate()->negate()), PHP_EOL; + +echo f(Time\Duration::fromSeconds(0, 1)->negate()), PHP_EOL; +echo f(Time\Duration::fromSeconds(0, 1)->negate()->negate()), PHP_EOL; + +echo f(Time\Duration::fromSeconds(1, 1)->negate()), PHP_EOL; +echo f(Time\Duration::fromSeconds(1, 1)->negate()->negate()), PHP_EOL; + +?> +--EXPECT-- + +0.000000000 + +0.000000000 + -1.000000000 + +1.000000000 + -0.000000001 + +0.000000001 + -1.000000001 + +1.000000001 diff --git a/tests/Time/phpt/methods/sub.phpt b/tests/Time/phpt/methods/sub.phpt new file mode 100644 index 00000000..5552a249 --- /dev/null +++ b/tests/Time/phpt/methods/sub.phpt @@ -0,0 +1,371 @@ +--TEST-- +Time\Duration::sub() +--FILE-- +sub($b); + echo f($a, pad: false, plus_sign: false), " - ", f($b, pad: false, plus_sign: false), " = ", f($result, pad: false), PHP_EOL; + if (PHP_INT_SIZE != 4 && (as_int($a) - as_int($b) !== as_int($result))) { + throw new \Exception('Verification failed'); + } + } +} + +?> +--EXPECT-- +======== + 0.000000000 - 0.000000000 = +0.000000000 + 0.000000000 - 0.000000001 = -0.000000001 + 0.000000000 - 0.000000002 = -0.000000002 + 0.000000000 - 1.000000000 = -1.000000000 + 0.000000000 - 1.000000001 = -1.000000001 + 0.000000000 - 1.000000002 = -1.000000002 + 0.000000000 - 2.000000000 = -2.000000000 + 0.000000000 - 2.000000001 = -2.000000001 + 0.000000000 - 2.000000002 = -2.000000002 +==== + 0.000000000 - -0.000000001 = +0.000000001 + 0.000000000 - -0.000000002 = +0.000000002 + 0.000000000 - -1.000000000 = +1.000000000 + 0.000000000 - -1.000000001 = +1.000000001 + 0.000000000 - -1.000000002 = +1.000000002 + 0.000000000 - -2.000000000 = +2.000000000 + 0.000000000 - -2.000000001 = +2.000000001 + 0.000000000 - -2.000000002 = +2.000000002 +======== + 0.000000001 - 0.000000000 = +0.000000001 + 0.000000001 - 0.000000001 = +0.000000000 + 0.000000001 - 0.000000002 = -0.000000001 + 0.000000001 - 1.000000000 = -0.999999999 + 0.000000001 - 1.000000001 = -1.000000000 + 0.000000001 - 1.000000002 = -1.000000001 + 0.000000001 - 2.000000000 = -1.999999999 + 0.000000001 - 2.000000001 = -2.000000000 + 0.000000001 - 2.000000002 = -2.000000001 +==== + 0.000000001 - -0.000000001 = +0.000000002 + 0.000000001 - -0.000000002 = +0.000000003 + 0.000000001 - -1.000000000 = +1.000000001 + 0.000000001 - -1.000000001 = +1.000000002 + 0.000000001 - -1.000000002 = +1.000000003 + 0.000000001 - -2.000000000 = +2.000000001 + 0.000000001 - -2.000000001 = +2.000000002 + 0.000000001 - -2.000000002 = +2.000000003 +======== + 0.000000002 - 0.000000000 = +0.000000002 + 0.000000002 - 0.000000001 = +0.000000001 + 0.000000002 - 0.000000002 = +0.000000000 + 0.000000002 - 1.000000000 = -0.999999998 + 0.000000002 - 1.000000001 = -0.999999999 + 0.000000002 - 1.000000002 = -1.000000000 + 0.000000002 - 2.000000000 = -1.999999998 + 0.000000002 - 2.000000001 = -1.999999999 + 0.000000002 - 2.000000002 = -2.000000000 +==== + 0.000000002 - -0.000000001 = +0.000000003 + 0.000000002 - -0.000000002 = +0.000000004 + 0.000000002 - -1.000000000 = +1.000000002 + 0.000000002 - -1.000000001 = +1.000000003 + 0.000000002 - -1.000000002 = +1.000000004 + 0.000000002 - -2.000000000 = +2.000000002 + 0.000000002 - -2.000000001 = +2.000000003 + 0.000000002 - -2.000000002 = +2.000000004 +======== + 1.000000000 - 0.000000000 = +1.000000000 + 1.000000000 - 0.000000001 = +0.999999999 + 1.000000000 - 0.000000002 = +0.999999998 + 1.000000000 - 1.000000000 = +0.000000000 + 1.000000000 - 1.000000001 = -0.000000001 + 1.000000000 - 1.000000002 = -0.000000002 + 1.000000000 - 2.000000000 = -1.000000000 + 1.000000000 - 2.000000001 = -1.000000001 + 1.000000000 - 2.000000002 = -1.000000002 +==== + 1.000000000 - -0.000000001 = +1.000000001 + 1.000000000 - -0.000000002 = +1.000000002 + 1.000000000 - -1.000000000 = +2.000000000 + 1.000000000 - -1.000000001 = +2.000000001 + 1.000000000 - -1.000000002 = +2.000000002 + 1.000000000 - -2.000000000 = +3.000000000 + 1.000000000 - -2.000000001 = +3.000000001 + 1.000000000 - -2.000000002 = +3.000000002 +======== + 1.000000001 - 0.000000000 = +1.000000001 + 1.000000001 - 0.000000001 = +1.000000000 + 1.000000001 - 0.000000002 = +0.999999999 + 1.000000001 - 1.000000000 = +0.000000001 + 1.000000001 - 1.000000001 = +0.000000000 + 1.000000001 - 1.000000002 = -0.000000001 + 1.000000001 - 2.000000000 = -0.999999999 + 1.000000001 - 2.000000001 = -1.000000000 + 1.000000001 - 2.000000002 = -1.000000001 +==== + 1.000000001 - -0.000000001 = +1.000000002 + 1.000000001 - -0.000000002 = +1.000000003 + 1.000000001 - -1.000000000 = +2.000000001 + 1.000000001 - -1.000000001 = +2.000000002 + 1.000000001 - -1.000000002 = +2.000000003 + 1.000000001 - -2.000000000 = +3.000000001 + 1.000000001 - -2.000000001 = +3.000000002 + 1.000000001 - -2.000000002 = +3.000000003 +======== + 1.000000002 - 0.000000000 = +1.000000002 + 1.000000002 - 0.000000001 = +1.000000001 + 1.000000002 - 0.000000002 = +1.000000000 + 1.000000002 - 1.000000000 = +0.000000002 + 1.000000002 - 1.000000001 = +0.000000001 + 1.000000002 - 1.000000002 = +0.000000000 + 1.000000002 - 2.000000000 = -0.999999998 + 1.000000002 - 2.000000001 = -0.999999999 + 1.000000002 - 2.000000002 = -1.000000000 +==== + 1.000000002 - -0.000000001 = +1.000000003 + 1.000000002 - -0.000000002 = +1.000000004 + 1.000000002 - -1.000000000 = +2.000000002 + 1.000000002 - -1.000000001 = +2.000000003 + 1.000000002 - -1.000000002 = +2.000000004 + 1.000000002 - -2.000000000 = +3.000000002 + 1.000000002 - -2.000000001 = +3.000000003 + 1.000000002 - -2.000000002 = +3.000000004 +======== + 2.000000000 - 0.000000000 = +2.000000000 + 2.000000000 - 0.000000001 = +1.999999999 + 2.000000000 - 0.000000002 = +1.999999998 + 2.000000000 - 1.000000000 = +1.000000000 + 2.000000000 - 1.000000001 = +0.999999999 + 2.000000000 - 1.000000002 = +0.999999998 + 2.000000000 - 2.000000000 = +0.000000000 + 2.000000000 - 2.000000001 = -0.000000001 + 2.000000000 - 2.000000002 = -0.000000002 +==== + 2.000000000 - -0.000000001 = +2.000000001 + 2.000000000 - -0.000000002 = +2.000000002 + 2.000000000 - -1.000000000 = +3.000000000 + 2.000000000 - -1.000000001 = +3.000000001 + 2.000000000 - -1.000000002 = +3.000000002 + 2.000000000 - -2.000000000 = +4.000000000 + 2.000000000 - -2.000000001 = +4.000000001 + 2.000000000 - -2.000000002 = +4.000000002 +======== + 2.000000001 - 0.000000000 = +2.000000001 + 2.000000001 - 0.000000001 = +2.000000000 + 2.000000001 - 0.000000002 = +1.999999999 + 2.000000001 - 1.000000000 = +1.000000001 + 2.000000001 - 1.000000001 = +1.000000000 + 2.000000001 - 1.000000002 = +0.999999999 + 2.000000001 - 2.000000000 = +0.000000001 + 2.000000001 - 2.000000001 = +0.000000000 + 2.000000001 - 2.000000002 = -0.000000001 +==== + 2.000000001 - -0.000000001 = +2.000000002 + 2.000000001 - -0.000000002 = +2.000000003 + 2.000000001 - -1.000000000 = +3.000000001 + 2.000000001 - -1.000000001 = +3.000000002 + 2.000000001 - -1.000000002 = +3.000000003 + 2.000000001 - -2.000000000 = +4.000000001 + 2.000000001 - -2.000000001 = +4.000000002 + 2.000000001 - -2.000000002 = +4.000000003 +======== + 2.000000002 - 0.000000000 = +2.000000002 + 2.000000002 - 0.000000001 = +2.000000001 + 2.000000002 - 0.000000002 = +2.000000000 + 2.000000002 - 1.000000000 = +1.000000002 + 2.000000002 - 1.000000001 = +1.000000001 + 2.000000002 - 1.000000002 = +1.000000000 + 2.000000002 - 2.000000000 = +0.000000002 + 2.000000002 - 2.000000001 = +0.000000001 + 2.000000002 - 2.000000002 = +0.000000000 +==== + 2.000000002 - -0.000000001 = +2.000000003 + 2.000000002 - -0.000000002 = +2.000000004 + 2.000000002 - -1.000000000 = +3.000000002 + 2.000000002 - -1.000000001 = +3.000000003 + 2.000000002 - -1.000000002 = +3.000000004 + 2.000000002 - -2.000000000 = +4.000000002 + 2.000000002 - -2.000000001 = +4.000000003 + 2.000000002 - -2.000000002 = +4.000000004 +======== +-0.000000001 - 0.000000000 = -0.000000001 +-0.000000001 - 0.000000001 = -0.000000002 +-0.000000001 - 0.000000002 = -0.000000003 +-0.000000001 - 1.000000000 = -1.000000001 +-0.000000001 - 1.000000001 = -1.000000002 +-0.000000001 - 1.000000002 = -1.000000003 +-0.000000001 - 2.000000000 = -2.000000001 +-0.000000001 - 2.000000001 = -2.000000002 +-0.000000001 - 2.000000002 = -2.000000003 +==== +-0.000000001 - -0.000000001 = +0.000000000 +-0.000000001 - -0.000000002 = +0.000000001 +-0.000000001 - -1.000000000 = +0.999999999 +-0.000000001 - -1.000000001 = +1.000000000 +-0.000000001 - -1.000000002 = +1.000000001 +-0.000000001 - -2.000000000 = +1.999999999 +-0.000000001 - -2.000000001 = +2.000000000 +-0.000000001 - -2.000000002 = +2.000000001 +======== +-0.000000002 - 0.000000000 = -0.000000002 +-0.000000002 - 0.000000001 = -0.000000003 +-0.000000002 - 0.000000002 = -0.000000004 +-0.000000002 - 1.000000000 = -1.000000002 +-0.000000002 - 1.000000001 = -1.000000003 +-0.000000002 - 1.000000002 = -1.000000004 +-0.000000002 - 2.000000000 = -2.000000002 +-0.000000002 - 2.000000001 = -2.000000003 +-0.000000002 - 2.000000002 = -2.000000004 +==== +-0.000000002 - -0.000000001 = -0.000000001 +-0.000000002 - -0.000000002 = +0.000000000 +-0.000000002 - -1.000000000 = +0.999999998 +-0.000000002 - -1.000000001 = +0.999999999 +-0.000000002 - -1.000000002 = +1.000000000 +-0.000000002 - -2.000000000 = +1.999999998 +-0.000000002 - -2.000000001 = +1.999999999 +-0.000000002 - -2.000000002 = +2.000000000 +======== +-1.000000000 - 0.000000000 = -1.000000000 +-1.000000000 - 0.000000001 = -1.000000001 +-1.000000000 - 0.000000002 = -1.000000002 +-1.000000000 - 1.000000000 = -2.000000000 +-1.000000000 - 1.000000001 = -2.000000001 +-1.000000000 - 1.000000002 = -2.000000002 +-1.000000000 - 2.000000000 = -3.000000000 +-1.000000000 - 2.000000001 = -3.000000001 +-1.000000000 - 2.000000002 = -3.000000002 +==== +-1.000000000 - -0.000000001 = -0.999999999 +-1.000000000 - -0.000000002 = -0.999999998 +-1.000000000 - -1.000000000 = +0.000000000 +-1.000000000 - -1.000000001 = +0.000000001 +-1.000000000 - -1.000000002 = +0.000000002 +-1.000000000 - -2.000000000 = +1.000000000 +-1.000000000 - -2.000000001 = +1.000000001 +-1.000000000 - -2.000000002 = +1.000000002 +======== +-1.000000001 - 0.000000000 = -1.000000001 +-1.000000001 - 0.000000001 = -1.000000002 +-1.000000001 - 0.000000002 = -1.000000003 +-1.000000001 - 1.000000000 = -2.000000001 +-1.000000001 - 1.000000001 = -2.000000002 +-1.000000001 - 1.000000002 = -2.000000003 +-1.000000001 - 2.000000000 = -3.000000001 +-1.000000001 - 2.000000001 = -3.000000002 +-1.000000001 - 2.000000002 = -3.000000003 +==== +-1.000000001 - -0.000000001 = -1.000000000 +-1.000000001 - -0.000000002 = -0.999999999 +-1.000000001 - -1.000000000 = -0.000000001 +-1.000000001 - -1.000000001 = +0.000000000 +-1.000000001 - -1.000000002 = +0.000000001 +-1.000000001 - -2.000000000 = +0.999999999 +-1.000000001 - -2.000000001 = +1.000000000 +-1.000000001 - -2.000000002 = +1.000000001 +======== +-1.000000002 - 0.000000000 = -1.000000002 +-1.000000002 - 0.000000001 = -1.000000003 +-1.000000002 - 0.000000002 = -1.000000004 +-1.000000002 - 1.000000000 = -2.000000002 +-1.000000002 - 1.000000001 = -2.000000003 +-1.000000002 - 1.000000002 = -2.000000004 +-1.000000002 - 2.000000000 = -3.000000002 +-1.000000002 - 2.000000001 = -3.000000003 +-1.000000002 - 2.000000002 = -3.000000004 +==== +-1.000000002 - -0.000000001 = -1.000000001 +-1.000000002 - -0.000000002 = -1.000000000 +-1.000000002 - -1.000000000 = -0.000000002 +-1.000000002 - -1.000000001 = -0.000000001 +-1.000000002 - -1.000000002 = +0.000000000 +-1.000000002 - -2.000000000 = +0.999999998 +-1.000000002 - -2.000000001 = +0.999999999 +-1.000000002 - -2.000000002 = +1.000000000 +======== +-2.000000000 - 0.000000000 = -2.000000000 +-2.000000000 - 0.000000001 = -2.000000001 +-2.000000000 - 0.000000002 = -2.000000002 +-2.000000000 - 1.000000000 = -3.000000000 +-2.000000000 - 1.000000001 = -3.000000001 +-2.000000000 - 1.000000002 = -3.000000002 +-2.000000000 - 2.000000000 = -4.000000000 +-2.000000000 - 2.000000001 = -4.000000001 +-2.000000000 - 2.000000002 = -4.000000002 +==== +-2.000000000 - -0.000000001 = -1.999999999 +-2.000000000 - -0.000000002 = -1.999999998 +-2.000000000 - -1.000000000 = -1.000000000 +-2.000000000 - -1.000000001 = -0.999999999 +-2.000000000 - -1.000000002 = -0.999999998 +-2.000000000 - -2.000000000 = +0.000000000 +-2.000000000 - -2.000000001 = +0.000000001 +-2.000000000 - -2.000000002 = +0.000000002 +======== +-2.000000001 - 0.000000000 = -2.000000001 +-2.000000001 - 0.000000001 = -2.000000002 +-2.000000001 - 0.000000002 = -2.000000003 +-2.000000001 - 1.000000000 = -3.000000001 +-2.000000001 - 1.000000001 = -3.000000002 +-2.000000001 - 1.000000002 = -3.000000003 +-2.000000001 - 2.000000000 = -4.000000001 +-2.000000001 - 2.000000001 = -4.000000002 +-2.000000001 - 2.000000002 = -4.000000003 +==== +-2.000000001 - -0.000000001 = -2.000000000 +-2.000000001 - -0.000000002 = -1.999999999 +-2.000000001 - -1.000000000 = -1.000000001 +-2.000000001 - -1.000000001 = -1.000000000 +-2.000000001 - -1.000000002 = -0.999999999 +-2.000000001 - -2.000000000 = -0.000000001 +-2.000000001 - -2.000000001 = +0.000000000 +-2.000000001 - -2.000000002 = +0.000000001 +======== +-2.000000002 - 0.000000000 = -2.000000002 +-2.000000002 - 0.000000001 = -2.000000003 +-2.000000002 - 0.000000002 = -2.000000004 +-2.000000002 - 1.000000000 = -3.000000002 +-2.000000002 - 1.000000001 = -3.000000003 +-2.000000002 - 1.000000002 = -3.000000004 +-2.000000002 - 2.000000000 = -4.000000002 +-2.000000002 - 2.000000001 = -4.000000003 +-2.000000002 - 2.000000002 = -4.000000004 +==== +-2.000000002 - -0.000000001 = -2.000000001 +-2.000000002 - -0.000000002 = -2.000000000 +-2.000000002 - -1.000000000 = -1.000000002 +-2.000000002 - -1.000000001 = -1.000000001 +-2.000000002 - -1.000000002 = -1.000000000 +-2.000000002 - -2.000000000 = -0.000000002 +-2.000000002 - -2.000000001 = -0.000000001 +-2.000000002 - -2.000000002 = +0.000000000 diff --git a/tests/Time/phpt/methods/sub_32.phpt b/tests/Time/phpt/methods/sub_32.phpt new file mode 100644 index 00000000..298b2bfe --- /dev/null +++ b/tests/Time/phpt/methods/sub_32.phpt @@ -0,0 +1,23 @@ +--TEST-- +Time\Duration::sub() (32 bit variation) +--SKIPIF-- + +--FILE-- +negate(); +$b = Time\Duration::fromSeconds(2_147_483_647, 999999999); + +try { + $a->sub($b); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Time\TimeException: The maximum representable range is 2_147_483_647 seconds (roughly 68 years) diff --git a/tests/Time/phpt/methods/sub_64.phpt b/tests/Time/phpt/methods/sub_64.phpt new file mode 100644 index 00000000..c8f8fac5 --- /dev/null +++ b/tests/Time/phpt/methods/sub_64.phpt @@ -0,0 +1,23 @@ +--TEST-- +Time\Duration::sub() (64 bit variation) +--SKIPIF-- + +--FILE-- +negate(); +$b = Time\Duration::fromSeconds(9_223_372_035, 999999999); + +try { + $a->sub($b); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Time\TimeException: The maximum representable range is 9_223_372_035 seconds (roughly 292 years)