[Uid] make Uuid::getTime() return subseconds info

This commit is contained in:
Nicolas Grekas 2020-03-12 18:51:21 +01:00
parent d108f7b7ef
commit 5a170b80ed
3 changed files with 104 additions and 3 deletions

View File

@ -0,0 +1,85 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Uid;
/**
* @internal
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class InternalUtil
{
public static function toBinary(string $digits): string
{
$bytes = '';
$len = \strlen($digits);
while ($len > $i = strspn($digits, '0')) {
for ($j = 2, $r = 0; $i < $len; $i += $j, $j = 0) {
do {
$r *= 10;
$d = (int) substr($digits, $i, ++$j);
} while ($i + $j < $len && $r + $d < 256);
$j = \strlen((string) $d);
$q = str_pad(($d += $r) >> 8, $j, '0', STR_PAD_LEFT);
$digits = substr_replace($digits, $q, $i, $j);
$r = $d % 256;
}
$bytes .= \chr($r);
}
return strrev($bytes);
}
public static function toDecimal(string $bytes): string
{
$digits = '';
$len = \strlen($bytes);
while ($len > $i = strspn($bytes, "\0")) {
for ($r = 0; $i < $len; $i += $j) {
$j = $d = 0;
do {
$r <<= 8;
$d = ($d << 8) + \ord($bytes[$i + $j]);
} while ($i + ++$j < $len && $r + $d < 10);
if (256 < $d) {
$q = intdiv($d += $r, 10);
$bytes[$i] = \chr($q >> 8);
$bytes[1 + $i] = \chr($q & 0xFF);
} else {
$bytes[$i] = \chr(intdiv($d += $r, 10));
}
$r = $d % 10;
}
$digits .= (string) $r;
}
return strrev($digits);
}
public static function binaryAdd(string $a, string $b): string
{
$sum = 0;
for ($i = 7; 0 <= $i; --$i) {
$sum += \ord($a[$i]) + \ord($b[$i]);
$a[$i] = \chr($sum & 0xFF);
$sum >>= 8;
}
return $a;
}
}

View File

@ -117,7 +117,7 @@ class UuidTest extends TestCase
$uuid = new Uuid(self::A_UUID_V1);
$this->assertSame(Uuid::VARIANT_DCE, $uuid->getVariant());
$this->assertSame(1583245966, $uuid->getTime());
$this->assertSame(1583245966.746458, $uuid->getTime());
$this->assertSame('3499710062d0', $uuid->getMac());
$this->assertSame(self::A_UUID_V1, (string) $uuid);
}

View File

@ -28,6 +28,12 @@ class Uuid implements \JsonSerializable
public const VARIANT_MICROSOFT = UUID_VARIANT_MICROSOFT;
public const VARIANT_OTHER = UUID_VARIANT_OTHER;
// https://tools.ietf.org/html/rfc4122#section-4.1.4
// 0x01b21dd213814000 is the number of 100-ns intervals between the
// UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
private const TIME_OFFSET_INT = 0x01b21dd213814000;
private const TIME_OFFSET_COM = "\xfe\x4d\xe2\x2d\xec\x7e\xc0\x00";
private $uuid;
public function __construct(string $uuid = null)
@ -105,13 +111,23 @@ class Uuid implements \JsonSerializable
return uuid_variant($this->uuid);
}
public function getTime(): int
public function getTime(): float
{
if (self::TYPE_1 !== $t = uuid_type($this->uuid)) {
throw new \LogicException("UUID of type $t doesn't contain a time.");
}
return uuid_time($this->uuid);
$time = '0'.substr($this->uuid, 15, 3).substr($this->uuid, 9, 4).substr($this->uuid, 0, 8);
if (\PHP_INT_SIZE >= 8) {
return (hexdec($time) - self::TIME_OFFSET_INT) / 10000000;
}
$time = str_pad(hex2bin($time), 8, "\0", STR_PAD_LEFT);
$time = InternalUtil::binaryAdd($time, self::TIME_OFFSET_COM);
$time[0] = $time[0] & "\x7F";
return InternalUtil::toDecimal($time) / 10000000;
}
public function getMac(): string