Return Hexadecimal type from TimeConverterInterface::calculateTime()

This commit is contained in:
Ben Ramsey
2020-01-21 23:28:10 -06:00
parent 4963f2320a
commit 80a7be00b3
11 changed files with 51 additions and 111 deletions
+4
View File
@@ -95,6 +95,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
string.
* `Generator\DefaultTimeGenerator` no longer adds the variant and version bits
to the bytes it returns. These must be applied to the bytes afterwards.
* `Converter/TimeConverterInterface::calculateTime()` now returns
`Type\Hexadecimal` instead of `array`. The value is the full UUID timestamp
value (count of 100-nanosecond intervals since the Gregorian calendar epoch)
in hexadecimal format.
* Change methods in converter interfaces to accept and return string values
instead of `mixed`; this simplifies the interface and makes it consistent:
* `NumberConverterInterface::fromHex(string $hex): string`
@@ -41,7 +41,7 @@ class BigNumberTimeConverter implements TimeConverterInterface
* @inheritDoc
* @psalm-pure
*/
public function calculateTime(string $seconds, string $microSeconds): array
public function calculateTime(string $seconds, string $microSeconds): Hexadecimal
{
return $this->converter->calculateTime($seconds, $microSeconds);
}
+2 -6
View File
@@ -41,7 +41,7 @@ class GenericTimeConverter implements TimeConverterInterface
* @inheritDoc
* @psalm-pure
*/
public function calculateTime(string $seconds, string $microSeconds): array
public function calculateTime(string $seconds, string $microSeconds): Hexadecimal
{
$timestamp = new Time($seconds, $microSeconds);
@@ -69,11 +69,7 @@ class GenericTimeConverter implements TimeConverterInterface
STR_PAD_LEFT
);
return [
'low' => substr($uuidTimeHex, 8),
'mid' => substr($uuidTimeHex, 4, 4),
'hi' => substr($uuidTimeHex, 0, 4),
];
return new Hexadecimal($uuidTimeHex);
}
/**
+2 -7
View File
@@ -64,7 +64,7 @@ class PhpTimeConverter implements TimeConverterInterface
* @inheritDoc
* @psalm-pure
*/
public function calculateTime(string $seconds, string $microSeconds): array
public function calculateTime(string $seconds, string $microSeconds): Hexadecimal
{
$seconds = new IntegerValue($seconds);
$microSeconds = new IntegerValue($microSeconds);
@@ -88,12 +88,7 @@ class PhpTimeConverter implements TimeConverterInterface
);
}
/** @psalm-suppress MixedArgument */
return [
'low' => sprintf('%08x', $uuidTime & 0xffffffff),
'mid' => sprintf('%04x', ($uuidTime >> 32) & 0xffff),
'hi' => sprintf('%04x', ($uuidTime >> 48) & 0x0fff),
];
return new Hexadecimal(str_pad(dechex($uuidTime), 16, '0', STR_PAD_LEFT));
}
/**
+5 -4
View File
@@ -24,8 +24,9 @@ use Ramsey\Uuid\Type\Time;
interface TimeConverterInterface
{
/**
* Uses the provided seconds and micro-seconds to calculate the time_low,
* time_mid, and time_high fields used by RFC 4122 version 1 UUIDs
* Uses the provided seconds and micro-seconds to calculate the count of
* 100-nanosecond intervals since UTC 00:00:00.00, 15 October 1582, for
* RFC 4122 variant UUIDs
*
* @link http://tools.ietf.org/html/rfc4122#section-4.2.2 RFC 4122, § 4.2.2: Generation Details
*
@@ -34,11 +35,11 @@ interface TimeConverterInterface
* @param string $microSeconds A string representation of the micro-seconds
* associated with the time to calculate
*
* @return string[] An array guaranteed to contain `low`, `mid`, and `hi` keys
* @return Hexadecimal The full UUID timestamp as a Hexadecimal value
*
* @psalm-pure
*/
public function calculateTime(string $seconds, string $microSeconds): array;
public function calculateTime(string $seconds, string $microSeconds): Hexadecimal;
/**
* Converts a timestamp extracted from a UUID to a Unix timestamp
+9 -16
View File
@@ -75,25 +75,18 @@ class DefaultTimeGenerator implements TimeGeneratorInterface
}
}
// Create a 60-bit time value as a count of 100-nanosecond intervals
// since 00:00:00.00, 15 October 1582.
$uuidTime = $this->timeConverter->calculateTime(
$this->timeProvider->getTime()->getSeconds()->toString(),
$this->timeProvider->getTime()->getMicroSeconds()->toString()
);
$hex = vsprintf(
'%08s%04s%04s%04s%012s',
[
$uuidTime['low'] ?? 0,
$uuidTime['mid'] ?? 0,
$uuidTime['hi'] ?? 0,
sprintf('%04x', $clockSeq),
$node,
]
);
$timeBytes = (string) hex2bin(str_pad($uuidTime->toString(), 16, '0', STR_PAD_LEFT));
return (string) hex2bin($hex);
return $timeBytes[4] . $timeBytes[5] . $timeBytes[6] . $timeBytes[7]
. $timeBytes[2] . $timeBytes[3]
. $timeBytes[0] . $timeBytes[1]
. pack('n*', $clockSeq)
. $node;
}
/**
@@ -102,7 +95,7 @@ class DefaultTimeGenerator implements TimeGeneratorInterface
*
* @param string|int|null $node A node value that may be used to override the node provider
*
* @return string Hexadecimal representation of the node ID
* @return string 6-byte binary string representation of the node
*
* @throws InvalidArgumentException
*/
@@ -114,13 +107,13 @@ class DefaultTimeGenerator implements TimeGeneratorInterface
// Convert the node to hex, if it is still an integer.
if (is_int($node)) {
$node = sprintf('%012x', $node);
$node = dechex($node);
}
if (!ctype_xdigit((string) $node) || strlen((string) $node) > 12) {
throw new InvalidArgumentException('Invalid node value');
}
return strtolower(sprintf('%012s', (string) $node));
return (string) hex2bin(str_pad((string) $node, 12, '0', STR_PAD_LEFT));
}
}
@@ -26,16 +26,15 @@ class BigNumberTimeConverterTest extends TestCase
$maskMid = BigInteger::fromBase('ffff', 16);
$maskHi = BigInteger::fromBase('0fff', 16);
$expectedArray = [
'low' => sprintf('%08s', $calculatedTime->and($maskLow)->toBase(16)),
'mid' => sprintf('%04s', $calculatedTime->shiftedRight(32)->and($maskMid)->toBase(16)),
'hi' => sprintf('%04s', $calculatedTime->shiftedRight(48)->and($maskHi)->toBase(16)),
];
$expected = sprintf('%04s', $calculatedTime->shiftedRight(48)->and($maskHi)->toBase(16));
$expected .= sprintf('%04s', $calculatedTime->shiftedRight(32)->and($maskMid)->toBase(16));
$expected .= sprintf('%08s', $calculatedTime->and($maskLow)->toBase(16));
$converter = new BigNumberTimeConverter();
$returned = $converter->calculateTime((string) $seconds, (string) $microSeconds);
$this->assertSame($expectedArray, $returned);
$this->assertInstanceOf(Hexadecimal::class, $returned);
$this->assertSame($expected, $returned->toString());
}
public function testConvertTime(): void
@@ -12,18 +12,16 @@ use Ramsey\Uuid\Type\Hexadecimal;
class GenericTimeConverterTest extends TestCase
{
/**
* @param string[] $expected
*
* @dataProvider provideCalculateTime
*/
public function testCalculateTime(string $seconds, string $microSeconds, array $expected): void
public function testCalculateTime(string $seconds, string $microSeconds, string $expected): void
{
$calculator = new BrickMathCalculator();
$converter = new GenericTimeConverter($calculator);
$result = $converter->calculateTime($seconds, $microSeconds);
$this->assertSame($expected, $result);
$this->assertSame($expected, $result->toString());
}
/**
@@ -35,29 +33,17 @@ class GenericTimeConverterTest extends TestCase
[
'seconds' => '-12219146756',
'microSeconds' => '0',
'expected' => [
'low' => '0901e600',
'mid' => '0154',
'hi' => '0000',
],
'expected' => '000001540901e600',
],
[
'seconds' => '103072857659',
'microseconds' => '999999',
'expected' => [
'low' => 'ff9785f6',
'mid' => 'ffff',
'hi' => '0fff',
],
'expected' => '0fffffffff9785f6',
],
[
'seconds' => '1578612359',
'microseconds' => '521023',
'expected' => [
'low' => '64c71df6',
'mid' => '3337',
'hi' => '01ea',
],
'expected' => '01ea333764c71df6',
],
// This is the earliest possible date supported by v1 UUIDs:
@@ -65,11 +51,7 @@ class GenericTimeConverterTest extends TestCase
[
'seconds' => '-12219292800',
'microSeconds' => '0',
'expected' => [
'low' => '00000000',
'mid' => '0000',
'hi' => '0000',
],
'expected' => '0000000000000000',
],
// This is the last possible time supported by v1 UUIDs:
@@ -77,11 +59,7 @@ class GenericTimeConverterTest extends TestCase
[
'seconds' => '1832455114570',
'microseconds' => '955161',
'expected' => [
'low' => 'fffffffa',
'mid' => 'ffff',
'hi' => 'ffff',
],
'expected' => 'fffffffffffffffa',
],
];
}
+11 -35
View File
@@ -29,16 +29,14 @@ class PhpTimeConverterTest extends TestCase
$maskMid = BigInteger::fromBase('ffff', 16);
$maskHi = BigInteger::fromBase('0fff', 16);
$expectedArray = [
'low' => sprintf('%08s', $calculatedTime->and($maskLow)->toBase(16)),
'mid' => sprintf('%04s', $calculatedTime->shiftedRight(32)->and($maskMid)->toBase(16)),
'hi' => sprintf('%04s', $calculatedTime->shiftedRight(48)->and($maskHi)->toBase(16)),
];
$expected = sprintf('%04s', $calculatedTime->shiftedRight(48)->and($maskHi)->toBase(16));
$expected .= sprintf('%04s', $calculatedTime->shiftedRight(32)->and($maskMid)->toBase(16));
$expected .= sprintf('%08s', $calculatedTime->and($maskLow)->toBase(16));
$converter = new PhpTimeConverter();
$returned = $converter->calculateTime((string) $seconds, (string) $microSeconds);
$this->assertSame($expectedArray, $returned);
$this->assertSame($expected, $returned->toString());
}
public function testCalculateTimeThrowsExceptionWhenSecondsIsNotOnlyDigits(): void
@@ -134,11 +132,9 @@ class PhpTimeConverterTest extends TestCase
}
/**
* @param string[] $expected
*
* @dataProvider provideCalculateTime
*/
public function testCalculateTime(string $seconds, string $microSeconds, array $expected): void
public function testCalculateTime(string $seconds, string $microSeconds, string $expected): void
{
$calculator = new BrickMathCalculator();
$fallbackConverter = new GenericTimeConverter($calculator);
@@ -146,7 +142,7 @@ class PhpTimeConverterTest extends TestCase
$result = $converter->calculateTime($seconds, $microSeconds);
$this->assertSame($expected, $result);
$this->assertSame($expected, $result->toString());
}
/**
@@ -158,29 +154,17 @@ class PhpTimeConverterTest extends TestCase
[
'seconds' => '-12219146756',
'microSeconds' => '0',
'expected' => [
'low' => '0901e600',
'mid' => '0154',
'hi' => '0000',
],
'expected' => '000001540901e600',
],
[
'seconds' => '103072857659',
'microseconds' => '999999',
'expected' => [
'low' => 'ff9785f6',
'mid' => 'ffff',
'hi' => '0fff',
],
'expected' => '0fffffffff9785f6',
],
[
'seconds' => '1578612359',
'microseconds' => '521023',
'expected' => [
'low' => '64c71df6',
'mid' => '3337',
'hi' => '01ea',
],
'expected' => '01ea333764c71df6',
],
// This is the earliest possible date supported by v1 UUIDs:
@@ -188,11 +172,7 @@ class PhpTimeConverterTest extends TestCase
[
'seconds' => '-12219292800',
'microSeconds' => '0',
'expected' => [
'low' => '00000000',
'mid' => '0000',
'hi' => '0000',
],
'expected' => '0000000000000000',
],
// This is the last possible time supported by v1 UUIDs:
@@ -200,11 +180,7 @@ class PhpTimeConverterTest extends TestCase
[
'seconds' => '1832455114570',
'microseconds' => '955161',
'expected' => [
'low' => 'fffffffa',
'mid' => 'ffff',
'hi' => 'ffff',
],
'expected' => 'fffffffffffffffa',
],
];
}
+2 -5
View File
@@ -13,6 +13,7 @@ use Ramsey\Uuid\DegradedUuid;
use Ramsey\Uuid\Generator\CombGenerator;
use Ramsey\Uuid\Generator\DefaultTimeGenerator;
use Ramsey\Uuid\Math\BrickMathCalculator;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Time;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidFactory;
@@ -532,11 +533,7 @@ class ExpectedBehaviorTest extends TestCase
$timeConverter
->shouldReceive('calculateTime')
->andReturnUsing(function ($seconds, $microSeconds) {
return [
'low' => dechex($seconds),
'mid' => dechex($microSeconds),
'hi' => 'abcd',
];
return new Hexadecimal('abcd' . dechex($microSeconds) . dechex($seconds));
});
$timeProvider = \Mockery::mock('Ramsey\Uuid\Provider\TimeProviderInterface', [
+3 -2
View File
@@ -16,6 +16,7 @@ use Ramsey\Uuid\Generator\DefaultTimeGenerator;
use Ramsey\Uuid\Provider\NodeProviderInterface;
use Ramsey\Uuid\Provider\TimeProviderInterface;
use Ramsey\Uuid\Test\TestCase;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Time;
class DefaultTimeGeneratorTest extends TestCase
@@ -46,7 +47,7 @@ class DefaultTimeGeneratorTest extends TestCase
private $currentTime;
/**
* @var string[]
* @var Hexadecimal
*/
private $calculatedTime;
@@ -61,7 +62,7 @@ class DefaultTimeGeneratorTest extends TestCase
$this->nodeProvider = $this->getMockBuilder(NodeProviderInterface::class)->getMock();
$this->timeConverter = $this->getMockBuilder(TimeConverterInterface::class)->getMock();
$this->currentTime = ['sec' => 1458733431, 'usec' => 877449];
$this->calculatedTime = ['low' => '83cb98e0', 'mid' => '98e0', 'hi' => '03cb'];
$this->calculatedTime = new Hexadecimal('03cb98e083cb98e0');
$time = new Time($this->currentTime['sec'], $this->currentTime['usec']);
$this->timeProvider = Mockery::mock(TimeProviderInterface::class, [