mirror of
https://github.com/ramsey/uuid.git
synced 2026-06-14 15:56:48 +03:00
Support microtime in returned DateTimeInterface instances
Fixes #90 and supersedes #93.
This commit is contained in:
+5
-3
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -349,7 +349,7 @@ class FeatureSet
|
||||
$genericConverter = new GenericTimeConverter($calculator);
|
||||
|
||||
if ($this->is64BitSystem()) {
|
||||
return new PhpTimeConverter($genericConverter);
|
||||
return new PhpTimeConverter($calculator, $genericConverter);
|
||||
}
|
||||
|
||||
return $genericConverter;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user