8000 [Uid] Add `UuidV6::fromV1()` and `UuidV7::fromV1()` methods · symfony/symfony@8471425 · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit 8471425

Browse files
committed
[Uid] Add UuidV6::fromV1() and UuidV7::fromV1() methods
1 parent 0d9562f commit 8471425

File tree

5 files changed

+77
-2
lines changed

5 files changed

+77
-2
lines changed

src/Symfony/Component/Uid/BinaryUtil.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,10 @@ public static function add(string $a, string $b): string
118118

119119
/**
120120
* @param string $time Count of 100-nanosecond intervals since the UUID epoch 1582-10-15 00:00:00 in hexadecimal
121+
*
122+
* @return string Count of 100-nanosecond intervals since the UUID epoch 1582-10-15 00:00:00 as a numeric string
121123
*/
122-
public static function hexToDateTime(string $time): \DateTimeImmutable
124+
public static function hexToNumericString(string $time): string
123125
{
124126
if (\PHP_INT_SIZE >= 8) {
125127
$time = (string) (hexdec($time) - self::TIME_OFFSET_INT);
@@ -140,7 +142,17 @@ public static function hexToDateTime(string $time): \DateTimeImmutable
140142
$time = '-' === $time[0] ? '-'.str_pad(substr($time, 1), 8, '0', \STR_PAD_LEFT) : str_pad($time, 8, '0', \STR_PAD_LEFT);
141143
}
142144

143-
return \DateTimeImmutable::createFromFormat('U.u?', substr_replace($time, '.', -7, 0));
145+
return $time;
146+
}
147+
148+
/**
149+
* Sub-microseconds are lost since they are not handled by \DateTimeImmutable.
150+
*
151+
* @param string $time Count of 100-nanosecond intervals since the UUID epoch 1582-10-15 00:00:00 in hexadecimal
152+
*/
153+
public static function hexToDateTime(string $time): \DateTimeImmutable
154+
{
155+
return \DateTimeImmutable::createFromFormat('U.u?', substr_replace(self::hexToNumericString($time), '.', -7, 0));
144156
}
145157

146158
/**

src/Symfony/Component/Uid/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
7.1
5+
---
6+
7+
* Add `UuidV6::fromV1()` and `UuidV7::fromV1()`
8+
49
6.2
510
---
611

src/Symfony/Component/Uid/Tests/UuidTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,4 +427,33 @@ public function testFromStringBase58Padding()
427427
{
428428
$this->assertInstanceOf(Uuid::class, Uuid::fromString('111111111u9QRyVM94rdmZ'));
429429
}
430+
431+
public function testV6FromV1()
432+
{
433+
$uuidV1 = new UuidV1('8189d3de-9670-11ee-b9d1-0242ac120002');
434+
$uuidV6 = UuidV6::fromV1($uuidV1);
435+
436+
$this->assertEquals($uuidV1->getDateTime(), $uuidV6->getDateTime());
437+
$this->assertSame($uuidV1->getNode(), $uuidV6->getNode());
438+
$this->assertEquals($uuidV6, UuidV6::fromV1($uuidV1));
439+
}
440+
441+
public function testV7FromV1BeforeUnixEpochThrows()
442+
{
443+
$this->expectException(\InvalidArgumentException::class);
444+
$this->expectExceptionMessage('UUIDv1 with a timestamp before Unix epoch cannot be converted to UUIDv7');
445+
446+
UuidV7::fromV1(new UuidV1('9aba8000-ff00-11b0-b3db-3b3fc83afdfc')); // Timestamp is 1969-01-01 00:00:00.0000000
447+
}
448+
449+
public function testV7FromV1()
450+
{
451+
$uuidV1 = new UuidV1('eb248d80-ea4f-11ec-9d2a-839425e6fb88');
452+
$sameUuidV1100NanosecondsLater = new UuidV1('eb248d81-ea4f-11ec-9d2a-839425e6fb88');
453+
$uuidV7 = UuidV7::fromV1($uuidV1);
454+
455+
$this->assertEquals($uuidV1->getDateTime(), $uuidV7->getDateTime());
456+
$this->assertEquals($uuidV7, UuidV7::fromV1($uuidV1));
457+
$this->assertNotEquals($uuidV7, UuidV7::fromV1($sameUuidV1100NanosecondsLater));
458+
}
430459
}

src/Symfony/Component/Uid/UuidV6.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ public function getNode(): string
4343
return substr($this->uid, 24);
4444
}
4545

46+
public static function fromV1(UuidV1 $uuidV1): self
47+
{
48+
$uuidV1 = $uuidV1->toRfc4122();
49+
50+
return new self(substr($uuidV1, 15, 3).substr($uuidV1, 9, 4).$uuidV1[0].'-'.substr($uuidV1, 1, 4).'-6'.substr($uuidV1, 5, 3).substr($uuidV1, 18, 6).substr($uuidV1, 24));
51+
}
52+
4653
public static function generate(\DateTimeInterface $time = null, Uuid $node = null): string
4754
{
4855
$uuidV1 = UuidV1::generate($time, $node);

src/Symfony/Component/Uid/UuidV7.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,28 @@ public function getDateTime(): \DateTimeImmutable
4949
return \DateTimeImmutable::createFromFormat('U.v', substr_replace($time, '.', -3, 0));
5050
}
5151

52+
/**
53+
* Sub-millisecond timestamp precision is lost since UUIDv7 A93C don't support it.
54+
*/
55+
public static function fromV1(UuidV1 $uuidV1): self
56+
{
57+
$uuidV1 = $uuidV1->toRfc4122();
58+
$time = '0'.substr($uuidV1, 15, 3).substr($uuidV1, 9, 4).substr($uuidV1, 0, 8);
59+
$time = BinaryUtil::hexToNumericString($time);
60+
if ('-' === $time[0]) {
61+
throw new \InvalidArgumentException('UUIDv1 with a timestamp before Unix epoch cannot be converted to UUIDv7');
62+
}
63+
64+
$time = str_pad($time, 5, '0', \STR_PAD_LEFT);
65+
66+
return new self(substr_replace(sprintf(
67+
'%012s-7%03s-%s',
68+
\PHP_INT_SIZE >= 8 ? dechex(substr($time, 0, -4)) : bin2hex(BinaryUtil::fromBase(substr($time, 0, -4), BinaryUtil::BASE10)),
69+
dechex((int) substr($time, -4, 3)),
70+
substr($uuidV1, 19)
71+
), '-', 8, 0));
72+
}
73+
5274
public static function generate(\DateTimeInterface $time = null): string
5375
{
5476
if (null === $mtime = $time) {

0 commit comments

Comments
 (0)
0