@@ -23,6 +23,8 @@ struct UUID
2323 NCS
2424 # Reserved for RFC 4122 Specification (default).
2525 RFC4122
26+ # Reserved for RFC 9562 Specification (default for v7).
27+ RFC9562 = RFC4122
2628 # Reserved by Microsoft for backward compatibility.
2729 Microsoft
2830 # Reserved for future expansion.
@@ -43,6 +45,8 @@ struct UUID
4345 V4 = 4
4446 # SHA1 hash and namespace.
4547 V5 = 5
48+ # Prefixed with a UNIX timestamp with millisecond precision, filled in with randomness.
49+ V7 = 7
4650 end
4751
4852 # A Domain represents a Version 2 domain (DCE security).
@@ -80,7 +84,7 @@ struct UUID
8084 # do nothing
8185 when Variant ::NCS
8286 @bytes [8 ] = (@bytes [8 ] & 0x7f )
83- when Variant ::RFC4122
87+ when Variant ::RFC4122 , Variant :: RFC9562
8488 @bytes [8 ] = (@bytes [8 ] & 0x3f ) | 0x80
8589 when Variant ::Microsoft
8690 @bytes [8 ] = (@bytes [8 ] & 0x1f ) | 0xc0
@@ -321,6 +325,30 @@ struct UUID
321325 end
322326 {% end % }
323327
328+ # Generates an RFC9562-compatible v7 UUID, allowing the values to be sorted
329+ # chronologically (with 1ms precision) by their raw or hexstring
330+ # representation.
331+ def self.v7 (random r : Random = Random ::Secure )
332+ buffer = uninitialized UInt8 [18 ]
333+ value = buffer.to_slice
334+
335+ # Generate the first 48 bits of the UUID with the current timestamp. We
336+ # allocated enough room for a 64-bit timestamp to accommodate the
337+ # NetworkEndian.encode call here, but we only need 48 bits of it so we chop
338+ # off the first 2 bytes.
339+ IO ::ByteFormat ::NetworkEndian .encode Time .utc.to_unix_ms, value
340+ value = value[2 ..]
341+
342+ # Fill in the rest with random bytes
343+ r.random_bytes(value[6 ..])
344+
345+ # Set the version and variant
346+ value[6 ] = (value[6 ] & 0x3F ) | 0x70
347+ value[8 ] = (value[8 ] & 0x0F ) | 0x80
348+
349+ new(value, variant: :rfc9562 , version: :v7 )
350+ end
351+
324352 # Generates an empty UUID.
325353 #
326354 # ```
@@ -375,6 +403,7 @@ struct UUID
375403 when 3 then Version ::V3
376404 when 4 then Version ::V4
377405 when 5 then Version ::V5
406+ when 7 then Version ::V7
378407 else Version ::Unknown
379408 end
380409 end
@@ -442,7 +471,7 @@ struct UUID
442471 class Error < Exception
443472 end
444473
445- {% for v in %w(1 2 3 4 5) % }
474+ {% for v in %w(1 2 3 4 5 7 ) % }
446475 # Returns `true` if UUID is a V{{ v.id }}, `false` otherwise.
447476 def v {{ v.id }}?
448477 variant == Variant ::RFC4122 && version == Version ::V {{ v.id }}
0 commit comments