Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 6 additions & 11 deletions src/TimeSpan.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,20 @@ public static function fromNanoseconds(int|float $nanoseconds): self
return new self($nanoseconds);
}

$nanoseconds = \sprintf('%.0f', round($nanoseconds));
$nanoseconds = round($nanoseconds);

if ($nanoseconds > 0 && self::comparePositiveNumericStrings($nanoseconds, (string) PHP_INT_MAX) > 0
|| $nanoseconds < 0 && self::compareNegativeNumericStrings($nanoseconds, (string) PHP_INT_MIN) < 0
) {
if (self::isOutOfBounds($nanoseconds)) {
throw new \OutOfBoundsException('The specified time span cannot be expressed as integer nanoseconds due to overflow.');
}

return new self((int) $nanoseconds);
}

private static function comparePositiveNumericStrings(string $a, string $b): int
private static function isOutOfBounds(float $nanoseconds): bool
{
return \strlen($a) <=> \strlen($b) ?: strcmp($a, $b);
}

private static function compareNegativeNumericStrings(string $a, string $b): int
{
return -(\strlen($a) <=> \strlen($b) ?: strcmp($a, $b));
return !is_finite($nanoseconds)
|| $nanoseconds >= ($bound = 2 ** 63)
|| $nanoseconds < -$bound;
}

public static function fromMicroseconds(int|float $microseconds): self
Expand Down
13 changes: 13 additions & 0 deletions tests/TimeSpanTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ public function testConstructorWithoutArgs(): void
#[TestWith([100.999_99, 101])]
#[TestWith([PHP_INT_MAX, PHP_INT_MAX])]
#[TestWith([PHP_INT_MIN, PHP_INT_MIN])]
#[TestWith([PHP_INT_MIN - 1_024, PHP_INT_MIN])]
#[TestWith([9_223_372_036_854_775_000.0, 9_223_372_036_854_774_784])]
#[TestWith([-9_223_372_036_854_775_000.0, -9_223_372_036_854_774_784])]
public function testFromNanoseconds(int|float $nanoseconds, int $expected): void
Expand Down Expand Up @@ -322,6 +323,8 @@ public function testFromDaysPHPSince84(float $days, int $expected): void
#[TestWith([106_751.991_2])]
#[TestWith([-106_752])]
#[TestWith([-106_751.991_2])]
#[TestWith([NAN])]
#[TestWith([INF])]
public function testFromDaysThrowsOutOfBounds(int|float $days): void
{
$this->expectException(\OutOfBoundsException::class);
Expand All @@ -330,6 +333,16 @@ public function testFromDaysThrowsOutOfBounds(int|float $days): void
TimeSpan::fromDays($days);
}

#[TestWith([PHP_INT_MAX + 1])]
#[TestWith([PHP_INT_MIN - 1_025])]
public function testFromNanosecondsThrowsOutOfBounds(float $nanoseconds): void
{
$this->expectException(\OutOfBoundsException::class);
$this->expectExceptionMessage('The specified time span cannot be expressed as integer nanoseconds due to overflow.');

TimeSpan::fromNanoseconds($nanoseconds);
}

#[TestWith([100_000, 100.0, 100])]
#[TestWith([100_100, 100.1, 100])]
#[TestWith([100_500, 100.5, 101])]
Expand Down