Support microtime in returned DateTimeInterface instances

Fixes #90 and supersedes #93.
This commit is contained in:
Ben Ramsey
2020-01-19 22:06:42 -06:00
parent 124e267553
commit a8d52100de
19 changed files with 308 additions and 126 deletions
+5 -3
View File
@@ -62,6 +62,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
* Add `Converter\Number\GenericNumberConverter` and
`Converter\Time\GenericTimeConverter` which will use the calculator provided
to convert numbers and time to values for UUIDs.
* The `\DateTimeInterface` instance returned by `UuidInterface::getDateTime()`
(and now `Rfc4122\UuidV1::getDateTime()`) now includes microseconds, as
specified by the version 1 UUID.
### Changed
@@ -91,9 +94,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
* `NumberConverterInterface::fromHex(string $hex): string`
* `NumberConverterInterface::toHex(string $number): string`
* `TimeConverterInterface::calculateTime(string $seconds, string $microSeconds): array`
* `TimeConverterInterface::convertTime(string $timestamp): string`
* `UnsatisfiedDependencyException` and `UnsupportedOperationException` are now
descended from `\LogicException`. Previously, they descended from `\RuntimeException`.
* `UnsupportedOperationException` is now descended from `\LogicException`.
Previously, it descended from `\RuntimeException`.
* When encoding to bytes or decoding from bytes, `OrderedTimeCodec` now checks
whether the UUID is an RFC 4122 variant, version 1 UUID. If not, it will throw
an exception—`InvalidArgumentException` when using
+1 -1
View File
@@ -19,7 +19,7 @@ use Ramsey\Uuid\Math\BrickMathCalculator;
/**
* Previously used to integrate moontoast/math as a bignum arithmetic library,
* BigNumberConverter is deprecated in favor of ArbitraryPrecisionNumberConverter
* BigNumberConverter is deprecated in favor of GenericNumberConverter
*
* @deprecated Transition to {@see GenericNumberConverter}.
*/
@@ -16,10 +16,12 @@ namespace Ramsey\Uuid\Converter\Time;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Math\BrickMathCalculator;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Time;
/**
* Previously used to integrate moontoast/math as a bignum arithmetic library,
* BigNumberTimeConverter is deprecated in favor of ArbitraryPrecisionTimeConverter
* BigNumberTimeConverter is deprecated in favor of GenericTimeConverter
*
* @deprecated Transition to {@see GenericTimeConverter}.
*/
@@ -48,8 +50,8 @@ class BigNumberTimeConverter implements TimeConverterInterface
* @inheritDoc
* @psalm-pure
*/
public function convertTime(string $timestamp): string
public function convertTime(Hexadecimal $uuidTimestamp): Time
{
return $this->converter->convertTime($timestamp);
return $this->converter->convertTime($uuidTimestamp);
}
}
+14 -7
View File
@@ -17,6 +17,7 @@ namespace Ramsey\Uuid\Converter\Time;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Math\CalculatorInterface;
use Ramsey\Uuid\Math\RoundingMode;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\IntegerValue;
use Ramsey\Uuid\Type\Time;
@@ -54,6 +55,7 @@ class GenericTimeConverter implements TimeConverterInterface
new IntegerValue('10')
);
/** @var IntegerValue $uuidTime */
$uuidTime = $this->calculator->add(
$sec,
$usec,
@@ -78,12 +80,14 @@ class GenericTimeConverter implements TimeConverterInterface
* @inheritDoc
* @psalm-pure
*/
public function convertTime(string $timestamp): string
public function convertTime(Hexadecimal $uuidTimestamp): Time
{
$timestamp = new IntegerValue($timestamp);
$unixTimestamp = $this->calculator->subtract(
$timestamp,
// From the total, subtract the number of 100-nanosecond intervals from
// the UUID epoch (Gregorian calendar date) to the Unix epoch. This
// gives us the number of 100-nanosecond intervals from the Unix epoch,
// which also includes the microtime.
$epochNanoseconds = $this->calculator->subtract(
$this->calculator->toIntegerValue($uuidTimestamp),
new IntegerValue('122192928000000000')
);
@@ -91,10 +95,13 @@ class GenericTimeConverter implements TimeConverterInterface
// into the next second, giving us the wrong Unix timestamp.
$unixTimestamp = $this->calculator->divide(
RoundingMode::DOWN,
$unixTimestamp,
6,
$epochNanoseconds,
new IntegerValue('10000000')
);
return $unixTimestamp->toString();
$split = explode('.', (string) $unixTimestamp, 2);
return new Time($split[0], $split[1] ?? 0);
}
}
+72 -12
View File
@@ -16,7 +16,10 @@ namespace Ramsey\Uuid\Converter\Time;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Math\BrickMathCalculator;
use Ramsey\Uuid\Math\CalculatorInterface;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\IntegerValue;
use Ramsey\Uuid\Type\Time;
/**
* PhpTimeConverter uses built-in PHP functions and standard math operations
@@ -25,18 +28,36 @@ use Ramsey\Uuid\Type\IntegerValue;
*/
class PhpTimeConverter implements TimeConverterInterface
{
/**
* @var CalculatorInterface
*/
private $calculator;
/**
* @var TimeConverterInterface
*/
private $fallbackConverter;
public function __construct(?TimeConverterInterface $fallbackConverter = null)
{
if ($fallbackConverter === null) {
$fallbackConverter = new GenericTimeConverter(new BrickMathCalculator());
/**
* @var int
*/
private $phpPrecision;
public function __construct(
?CalculatorInterface $calculator = null,
?TimeConverterInterface $fallbackConverter = null
) {
if ($calculator === null) {
$calculator = new BrickMathCalculator();
}
if ($fallbackConverter === null) {
$fallbackConverter = new GenericTimeConverter($calculator);
}
$this->calculator = $calculator;
$this->fallbackConverter = $fallbackConverter;
$this->phpPrecision = (int) ini_get('precision');
}
/**
@@ -79,18 +100,57 @@ class PhpTimeConverter implements TimeConverterInterface
* @inheritDoc
* @psalm-pure
*/
public function convertTime(string $timestamp): string
public function convertTime(Hexadecimal $uuidTimestamp): Time
{
$timestamp = new IntegerValue($timestamp);
$timestamp = $this->calculator->toIntegerValue($uuidTimestamp);
$unixTime = ((int) $timestamp->toString() - 0x01b21dd213814000) / 10000000;
$splitTime = $this->splitTime(
((int) $timestamp->toString() - 0x01b21dd213814000) / 10000000
);
if (!is_int($unixTime)) {
return $this->fallbackConverter->convertTime(
$timestamp->toString()
);
if (count($splitTime) === 0) {
return $this->fallbackConverter->convertTime($uuidTimestamp);
}
return (string) $unixTime;
return new Time($splitTime['sec'], $splitTime['usec']);
}
/**
* @param int|float $time The time to split into seconds and microseconds
*
* @return string[]
*
* @psalm-pure
*/
private function splitTime($time): array
{
$split = explode('.', (string) $time, 2);
// If the $time value is a float but $split only has 1 element, then the
// float math was rounded up to the next second, so we want to return
// an empty array to allow use of the fallback converter.
if (is_float($time) && count($split) === 1) {
return [];
}
if (count($split) === 1) {
return [
'sec' => $split[0],
'usec' => '0',
];
}
// If the microseconds are less than six characters AND the length of
// the number is greater than or equal the PHP precision , then it's
// possible that we lost some precision for the microseconds. Return an
// empty array, so that we can choose to use the fallback converter.
if (strlen($split[1]) < 6 && strlen((string) $time) >= $this->phpPrecision) {
return [];
}
return [
'sec' => $split[0],
'usec' => str_pad($split[1], 6, '0', STR_PAD_RIGHT),
];
}
}
+7 -5
View File
@@ -14,6 +14,9 @@ declare(strict_types=1);
namespace Ramsey\Uuid\Converter;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Time;
/**
* A time converter converts timestamps into representations that may be used
* in UUIDs
@@ -40,14 +43,13 @@ interface TimeConverterInterface
/**
* Converts a timestamp extracted from a UUID to a Unix timestamp
*
* @param string $timestamp A string integer representation of a UUID
* @param Hexadecimal $uuidTimestamp A hexadecimal representation of a UUID
* timestamp; a UUID timestamp is a count of 100-nanosecond intervals
* since UTC 00:00:00.00, 15 October 1582; this must be a numeric string
* to accommodate unsigned integers greater than PHP_INT_MAX.
* since UTC 00:00:00.00, 15 October 1582.
*
* @return string String representation of an integer
* @return Time An instance of {@see Time}
*
* @psalm-pure
*/
public function convertTime(string $timestamp): string;
public function convertTime(Hexadecimal $uuidTimestamp): Time;
}
+6 -9
View File
@@ -136,18 +136,15 @@ trait DeprecatedUuidMethodsTrait
throw new UnsupportedOperationException('Not a time-based UUID');
}
$unixTime = $this->timeConverter->convertTime(
$this->numberConverter->fromHex($this->fields->getTimestamp()->toString())
);
$time = $this->timeConverter->convertTime($this->fields->getTimestamp());
try {
return new DateTimeImmutable("@{$unixTime}");
} catch (Throwable $exception) {
throw new DateTimeException(
$exception->getMessage(),
(int) $exception->getCode(),
$exception
return new DateTimeImmutable(
date('Y-m-d H:i:s', (int) $time->getSeconds()->toString()) . '.'
. str_pad($time->getMicroSeconds()->toString(), 6, '0', STR_PAD_LEFT)
);
} catch (Throwable $e) {
throw new DateTimeException($e->getMessage(), (int) $e->getCode(), $e);
}
}
+1 -1
View File
@@ -349,7 +349,7 @@ class FeatureSet
$genericConverter = new GenericTimeConverter($calculator);
if ($this->is64BitSystem()) {
return new PhpTimeConverter($genericConverter);
return new PhpTimeConverter($calculator, $genericConverter);
}
return $genericConverter;
+20 -8
View File
@@ -14,12 +14,15 @@ declare(strict_types=1);
namespace Ramsey\Uuid\Math;
use Brick\Math\BigDecimal;
use Brick\Math\BigInteger;
use Brick\Math\Exception\MathException;
use Brick\Math\RoundingMode as BrickMathRounding;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Type\Decimal;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\IntegerValue;
use Ramsey\Uuid\Type\NumberInterface;
/**
* A calculator using the brick/math library for arbitrary-precision arithmetic
@@ -41,7 +44,7 @@ final class BrickMathCalculator implements CalculatorInterface
RoundingMode::HALF_EVEN => BrickMathRounding::HALF_EVEN,
];
public function add(IntegerValue $augend, IntegerValue ...$addends): IntegerValue
public function add(NumberInterface $augend, NumberInterface ...$addends): NumberInterface
{
/** @psalm-suppress ImpureMethodCall */
$sum = BigInteger::of($augend->toString());
@@ -54,7 +57,7 @@ final class BrickMathCalculator implements CalculatorInterface
return new IntegerValue((string) $sum);
}
public function subtract(IntegerValue $minuend, IntegerValue ...$subtrahends): IntegerValue
public function subtract(NumberInterface $minuend, NumberInterface ...$subtrahends): NumberInterface
{
/** @psalm-suppress ImpureMethodCall */
$difference = BigInteger::of($minuend->toString());
@@ -67,7 +70,7 @@ final class BrickMathCalculator implements CalculatorInterface
return new IntegerValue((string) $difference);
}
public function multiply(IntegerValue $multiplicand, IntegerValue ...$multipliers): IntegerValue
public function multiply(NumberInterface $multiplicand, NumberInterface ...$multipliers): NumberInterface
{
/** @psalm-suppress ImpureMethodCall */
$product = BigInteger::of($multiplicand->toString());
@@ -80,19 +83,28 @@ final class BrickMathCalculator implements CalculatorInterface
return new IntegerValue((string) $product);
}
public function divide(int $roundingMode, IntegerValue $dividend, IntegerValue ...$divisors): IntegerValue
{
public function divide(
int $roundingMode,
int $scale,
NumberInterface $dividend,
NumberInterface ...$divisors
): NumberInterface {
$brickRounding = $this->getBrickRoundingMode($roundingMode);
/** @psalm-suppress ImpureMethodCall */
$quotient = BigInteger::of($dividend->toString());
$quotient = BigDecimal::of($dividend->toString());
foreach ($divisors as $divisor) {
/** @psalm-suppress ImpureMethodCall */
$quotient = $quotient->dividedBy($divisor->toString(), $brickRounding);
$quotient = $quotient->dividedBy($divisor->toString(), $scale, $brickRounding);
}
return new IntegerValue((string) $quotient);
if ($scale === 0) {
/** @psalm-suppress ImpureMethodCall */
return new IntegerValue((string) $quotient->toBigInteger());
}
return new Decimal((string) $quotient);
}
public function fromBase(string $value, int $base): IntegerValue
+23 -16
View File
@@ -16,6 +16,7 @@ namespace Ramsey\Uuid\Math;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\IntegerValue;
use Ramsey\Uuid\Type\NumberInterface;
/**
* A calculator performs arithmetic operations on numbers
@@ -27,45 +28,51 @@ interface CalculatorInterface
/**
* Returns the sum of all the provided parameters
*
* @param IntegerValue $augend The first addend (the integer being added to)
* @param IntegerValue ...$addends The additional integers to a add to the augend
* @param NumberInterface $augend The first addend (the integer being added to)
* @param NumberInterface ...$addends The additional integers to a add to the augend
*
* @return IntegerValue The sum of all the parameters
* @return NumberInterface The sum of all the parameters
*/
public function add(IntegerValue $augend, IntegerValue ...$addends): IntegerValue;
public function add(NumberInterface $augend, NumberInterface ...$addends): NumberInterface;
/**
* Returns the difference of all the provided parameters
*
* @param IntegerValue $minuend The integer being subtracted from
* @param IntegerValue ...$subtrahends The integers to subtract from the minuend
* @param NumberInterface $minuend The integer being subtracted from
* @param NumberInterface ...$subtrahends The integers to subtract from the minuend
*
* @return IntegerValue The difference after subtracting all parameters
* @return NumberInterface The difference after subtracting all parameters
*/
public function subtract(IntegerValue $minuend, IntegerValue ...$subtrahends): IntegerValue;
public function subtract(NumberInterface $minuend, NumberInterface ...$subtrahends): NumberInterface;
/**
* Returns the product of all the provided parameters
*
* @param IntegerValue $multiplicand The integer to be multiplied
* @param IntegerValue ...$multipliers The factors by which to multiply the multiplicand
* @param NumberInterface $multiplicand The integer to be multiplied
* @param NumberInterface ...$multipliers The factors by which to multiply the multiplicand
*
* @return IntegerValue The product of multiplying all the provided parameters
* @return NumberInterface The product of multiplying all the provided parameters
*/
public function multiply(IntegerValue $multiplicand, IntegerValue ...$multipliers): IntegerValue;
public function multiply(NumberInterface $multiplicand, NumberInterface ...$multipliers): NumberInterface;
/**
* Returns the quotient of the provided parameters divided left-to-right
*
* @param int $roundingMode The RoundingMode constant to use for this operation
* @param IntegerValue $dividend The integer to be divided
* @param IntegerValue ...$divisors The integers to divide the dividend, in
* @param int $scale The scale to use for this operation
* @param NumberInterface $dividend The integer to be divided
* @param NumberInterface ...$divisors
* the order in which the division operations should take place
* (left-to-right)
*
* @return IntegerValue The quotient of dividing the provided parameters left-to-right
* @return NumberInterface The quotient of dividing the provided parameters left-to-right
*/
public function divide(int $roundingMode, IntegerValue $dividend, IntegerValue ...$divisors): IntegerValue;
public function divide(
int $roundingMode,
int $scale,
NumberInterface $dividend,
NumberInterface ...$divisors
): NumberInterface;
/**
* Converts a value from an arbitrary base to a base-10 integer value
+5 -4
View File
@@ -40,12 +40,13 @@ final class UuidV1 extends Uuid implements UuidInterface
*/
public function getDateTime(): DateTimeInterface
{
$unixTime = $this->timeConverter->convertTime(
$this->numberConverter->fromHex($this->fields->getTimestamp()->toString())
);
$time = $this->timeConverter->convertTime($this->fields->getTimestamp());
try {
return new DateTimeImmutable("@{$unixTime}");
return new DateTimeImmutable(
date('Y-m-d H:i:s', (int) $time->getSeconds()->toString()) . '.'
. str_pad($time->getMicroSeconds()->toString(), 6, '0', STR_PAD_LEFT)
);
} catch (Throwable $e) {
throw new DateTimeException($e->getMessage(), (int) $e->getCode(), $e);
}
+64
View File
@@ -0,0 +1,64 @@
<?php
/**
* This file is part of the ramsey/uuid library
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
* @license http://opensource.org/licenses/MIT MIT
*/
declare(strict_types=1);
namespace Ramsey\Uuid\Type;
use Ramsey\Uuid\Exception\InvalidArgumentException;
/**
* A value object representing a decimal
*
* This class exists for type-safety purposes, to ensure that decimals
* returned from ramsey/uuid methods as strings are truly decimals and not some
* other kind of string.
*
* To support values as true decimals and not as floats or doubles, we store the
* decimals as strings.
*
* @psalm-immutable
*/
final class Decimal implements NumberInterface
{
/**
* @var string
*/
private $value;
/**
* @param mixed $value The decimal value to store
*/
public function __construct($value)
{
$value = (string) $value;
if (!is_numeric($value)) {
throw new InvalidArgumentException(
'Value must be a signed decimal or a string containing only '
. 'digits 0-9 and, optionally, a decimal point or sign (+ or -)'
);
}
$this->value = $value;
}
public function toString(): string
{
return $this->value;
}
public function __toString(): string
{
return $this->toString();
}
}
+9 -1
View File
@@ -30,7 +30,7 @@ use function ctype_digit;
*
* @psalm-immutable
*/
final class IntegerValue
final class IntegerValue implements NumberInterface
{
/**
* @var string
@@ -58,6 +58,14 @@ final class IntegerValue
);
}
// Trim any leading zeros.
$value = ltrim($value, '0');
// Set to zero if the string is empty after trimming zeros.
if ($value === '') {
$value = '0';
}
// Add the negative sign back to the value.
if ($sign === '-' && $value !== '0') {
$value = $sign . $value;
+25
View File
@@ -0,0 +1,25 @@
<?php
/**
* This file is part of the ramsey/uuid library
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
* @license http://opensource.org/licenses/MIT MIT
*/
declare(strict_types=1);
namespace Ramsey\Uuid\Type;
/**
* NumberInterface ensures consistency in numeric values returned by ramsey/uuid
*/
interface NumberInterface
{
public function toString(): string;
public function __toString(): string;
}
@@ -8,6 +8,7 @@ use Brick\Math\BigInteger;
use Ramsey\Uuid\Converter\Time\BigNumberTimeConverter;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Test\TestCase;
use Ramsey\Uuid\Type\Hexadecimal;
class BigNumberTimeConverterTest extends TestCase
{
@@ -40,9 +41,9 @@ class BigNumberTimeConverterTest extends TestCase
public function testConvertTime(): void
{
$converter = new BigNumberTimeConverter();
$returned = $converter->convertTime('135606608744910000');
$returned = $converter->convertTime(new Hexadecimal('1e1c57dff6f8cb0'));
$this->assertSame('1341368074', $returned);
$this->assertSame('1341368074', $returned->getSeconds()->toString());
}
public function testCalculateTimeThrowsExceptionWhenSecondsIsNotOnlyDigits(): void
@@ -70,17 +71,4 @@ class BigNumberTimeConverterTest extends TestCase
$converter->calculateTime('1234', '56.78');
}
public function testConvertTimeThrowsExceptionWhenTimestampIsNotOnlyDigits(): void
{
$converter = new BigNumberTimeConverter();
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage(
'Value must be a signed integer or a string containing only digits '
. '0-9 and, optionally, a sign (+ or -)'
);
$converter->convertTime('1234.56');
}
}
@@ -7,6 +7,7 @@ namespace Ramsey\Uuid\Test\Converter\Time;
use Ramsey\Uuid\Converter\Time\GenericTimeConverter;
use Ramsey\Uuid\Math\BrickMathCalculator;
use Ramsey\Uuid\Test\TestCase;
use Ramsey\Uuid\Type\Hexadecimal;
class GenericTimeConverterTest extends TestCase
{
@@ -88,14 +89,15 @@ class GenericTimeConverterTest extends TestCase
/**
* @dataProvider provideConvertTime
*/
public function testConvertTime(string $uuidTimestamp, string $unixTimestamp): void
public function testConvertTime(Hexadecimal $uuidTimestamp, string $unixTimestamp, string $microSeconds): void
{
$calculator = new BrickMathCalculator();
$converter = new GenericTimeConverter($calculator);
$result = $converter->convertTime($uuidTimestamp);
$this->assertSame($unixTimestamp, $result);
$this->assertSame($unixTimestamp, $result->getSeconds()->toString());
$this->assertSame($microSeconds, $result->getMicroSeconds()->toString());
}
/**
@@ -105,38 +107,44 @@ class GenericTimeConverterTest extends TestCase
{
return [
[
'uuidTimestamp' => '135606608744910000',
'uuidTimestamp' => new Hexadecimal('1e1c57dff6f8cb0'),
'unixTimestamp' => '1341368074',
'microSeconds' => '491000',
],
[
'uuidTimestamp' => '137979051595210230',
'uuidTimestamp' => new Hexadecimal('1ea333764c71df6'),
'unixTimestamp' => '1578612359',
'microSeconds' => '521023',
],
[
'uuidTimestamp' => '1152921504599999990',
'uuidTimestamp' => new Hexadecimal('fffffffff9785f6'),
'unixTimestamp' => '103072857659',
'microSeconds' => '999999',
],
// This is the last possible time supported by v1 UUIDs. When
// converted to a Unix timestamp, the microseconds are lost.
// 60038-03-11 05:36:10.955161
[
'uuidTimestamp' => '18446744073709551610',
'uuidTimestamp' => new Hexadecimal('fffffffffffffffa'),
'unixTimestamp' => '1832455114570',
'microSeconds' => '955161',
],
// This is the earliest possible date supported by v1 UUIDs:
// 1582-10-15 00:00:00.000000
[
'uuidTimestamp' => '0',
'uuidTimestamp' => new Hexadecimal('000000000000'),
'unixTimestamp' => '-12219292800',
'microSeconds' => '0',
],
// This is the Unix epoch:
// 1970-01-01 00:00:00.000000
[
'uuidTimestamp' => '122192928000000000',
'uuidTimestamp' => new Hexadecimal('1b21dd213814000'),
'unixTimestamp' => '0',
'microSeconds' => '0',
],
];
}
+18 -24
View File
@@ -11,6 +11,7 @@ use Ramsey\Uuid\Converter\Time\PhpTimeConverter;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Math\BrickMathCalculator;
use Ramsey\Uuid\Test\TestCase;
use Ramsey\Uuid\Type\Hexadecimal;
class PhpTimeConverterTest extends TestCase
{
@@ -68,32 +69,19 @@ class PhpTimeConverterTest extends TestCase
$converter->calculateTime('1234', '56.78');
}
public function testConvertTimeThrowsExceptionWhenTimestampIsNotOnlyDigits(): void
{
/** @var Mockery\MockInterface & PhpTimeConverter $converter */
$converter = Mockery::mock(PhpTimeConverter::class)->makePartial();
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage(
'Value must be a signed integer or a string containing only digits '
. '0-9 and, optionally, a sign (+ or -)'
);
$converter->convertTime('1234.56');
}
/**
* @dataProvider provideConvertTime
*/
public function testConvertTime(string $uuidTimestamp, string $unixTimestamp): void
public function testConvertTime(Hexadecimal $uuidTimestamp, string $unixTimestamp, string $microSeconds): void
{
$calculator = new BrickMathCalculator();
$fallbackConverter = new GenericTimeConverter($calculator);
$converter = new PhpTimeConverter($fallbackConverter);
$converter = new PhpTimeConverter($calculator, $fallbackConverter);
$result = $converter->convertTime($uuidTimestamp);
$this->assertSame($unixTimestamp, $result);
$this->assertSame($unixTimestamp, $result->getSeconds()->toString());
$this->assertSame($microSeconds, $result->getMicroSeconds()->toString());
}
/**
@@ -103,38 +91,44 @@ class PhpTimeConverterTest extends TestCase
{
return [
[
'uuidTimestamp' => '135606608744910000',
'uuidTimestamp' => new Hexadecimal('1e1c57dff6f8cb0'),
'unixTimestamp' => '1341368074',
'microSeconds' => '491000',
],
[
'uuidTimestamp' => '137979051595210230',
'uuidTimestamp' => new Hexadecimal('1ea333764c71df6'),
'unixTimestamp' => '1578612359',
'microSeconds' => '521023',
],
[
'uuidTimestamp' => '1152921504599999990',
'uuidTimestamp' => new Hexadecimal('fffffffff9785f6'),
'unixTimestamp' => '103072857659',
'microSeconds' => '999999',
],
// This is the last possible time supported by v1 UUIDs. When
// converted to a Unix timestamp, the microseconds are lost.
// 60038-03-11 05:36:10.955161
[
'uuidTimestamp' => '18446744073709551610',
'uuidTimestamp' => new Hexadecimal('fffffffffffffffa'),
'unixTimestamp' => '1832455114570',
'microSeconds' => '955161',
],
// This is the earliest possible date supported by v1 UUIDs:
// 1582-10-15 00:00:00.000000
[
'uuidTimestamp' => '0',
'uuidTimestamp' => new Hexadecimal('000000000000'),
'unixTimestamp' => '-12219292800',
'microSeconds' => '0',
],
// This is the Unix epoch:
// 1970-01-01 00:00:00.000000
[
'uuidTimestamp' => '122192928000000000',
'uuidTimestamp' => new Hexadecimal('1b21dd213814000'),
'unixTimestamp' => '0',
'microSeconds' => '0',
],
];
}
@@ -148,7 +142,7 @@ class PhpTimeConverterTest extends TestCase
{
$calculator = new BrickMathCalculator();
$fallbackConverter = new GenericTimeConverter($calculator);
$converter = new PhpTimeConverter($fallbackConverter);
$converter = new PhpTimeConverter($calculator, $fallbackConverter);
$result = $converter->calculateTime($seconds, $microSeconds);
+1 -1
View File
@@ -59,7 +59,7 @@ class BrickMathCalculatorTest extends TestCase
$calculator = new BrickMathCalculator();
$result = $calculator->divide(RoundingMode::HALF_UP, $int1, $int2, $int3);
$result = $calculator->divide(RoundingMode::HALF_UP, 0, $int1, $int2, $int3);
$this->assertSame('24', $result->toString());
}
+13 -8
View File
@@ -162,22 +162,26 @@ class UuidTest extends TestCase
// Check a recent date
$uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66');
$this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
$this->assertEquals('2012-07-04T02:14:34+00:00', $uuid->getDateTime()->format('c'));
$this->assertSame('2012-07-04T02:14:34+00:00', $uuid->getDateTime()->format('c'));
$this->assertSame('1341368074.491000', $uuid->getDateTime()->format('U.u'));
// Check an old date
$uuid = Uuid::fromString('0901e600-0154-1000-9b21-0800200c9a66');
$this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
$this->assertEquals('1582-10-16T16:34:04+00:00', $uuid->getDateTime()->format('c'));
$this->assertSame('1582-10-16T16:34:04+00:00', $uuid->getDateTime()->format('c'));
$this->assertSame('-12219146756.000000', $uuid->getDateTime()->format('U.u'));
// Check a future date
$uuid = Uuid::fromString('ff9785f6-ffff-1fff-9669-00007ffffffe');
$this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
$this->assertEquals('5236-03-31T21:20:59+00:00', $uuid->getDateTime()->format('c'));
$this->assertSame('5236-03-31T21:20:59+00:00', $uuid->getDateTime()->format('c'));
$this->assertSame('103072857659.999999', $uuid->getDateTime()->format('U.u'));
// Check the oldest date
$uuid = Uuid::fromString('00000000-0000-1000-9669-00007ffffffe');
$this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime());
$this->assertEquals('1582-10-15T00:00:00+00:00', $uuid->getDateTime()->format('c'));
$this->assertSame('1582-10-15T00:00:00+00:00', $uuid->getDateTime()->format('c'));
$this->assertSame('-12219292800.000000', $uuid->getDateTime()->format('U.u'));
}
public function testGetDateTimeFromNonVersion1Uuid(): void
@@ -188,7 +192,7 @@ class UuidTest extends TestCase
$this->expectException(UnsupportedOperationException::class);
$this->expectExceptionMessage('Not a time-based UUID');
$date = $uuid->getDateTime();
$uuid->getDateTime();
}
public function testGetFields(): void
@@ -1427,7 +1431,7 @@ class UuidTest extends TestCase
$this->assertEquals($uuid->getVersion(), Uuid::UUID_TYPE_HASH_SHA1);
}
public function testGetDateTimeThrowsExceptionWhenDateTimeCannotParseString(): void
public function testGetDateTimeThrowsExceptionWhenDateTimeCannotParseDate(): void
{
$numberConverter = new BigNumberConverter();
$timeConverter = Mockery::mock(TimeConverterInterface::class);
@@ -1435,7 +1439,7 @@ class UuidTest extends TestCase
$timeConverter
->shouldReceive('convertTime')
->once()
->andReturn('foobar');
->andReturn(new Time(1579476464, '1234567890'));
$builder = new DefaultUuidBuilder($numberConverter, $timeConverter);
$codec = new StringCodec($builder);
@@ -1448,7 +1452,8 @@ class UuidTest extends TestCase
$this->expectException(DateTimeException::class);
$this->expectExceptionMessage(
'DateTimeImmutable::__construct(): Failed to parse time string '
. '(@foobar) at position 0 (@): Unexpected character'
. '(2020-01-19 23:27:44.1234567890) at position 11 (2): '
. 'The timezone could not be found in the database'
);
$uuid->getDateTime();