UUIDv7
RFC9562 Universally Unique IDentifiers (UUIDs)で正式に定義された。 code:txt
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| unix_ts_ms |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| unix_ts_ms | ver | rand_a |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|var| rand_b |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| rand_b |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
unix_ts_ms はいわゆる Unix time (1970-01-01 00:00:00 UTC 起点)でミリ秒単位。
ver は 0b0111
var は 0b10
10オクテット分をランダム値として結合した後
ver と var の部分をANDマスクしてORで設定するのが単純。
JavaScript の場合、128bit 値を簡潔に扱うには、BigInt を使うのが楽。
unix_ts_ms は 48bit で約8919年で周回する。
2^48 / (365.25 * 24 * 60 * 60 * 1000) = 8919.40377946
PHP の場合、整数(int型)は 32bit システムでは 32bit、64bit システムでは 64bit になっている。これを超えると float 型になってしまう。
PHP の標準では microtime で時間を取得する。
microtime(true) だと正確な値が取得できるが、"秒数未満 秒数" のstring型になってしまう。
microtime(true) では float 型で取得される。
ランダム値は random_bytes で取得できる。バイト数分の文字列として返ってくる。
10バイト取得して、ビット演算するために、1桁目と3桁目を切り出して上書きする。
code:php
function generateUuidv7Bin() {
$t = microtime(true);
$m = floor($t * 1000);
$p = "";
for ($i = 5; $i >= 0; $i--) {
$b = fmod($m, 256);
$m = ($m - $b) / 256;
$p = chr($b) . $p;
}
$r = str_split(random_bytes(10));
$r0 = chr((ord($r0) & 0b00001111) | 0b01110000); $r2 = chr((ord($r2) & 0b00111111) | 0b10000000); return $p . implode($r);
}
function toStringFromUuidBin($uuidBin) {
$s = bin2hex($uuidBin);
return substr($s, 0, 8) . '-' . substr($s, 8, 4) . '-' . substr($s, 12, 4) . '-' . substr($s, 16);
}