Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8357251

Add Support for UUID Version 7 (UUIDv7) defined in RFC 9562

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Unresolved
    • Icon: P4 P4
    • tbd
    • core-libs
    • None
    • source
    • minimal
    • Hide
      This is a new static method therefor there should be a minimal compatibility risk.
      Show
      This is a new static method therefor there should be a minimal compatibility risk.
    • Java API
    • SE

      Summary

      Introduce a static factory method UUID.timestampUUID() to generate Version 7 UUIDs as defined in RFC 9562 using a millisecond precision Unix timestamp with sub-millisecond extra percision.

      Problem

      The JDK currently supports version 3 and version 4 UUIDs via UUID.nameUUIDFromBytes() and UUID.randomUUID(), but does not provide built-in support for generating time-based version 7 UUIDs. UUIDv7 provides unique identifiers which can be sorted by creation time and has been of increasing popularity especialy in distributed systems.

      Solution

      Add a static factory method UUID.timestampUUID() to the java.util.UUID class. This method generates a Version 7 UUID by embedding the current Unix timestamp in ms into the first 48 bits, sets the version and variant fields according to RFC 9562, and uses a combination of sub ms time-derived bits and pseudorandom data to populate the remaining bits. Alternative approaches without the additional sub-ms time enhancement aswel as a counter-based approach were also considered but fell short in the percision/performance trade-off.

      Specification

      Update UUID.java spec with the following:

      diff --git a/src/java.base/share/classes/java/util/UUID.java b/src/java.base/share/classes/java/util/UUID.java
      index 08b9b87b7ca..0d3d4045d7e 100644
      --- a/src/java.base/share/classes/java/util/UUID.java
      +++ b/src/java.base/share/classes/java/util/UUID.java
      @@ -61,10 +61,10 @@
        * {@code UUID}.  The bit layout described above is valid only for a {@code
        * UUID} with a variant value of 2, which indicates the Leach-Salz variant.
        *
      - * <p> The version field holds a value that describes the type of this {@code
      - * UUID}.  There are four different basic types of UUIDs: time-based, DCE
      - * security, name-based, and randomly generated UUIDs.  These types have a
      - * version value of 1, 2, 3 and 4, respectively.
      + * <p> There are eight defined types of UUIDs, each identified by a version number:
      + * time-based (version 1), DCE security (version 2), name-based with MD5 (version 3),
      + * randomly generated (version 4), name-based with SHA-1 (version 5), reordered time-based (version 6),
      + * Unix epoch time-based (version 7), and custom-defined layout (version 8).
        *
        * <p> For more information including algorithms used to create {@code UUID}s,
        * see <a href="http://www.ietf.org/rfc/rfc4122.txt"> <i>RFC&nbsp;4122: A
      @@ -181,6 +181,44 @@ public static UUID nameUUIDFromBytes(byte[] name) {
               return new UUID(md5Bytes);
           }
      
      +    /**
      +     * Static factory to retrieve a type 7 (time based) {@code UUID} based on
      +     * the current timestamp, enhanced with sub-millisecond precision.
      +     *
      +     * The {@code UUID} embeds the current Unix timestamp in milliseconds into
      +     * the first 6 bytes, sets the version and variant bits as per RFC 9562,
      +     * replaces the first 12 random bits with a value derived from the current
      +     * system clock's sub-millisecond precision, and fills the
      +     * remaining bytes with cryptographically strong random data.
      +     *
      +     * @return A {@code UUID} generated from the current system time
      +     */
      +    public static UUID timestampUUID() {
      +        long msTime = System.currentTimeMillis();
      +        long nsTime = System.nanoTime();
      +        SecureRandom ng = Holder.numberGenerator;
      +        byte[] randomBytes = new byte[16];
      +        ng.nextBytes(randomBytes);
      +
      +        // Set the first 6 bytes to the ms time
      +        for (int i = 0; i < 6; i++) {
      +            randomBytes[i] = (byte) (msTime >>> (40 - 8 * i));
      +        }
      +
      +        // Scale sub-ms nanoseconds to a 12-bit value
      +        int nsBits = (int) ((nsTime % 1_000_000) / 1_000_000.0 * 4096);
      +
      +        // Set version and increased percision time bits
      +        randomBytes[6] = (byte) (0x70 | ((nsBits >>> 8) & 0x0F));
      +        randomBytes[7] = (byte) (nsBits & 0xFF);
      +
      +        // Set variant to 2
      +        randomBytes[8] &= 0x3F;
      +        randomBytes[8] |= (byte) 0x80;
      +
      +        return new UUID(randomBytes);
      +    }
      +
           private static final byte[] NIBBLES;
      static {
      byte[] ns = new byte[256]; @@ -323,6 +361,7 @@ public long getMostSignificantBits() { * <li>2 DCE security UUID * <li>3 Name-based UUID * <li>4 Randomly generated UUID + * <li>7 Unix timestamp-based UUID * </ul> * * @return The version number of this {@code UUID} @@ -481,7 +520,7 @@ public String toString() { HexDigits.put4(buf, 28, i3 >> 16); HexDigits.put4(buf, 32, i3); try { - return jla.uncheckedNewStringNoRepl(buf, StandardCharsets.ISO_8859_1); + return jla.newStringNoRepl(buf, StandardCharsets.ISO_8859_1); } catch (CharacterCodingException cce) { throw new AssertionError(cce); }

            kfarrell Kieran Farrell
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated: