Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ $uuid8_first = UUID::uuid8();
echo $uuid8_first . "\n"; // e.g. 017f22e2-79b0-8cc3-98c4-dc0c0c07398f
$uuid8_second = UUID::uuid8();
var_dump($uuid8_first < $uuid8_second); // bool(true)

$firstDate = \DateTime::createFromFormat('Y-m-d H:i:s.u', '2024-01-01 13:12:12.129817');
$uuid8_from_date = UUID::uuid8($firstDate);
$uuid8_zero_value = UUID::firstUuid8($firstDate) // 018cc527-3c61-8d12-9000-000000000000 first uuid from this time
$uuid8_last_value = UUID::lastUuid8($firstDate) // 018cc527-3c61-8d12-9fff-ffffffffffff last uuid from this time
var_dump(($uuid8_zero_value < $uuid8_from_date) && ($uuid8_from_date < $uuid8_last_value)); // bool(true)
```

### Tools
Expand Down
40 changes: 38 additions & 2 deletions src/UUID.php
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,17 @@ public static function uuid7(): string
* Generate a version 8 UUID. A v8 UUID is lexicographically sortable and is
* designed to encode a Unix timestamp with arbitrary sub-second precision.
*
* @param \DateTime|null $dateTime The time for which the uuid is generated
* @return string The string standard representation of the UUID
*/
public static function uuid8(): string
public static function uuid8(\DateTime|null $dateTime=null): string
{
[$unixts, $subsec] = self::getUnixTimeSubsec();
if(!is_null($dateTime)){
$unixts = $dateTime->getTimestamp(); // Получаем секунды
$subsec = (int) ($dateTime->format('u') . "0"); // Получаем микросекунды
} else {
[$unixts, $subsec] = self::getUnixTimeSubsec();
}
$unixtsms = $unixts * 1000 + intdiv($subsec, self::V8_SUBSEC_RANGE);
$subsec = self::encodeSubsec($subsec % self::V8_SUBSEC_RANGE);
$subsecA = $subsec >> 2;
Expand All @@ -270,6 +276,36 @@ public static function uuid8(): string
return self::uuidFromHex($uhex, 8);
}

/**
* The first uuid for a given time is generated, which allows you to use the v8 uuid for time search
* A v8 UUID is lexicographically sortable and is
* designed to encode a Unix timestamp with arbitrary sub-second precision.
*
* @param \DateTime|null $dateTime
* @return string
*/
public static function firstUuid8(\DateTime|null $dateTime=null): string
{
$uuid = self::uuid8($dateTime);
$zero = '000-000000000000';
return substr_replace($uuid, $zero, -strlen($zero));
}

/**
* Generate The last uuid for the given time is generated, which allows you to use uuidv8 for time search.
* A v8 UUID is lexicographically sortable and is
* designed to encode a Unix timestamp with arbitrary sub-second precision.
*
* @param \DateTime|null $dateTime
* @return string
*/
public static function lastUuid8(\DateTime|null $dateTime=null): string
{
$uuid = self::uuid8($dateTime);
$last = 'fff-ffffffffffff';
return substr_replace($uuid, $last, -strlen($last));
}

/**
* Check if a string is a valid UUID.
*
Expand Down
78 changes: 78 additions & 0 deletions tests/UuidTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,84 @@ public function testCanGenerateValidVersion8()
}
}

public function testCanGenerateValidVersion8FromTime()
{
$firstDate = \DateTime::createFromFormat('Y-m-d H:i:s', '2024-01-01 13:12:12');
$uuid1 = UUID::uuid8($firstDate);
for ($x = 0; $x < 1000; $x++) {
$this->assertMatchesRegularExpression(
'/^[0-9a-f]{8}\-[0-9a-f]{4}\-8[0-9a-f]{3}\-[89ab][0-9a-f]{3}\-[0-9a-f]{12}$/',
$uuid1
);
$secondDate = clone $firstDate; // Клонируем первый объект
$secondDate->modify('+524 milliseconds');

$uuid2 = UUID::uuid8($secondDate);
$this->assertGreaterThan(
$uuid1,
$uuid2
);
$this->assertLessThan(
0,
UUID::cmp($uuid1, $uuid2)
);
$uuid1 = $uuid2;
$firstDate = clone $secondDate;
}
}

public function testCanGenerateValidVersion8ZeroValue()
{
$firstDate = \DateTime::createFromFormat('Y-m-d H:i:s.u', '2024-01-01 13:12:12.129817');
for ($x = 0; $x < 1000; $x++) {
$uuidZero = UUID::firstUuid8($firstDate);
$this->assertMatchesRegularExpression(
'/^[0-9a-f]{8}\-[0-9a-f]{4}\-8[0-9a-f]{3}\-[89ab][0-9a-f]{3}\-[0-9a-f]{12}$/',
$uuidZero
);
$uuid = UUID::uuid8($firstDate);
$this->assertGreaterThan(
$uuidZero,
$uuid
);
$firstDate->modify('+564 milliseconds');

}
}

public function testCanGenerateValidVersion8LastVersion()
{
$firstDate = \DateTime::createFromFormat('Y-m-d H:i:s.u', '2024-01-01 13:12:12.129817');
for ($x = 0; $x < 1000; $x++) {
$uuidZero = UUID::lastUuid8($firstDate);
$this->assertMatchesRegularExpression(
'/^[0-9a-f]{8}\-[0-9a-f]{4}\-8[0-9a-f]{3}\-[89ab][0-9a-f]{3}\-[0-9a-f]{12}$/',
$uuidZero
);
$uuid = UUID::uuid8($firstDate);
$this->assertGreaterThan(
$uuid,
$uuidZero
);
$firstDate->modify('+564 milliseconds');

}
}

public function testCheckValidDateVersion8()
{
$firstDate = \DateTime::createFromFormat('Y-m-d H:i:s', '2024-01-01 13:12:12');
for ($x = 0; $x < 100; $x++) {
$timestamp = $firstDate->getTimestamp();
$microseconds = $firstDate->format('u');
$checkTimestamp = $timestamp + ($microseconds / 1000000);
$uuid = UUID::uuid8($firstDate);
$timeFromUuid = UUID::getTime($uuid);
$this->assertEquals($checkTimestamp,$timeFromUuid,"UUIDV8 get not true date");
$firstDate->modify('+564 milliseconds');
}
}

public function testCannotBeCreatedFromInvalidNamespace()
{
$this->expectException(\InvalidArgumentException::class);
Expand Down