@@ -23,6 +23,7 @@ $(TR $(TDNW Generating UUIDs)
23
23
$(TD $(MYREF sha1UUID)
24
24
$(MYREF randomUUID)
25
25
$(MYREF md5UUID)
26
+ $(MYREF timestampRandomUUID)
26
27
)
27
28
)
28
29
$(TR $(TDNW Using UUIDs)
@@ -119,6 +120,9 @@ module std.uuid;
119
120
assert (id.empty);
120
121
}
121
122
123
+ import core.time : dur;
124
+ import std.datetime.systime : SysTime;
125
+ import std.datetime : Clock , DateTime , UTC ;
122
126
import std.range.primitives ;
123
127
import std.traits ;
124
128
@@ -193,7 +197,9 @@ public struct UUID
193
197
// /Version 4 (Random)
194
198
randomNumberBased = 4 ,
195
199
// /Version 5 (Name based + SHA-1)
196
- nameBasedSHA1 = 5
200
+ nameBasedSHA1 = 5 ,
201
+ // /Version 7 (milliseconds since unix epoch + random)
202
+ timestampRandom = 7
197
203
}
198
204
199
205
union
@@ -309,6 +315,41 @@ public struct UUID
309
315
assert (! __traits(compiles, typeof (UUID (0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,1 ))));
310
316
}
311
317
318
+ /**
319
+ * UUID V7 constructor
320
+ *
321
+ * Params:
322
+ * timestamp = the timestamp part of the UUID V7
323
+ * random = UUID V7 has 74 bits of random data, which rounds to 10 ubyte's.
324
+ * If no random data is given, random data is generated.
325
+ */
326
+ @safe pure this (SysTime timestamp, ubyte [10 ] random = generateV7RandomData())
327
+ {
328
+ ulong epoch = (timestamp - SysTime.fromUnixTime(0 )).total! " msecs" ;
329
+ this .data[6 .. $] = random[];
330
+
331
+ this .data[0 ] = cast (ubyte )((epoch >> 40 ) & 0xFF );
332
+ this .data[1 ] = cast (ubyte )((epoch >> 32 ) & 0xFF );
333
+ this .data[2 ] = cast (ubyte )((epoch >> 24 ) & 0xFF );
334
+ this .data[3 ] = cast (ubyte )((epoch >> 16 ) & 0xFF );
335
+ this .data[4 ] = cast (ubyte )((epoch >> 8 ) & 0xFF );
336
+ this .data[5 ] = cast (ubyte )(epoch & 0xFF );
337
+
338
+ // version and variant
339
+ this .data[6 ] = (this .data[6 ] & 0x0F ) | 0x70 ;
340
+ this .data[8 ] = (this .data[8 ] & 0x3F ) | 0x80 ;
341
+ }
342
+
343
+ // /
344
+ unittest
345
+ {
346
+ import std.datetime : DateTime , SysTime;
347
+ SysTime st = DateTime (2025 , 8 , 19 , 10 , 38 , 45 );
348
+ UUID u = UUID (st);
349
+ SysTime o = u.v7Timestamp();
350
+ assert (o == st, st.toString() ~ " | " ~ o.toString());
351
+ }
352
+
312
353
/**
313
354
* <a name =" UUID(string)" ></a>
314
355
* Parse a UUID from its canonical string form. An UUID in its
@@ -515,6 +556,25 @@ public struct UUID
515
556
enum res = ctfeTest();
516
557
}
517
558
559
+ /**
560
+ * If the UUID is of version 7 it has a timestamp that this function
561
+ * returns, otherwise and UUIDParsingException is thrown.
562
+ */
563
+ SysTime v7Timestamp () const {
564
+ if (this .uuidVersion != Version.timestampRandom)
565
+ {
566
+ throw new UUIDParsingException(" The UUID is not of version" ~
567
+ " v7 therefore no timestamp exist" , 0 );
568
+ }
569
+ ulong milli = (cast (ulong )(this .data[0 ]) << 40 ) |
570
+ (cast (ulong )(this .data[1 ]) << 32 ) |
571
+ (cast (ulong )(this .data[2 ]) << 24 ) |
572
+ (cast (ulong )(this .data[3 ]) << 16 ) |
573
+ (cast (ulong )(this .data[4 ]) << 8 ) |
574
+ (cast (ulong )(this .data[5 ]));
575
+ return SysTime (DateTime (1970 , 1 , 1 ), UTC ()) + dur! " msecs" (milli);
576
+ }
577
+
518
578
/**
519
579
* RFC 4122 defines different internal data layouts for UUIDs.
520
580
* Returns the format used by this UUID.
@@ -601,6 +661,8 @@ public struct UUID
601
661
return Version.randomNumberBased;
602
662
else if ((octet9 & 0xF0 ) == 0x50 )
603
663
return Version.nameBasedSHA1;
664
+ else if ((octet9 & 0xF0 ) == 0x70 )
665
+ return Version.timestampRandom;
604
666
else
605
667
return Version.unknown;
606
668
}
@@ -622,7 +684,7 @@ public struct UUID
622
684
0x40 : UUID .Version.randomNumberBased,
623
685
0x50 : UUID .Version.nameBasedSHA1,
624
686
0x60 : UUID .Version.unknown,
625
- 0x70 : UUID .Version.unknown ,
687
+ 0x70 : UUID .Version.timestampRandom ,
626
688
0x80 : UUID .Version.unknown,
627
689
0x90 : UUID .Version.unknown,
628
690
0xa0 : UUID .Version.unknown,
@@ -1317,6 +1379,13 @@ if (isInputRange!RNG && isIntegral!(ElementType!RNG))
1317
1379
assert (u1.uuidVersion == UUID .Version.randomNumberBased);
1318
1380
}
1319
1381
1382
+ /**
1383
+ * This function returns a timestamp + random based UUID aka. uuid v7.
1384
+ */
1385
+ UUID timestampRandomUUID () {
1386
+ return UUID (Clock .currTime());
1387
+ }
1388
+
1320
1389
/**
1321
1390
* This is a less strict parser compared to the parser used in the
1322
1391
* UUID constructor. It enforces the following rules:
@@ -1718,6 +1787,19 @@ enum uuidRegex = "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}"~
1718
1787
]);
1719
1788
}
1720
1789
1790
+ ubyte [10 ] generateV7RandomData () {
1791
+ import std.random ;
1792
+ auto rnd = Random (unpredictableSeed! (ubyte )());
1793
+
1794
+ ubyte [10 ] bytes;
1795
+ foreach (idx; 0 .. bytes.length)
1796
+ {
1797
+ bytes[idx] = uniform! (ubyte )(rnd);
1798
+ rnd.popFront();
1799
+ }
1800
+ return bytes;
1801
+ }
1802
+
1721
1803
/**
1722
1804
* This exception is thrown if an error occurs when parsing a UUID
1723
1805
* from a string.
@@ -1777,3 +1859,39 @@ public class UUIDParsingException : Exception
1777
1859
assert (ex.position == 10 );
1778
1860
assert (ex.reason == UUIDParsingException.Reason.tooMuch);
1779
1861
}
1862
+
1863
+ // / uuidv7
1864
+ unittest
1865
+ {
1866
+ import std.datetime : DateTime , SysTime;
1867
+
1868
+ SysTime st = DateTime (2025 , 8 , 19 , 10 , 38 , 45 );
1869
+ UUID u = UUID (st);
1870
+ assert (u.uuidVersion == UUID .Version.timestampRandom);
1871
+ SysTime o = u.v7Timestamp();
1872
+ assert (o == st, st.toString() ~ " | " ~ o.toString());
1873
+ string s = u.toString();
1874
+ UUID u2 = UUID (s);
1875
+ SysTime o2 = u2.v7Timestamp();
1876
+ assert (o2 == st, st.toString() ~ " | " ~ o2.toString());
1877
+ }
1878
+
1879
+ unittest
1880
+ {
1881
+ import std.datetime : SysTime;
1882
+
1883
+ UUID u = timestampRandomUUID();
1884
+ assert (u.uuidVersion == UUID .Version.timestampRandom);
1885
+
1886
+ SysTime o = u.v7Timestamp();
1887
+ assert (o.year > 2024 );
1888
+ assert (o.year < 3024 );
1889
+ }
1890
+
1891
+ unittest
1892
+ {
1893
+ UUID u = UUID (" 0198c2b2-c5a8-7a0f-a1db-86aac7906c7b" );
1894
+ import std.datetime : DateTime , SysTime;
1895
+ auto d = DateTime (2025 ,8 ,19 );
1896
+ assert ((cast (DateTime )u.v7Timestamp()).year == d.year);
1897
+ }
0 commit comments