version7.rst 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. .. _rfc4122.version7:
  2. ==========================
  3. Version 7: Unix Epoch Time
  4. ==========================
  5. .. note::
  6. Version 7, Unix Epoch time UUIDs are a new format of UUID, proposed in an
  7. `Internet-Draft under review`_ at the IETF. While the draft is still going
  8. through the IETF process, the version 7 format is not expected to change
  9. in any way that breaks compatibility.
  10. .. admonition:: ULIDs and Version 7 UUIDs
  11. :class: hint
  12. Version 7 UUIDs are binary-compatible with `ULIDs`_ (universally unique
  13. lexicographically-sortable identifiers).
  14. Both use a 48-bit timestamp in milliseconds since the Unix Epoch, filling
  15. the rest with random data. Version 7 UUIDs then add the version and variant
  16. bits required by the UUID specification, which reduces the randomness from
  17. 80 bits to 74. Otherwise, they are identical.
  18. You may even convert a version 7 UUID to a ULID.
  19. :ref:`See below for an example. <rfc4122.version7.ulid>`
  20. Version 7 UUIDs solve `two problems that have long existed`_ with the use of
  21. :ref:`version 1 <rfc4122.version1>` UUIDs:
  22. 1. Scattered database records
  23. 2. Inability to sort by an identifier in a meaningful way (i.e., insert order)
  24. To overcome these issues, we need the ability to generate UUIDs that are
  25. *monotonically increasing*.
  26. :ref:`Version 6 UUIDs <rfc4122.version6>` provide an excellent solution for
  27. those who need monotonically increasing, sortable UUIDs with the features of
  28. version 1 UUIDs (MAC address and clock sequence), but if those features aren't
  29. necessary for your application, using a version 6 UUID might be overkill.
  30. Version 7 UUIDs combine random data (like version 4 UUIDs) with a timestamp (in
  31. milliseconds since the Unix Epoch, i.e., 1970-01-01 00:00:00 UTC) to create a
  32. monotonically increasing, sortable UUID that doesn't have any privacy concerns,
  33. since it doesn't include a MAC address.
  34. For this reason, implementations should use version 7 UUIDs over versions 1 and
  35. 6, if possible.
  36. .. code-block:: php
  37. :caption: Generate a version 7, Unix Epoch time UUID
  38. :name: rfc4122.version7.example
  39. use Ramsey\Uuid\Uuid;
  40. $uuid = Uuid::uuid7();
  41. printf(
  42. "UUID: %s\nVersion: %d\nDate: %s\n",
  43. $uuid->toString(),
  44. $uuid->getFields()->getVersion(),
  45. $uuid->getDateTime()->format('r'),
  46. );
  47. This will generate a version 7 UUID and print out its string representation and
  48. the time it was created.
  49. It will look something like this:
  50. .. code-block:: text
  51. UUID: 01833ce0-3486-7bfd-84a1-ad157cf64005
  52. Version: 7
  53. Date: Wed, 14 Sep 2022 16:41:10 +0000
  54. To use an existing date and time to generate a version 7 UUID, you may pass a
  55. ``\DateTimeInterface`` instance to the ``uuid7()`` method.
  56. .. code-block:: php
  57. :caption: Generate a version 7 UUID from an existing date and time
  58. :name: rfc4122.version7.example-datetime
  59. use DateTimeImmutable;
  60. use Ramsey\Uuid\Uuid;
  61. $dateTime = new DateTimeImmutable('@281474976710.655');
  62. $uuid = Uuid::uuid7($dateTime);
  63. printf(
  64. "UUID: %s\nVersion: %d\nDate: %s\n",
  65. $uuid->toString(),
  66. $uuid->getFields()->getVersion(),
  67. $uuid->getDateTime()->format('r'),
  68. );
  69. Which will print something like this:
  70. .. code-block:: text
  71. UUID: ffffffff-ffff-7964-a8f6-001336ac20cb
  72. Version: 7
  73. Date: Tue, 02 Aug 10889 05:31:50 +0000
  74. .. tip::
  75. Version 7 UUIDs generated in ramsey/uuid are instances of UuidV7. Check out
  76. the :php:class:`Ramsey\\Uuid\\Rfc4122\\UuidV7` API documentation to learn
  77. more about what you can do with a UuidV7 instance.
  78. .. _rfc4122.version7.ulid:
  79. Convert a Version 7 UUID to a ULID
  80. ##################################
  81. As mentioned in the callout above, version 7 UUIDs are binary-compatible with
  82. `ULIDs`_. This means you can encode a version 7 UUID using `Crockford's Base 32
  83. algorithm`_ and it will be a valid ULID, timestamp and all.
  84. Using the third-party library `tuupola/base32`_, here's how we can encode a
  85. version 7 UUID as a ULID. Note that there's a little bit of work to perform the
  86. conversion, since you're working with different bases.
  87. .. code-block:: php
  88. :caption: Encode a version 7, Unix Epoch time UUID as a ULID
  89. :name: rfc4122.version7.example-ulid
  90. use Ramsey\Uuid\Uuid;
  91. use Tuupola\Base32;
  92. $crockford = new Base32([
  93. 'characters' => Base32::CROCKFORD,
  94. 'padding' => false,
  95. 'crockford' => true,
  96. ]);
  97. $uuid = Uuid::uuid7();
  98. // First, we must pad the 16-byte string to 20 bytes
  99. // for proper conversion without data loss.
  100. $bytes = str_pad($uuid->getBytes(), 20, "\x00", STR_PAD_LEFT);
  101. // Use Crockford's Base 32 encoding algorithm.
  102. $encoded = $crockford->encode($bytes);
  103. // That 20-byte string was encoded to 32 characters to avoid loss
  104. // of data. We must strip off the first 6 characters--which are
  105. // all zeros--to get a valid 26-character ULID string.
  106. $ulid = substr($encoded, 6);
  107. printf("ULID: %s\n", $ulid);
  108. This will print something like this:
  109. .. code-block:: text
  110. ULID: 01GCZ05N3JFRKBRWKNGCQZGP44
  111. .. caution::
  112. Be aware that all version 7 UUIDs may be converted to ULIDs but not all
  113. ULIDs may be converted to UUIDs.
  114. For that matter, all UUIDs of any version may be encoded as ULIDs, but they
  115. will not be monotonically increasing and sortable unless they are version 7
  116. UUIDs. You will also not be able to extract a meaningful timestamp from the
  117. ULID, unless it was converted from a version 7 UUID.
  118. .. _ULIDs: https://github.com/ulid/spec
  119. .. _Internet-Draft under review: https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00#section-5.7
  120. .. _two problems that have long existed: https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/
  121. .. _Crockford's Base 32 algorithm: https://www.crockford.com/base32.html
  122. .. _tuupola/base32: https://packagist.org/packages/tuupola/base32