├── README.md └── python ├── new_uuid.py ├── testing_v6.py ├── testing_v7.py └── testing_v8.py /README.md: -------------------------------------------------------------------------------- 1 | # Update 2 | Final: 3 | ``` 4 | RFC 9562 5 | 6 | Title: Universally Unique IDentifiers (UUIDs) 7 | Author: K. Davis, 8 | B. Peabody, 9 | P. Leach 10 | Status: Standards Track 11 | Stream: IETF 12 | Date: May 2024 13 | Mailbox: kydavis@cisco.com, 14 | brad@peabody.io, 15 | pjl7@uw.edu 16 | Pages: 46 17 | Obsoletes: RFC 4122 18 | 19 | I-D Tag: draft-ietf-uuidrev-rfc4122bis-14.txt 20 | 21 | URL: https://www.rfc-editor.org/info/rfc9562 22 | 23 | DOI: 10.17487/RFC9562 24 | 25 | ``` 26 | 27 | # Prototypes 28 | Draft Prototypes and Tests for UUIDv6 and beyond 29 | | Name | Language | UUIDv6 | UUIDv7 | UUIDv8 | RFC/Draft | 30 | |--------------------------------------------------------------------------------------------------|------------|--------|--------|--------|-----------| 31 | | [uuid6/prototypes/python](https://github.com/uuid6/prototypes/tree/main/python) | Python | Yes | Yes | Yes | [draft-peabody-dispatch-new-uuid-format-02][draft-peabody-dispatch-new-uuid-format-02] | 32 | | [oittaa/uuid6-python](https://github.com/oittaa/uuid6-python) | Python | Yes | Yes | Yes | [draft-ietf-uuidrev-rfc4122bis-02][draft-ietf-uuidrev-rfc4122bis-02] | 33 | | [quwac/newnewid-python](https://github.com/quwac/newnewid-python) | Python | Yes | Yes | Yes | [draft-ietf-uuidrev-rfc4122bis-03][draft-ietf-uuidrev-rfc4122bis-03] and all below | 34 | | [jdknezek/uuid6-zig](https://github.com/jdknezek/uuid6-zig) | Zig | Yes | Yes | No | [draft-peabody-dispatch-new-uuid-format-03][draft-peabody-dispatch-new-uuid-format-03] | 35 | | [daegalus/dart-uuid](https://github.com/Daegalus/dart-uuid) | Dart | Yes | Yes | Yes | [draft-peabody-dispatch-new-uuid-format-04][draft-peabody-dispatch-new-uuid-format-04] | 36 | | [f4b6a3/uuid-creator](https://github.com/f4b6a3/uuid-creator) | Java | Yes | Yes | No | [draft-peabody-dispatch-new-uuid-format-04][draft-peabody-dispatch-new-uuid-format-04] | 37 | | [chrylis/time-based-uuid-reordering](https://github.com/chrylis/time-based-uuid-reordering) | Java | Yes | No | No | [draft-peabody-dispatch-new-uuid-format-04][draft-peabody-dispatch-new-uuid-format-04] | 38 | | [mikemix/php-uuid-v6](https://github.com/mikemix/php-uuid-v6) | PHP | Yes | No | No | [pre-ietf-draft-0x][pre-ietf-draft-0x] | 39 | | [oittaa/uuid-php](https://github.com/oittaa/uuid-php) | PHP | Yes | Yes | Yes | [draft-ietf-uuidrev-rfc4122bis-02][draft-ietf-uuidrev-rfc4122bis-02] | 40 | | [symfony/uid](https://github.com/symfony/uid/tree/6.2) | PHP | Yes | Yes | Yes | [draft-peabody-dispatch-new-uuid-format-04][draft-peabody-dispatch-new-uuid-format-04] | 41 | | [kurttheviking/uuid-with-v6-js](https://github.com/kurttheviking/uuid-with-v6-js) | JavaScript | Yes | No | No | [pre-ietf-draft-0x][pre-ietf-draft-0x] | 42 | | [bradleypeabody/gouuidv6](https://github.com/bradleypeabody/gouuidv6) | Go | Yes | No | No | [pre-ietf-draft-0x][pre-ietf-draft-0x] | 43 | | [gofrs/uuid](https://github.com/gofrs/uuid) | Go | Yes | Yes | No | [draft-peabody-dispatch-new-uuid-format-04][draft-peabody-dispatch-new-uuid-format-04] | 44 | | [sprql/uuid7-ruby](https://github.com/sprql/uuid7-ruby) | Ruby | No | Yes | No | [draft-peabody-dispatch-new-uuid-format-01][draft-peabody-dispatch-new-uuid-format-01] | 45 | | [kjmph/UUID_v7_for_Postgres.sql](https://gist.github.com/kjmph/5bd772b2c2df145aa645b837da7eca74) | Postgres | No | Yes | Yes | [draft-peabody-dispatch-new-uuid-format-03][draft-peabody-dispatch-new-uuid-format-03] | 46 | | [MatrixAI/js-id](https://github.com/MatrixAI/js-id) | TypeScript | No | Yes | No | [draft-peabody-dispatch-new-uuid-format-01][draft-peabody-dispatch-new-uuid-format-01] | 47 | | [LiosK/uuidv7](https://github.com/LiosK/uuidv7) | TypeScript | No | Yes | No | [draft-peabody-dispatch-new-uuid-format-04][draft-peabody-dispatch-new-uuid-format-04] | 48 | | [kripod/uuidv7](https://github.com/kripod/uuidv7) | TypeScript | No | Yes | No | [draft-peabody-dispatch-new-uuid-format-04][draft-peabody-dispatch-new-uuid-format-04] | 49 | | [karwa/uniqueid](https://github.com/karwa/uniqueid) | Swift | Yes | No | No | [draft-peabody-dispatch-new-uuid-format-02][draft-peabody-dispatch-new-uuid-format-02] | 50 | | [fabiolimace/UUIDv7_for_C](https://gist.github.com/fabiolimace/9873fe7bbcb1e6dc40638a4f98676d72) | C | No | Yes | No | [draft-peabody-dispatch-new-uuid-format-03][draft-peabody-dispatch-new-uuid-format-03] | 51 | | [LiosK/uuidv7-h](https://github.com/LiosK/uuidv7-h) | C/C++ | No | Yes | No | [draft-peabody-dispatch-new-uuid-format-04][draft-peabody-dispatch-new-uuid-format-04] | 52 | | [mareek/UUIDNext](https://github.com/mareek/UUIDNext) | C# | Yes | Yes | Yes | [draft-peabody-dispatch-new-uuid-format-04][draft-peabody-dispatch-new-uuid-format-04] | 53 | | [BaerMitUmlaut/GuidPlus](https://github.com/BaerMitUmlaut/GuidPlus) | C# | Yes | Yes | Yes | [draft-peabody-dispatch-new-uuid-format-02][draft-peabody-dispatch-new-uuid-format-02] | 54 | | [bgrainger/NGuid](https://github.com/bgrainger/NGuid) | C# | Yes | Yes | Yes | [draft-ietf-uuidrev-rfc4122bis-07][draft-ietf-uuidrev-rfc4122bis-07] | 55 | | [Medo/Uuid7](https://github.com/medo64/Medo.Uuid7) | C# | No | Yes | No | [draft-peabody-dispatch-new-uuid-format-04][draft-peabody-dispatch-new-uuid-format-04] | 56 | | [uuid-rs/uuid](https://github.com/uuid-rs/uuid) | Rust | Yes | Yes | Yes | [draft-peabody-dispatch-new-uuid-format-04][draft-peabody-dispatch-new-uuid-format-04] | 57 | | [LiosK/uuid7-rs](https://github.com/LiosK/uuid7-rs) | Rust | No | Yes | No | [draft-ietf-uuidrev-rfc4122bis-02][draft-ietf-uuidrev-rfc4122bis-02] | 58 | | [DianaNites/nuuid](https://github.com/DianaNites/nuuid) | Rust | Yes | Yes | Yes | [draft-peabody-dispatch-new-uuid-format-04][draft-peabody-dispatch-new-uuid-format-04] | 59 | | [jakwings/uuid.sh](https://github.com/jakwings/uuid.sh) | Shell | Yes | Yes | Yes | [draft-peabody-dispatch-new-uuid-format-04][draft-peabody-dispatch-new-uuid-format-04] | 60 | | [x4m/pg_uuid_next](https://github.com/x4m/pg_uuid_next) | C | No | Yes | Yes | [draft-peabody-dispatch-new-uuid-format-04][draft-peabody-dispatch-new-uuid-format-04] | 61 | | [pluots/udf-suite](https://github.com/pluots/udf-suite/tree/main) | MariaDB/MySQL | Yes | Yes | No | [draft-peabody-dispatch-new-uuid-format-04][draft-peabody-dispatch-new-uuid-format-04] | 62 | | [danielmarschall/uuid_mac_utils](https://github.com/danielmarschall/uuid_mac_utils/) | PHP | Yes | Yes | Yes | RFC 9562 | 63 | | [ramsey/uuid](https://github.com/ramsey/uuid/) | PHP | Yes | Yes | Yes | [draft-ietf-uuidrev-rfc4122bis-00][draft-ietf-uuidrev-rfc4122bis-00] ? | 64 | 65 | *Note: UUIDv8 prototypes will likely vary among implementations* 66 | 67 | # Contributing 68 | Create a repository and open a Pull Request to have this table updated. 69 | 70 | Please include: 71 | - Link to repository 72 | - Programming language used 73 | - List UUIDvX versions supported in repository 74 | - Define RFC version you are implementing (Preferably the [latest available](https://datatracker.ietf.org/doc/draft-peabody-dispatch-new-uuid-format/)) 75 | - Any comments/notes to include 76 | 77 | [pre-ietf-draft-0x]: http://gh.peabody.io/uuidv6/ 78 | [draft-peabody-dispatch-new-uuid-format-00]: https://tools.ietf.org/html/draft-peabody-dispatch-new-uuid-format-00 79 | [draft-peabody-dispatch-new-uuid-format-01]: https://tools.ietf.org/html/draft-peabody-dispatch-new-uuid-format-01 80 | [draft-peabody-dispatch-new-uuid-format-02]: https://tools.ietf.org/html/draft-peabody-dispatch-new-uuid-format-02 81 | [draft-peabody-dispatch-new-uuid-format-03]: https://tools.ietf.org/html/draft-peabody-dispatch-new-uuid-format-03 82 | [draft-peabody-dispatch-new-uuid-format-04]: https://tools.ietf.org/html/draft-peabody-dispatch-new-uuid-format-04 83 | [draft-ietf-uuidrev-rfc4122bis-00]: https://tools.ietf.org/html/draft-ietf-uuidrev-rfc4122bis-00 84 | [draft-ietf-uuidrev-rfc4122bis-01]: https://tools.ietf.org/html/draft-ietf-uuidrev-rfc4122bis-01 85 | [draft-ietf-uuidrev-rfc4122bis-02]: https://tools.ietf.org/html/draft-ietf-uuidrev-rfc4122bis-02 86 | [draft-ietf-uuidrev-rfc4122bis-03]: https://tools.ietf.org/html/draft-ietf-uuidrev-rfc4122bis-03 87 | [draft-ietf-uuidrev-rfc4122bis-07]: https://tools.ietf.org/html/draft-ietf-uuidrev-rfc4122bis-07 88 | [draft-ietf-uuidrev-rfc4122bis-08]: https://tools.ietf.org/html/draft-ietf-uuidrev-rfc4122bis-08 89 | -------------------------------------------------------------------------------- /python/new_uuid.py: -------------------------------------------------------------------------------- 1 | import time 2 | import random 3 | 4 | sequenceCounter = 0 5 | _last_v1timestamp = 0 6 | _last_v6timestamp = 0 7 | _last_v7timestamp = 0 8 | _last_v8timestamp = 0 9 | _last_uuid_int = 0 10 | _last_sequence = None 11 | uuidVariant = '10' 12 | 13 | def uuid1(devDebugs=False, returnType="hex"): 14 | """Generates a 128-bit version 1 UUID with 100-ns timestamp RFC 4122 Epoch and random node 15 | 16 | example: dc38cdf9-22fe-11eb-a595-05480e4ca6cb 17 | 18 | format: time_low|time_mid|version|time_high|variant|clock_seq_high|clock_seq_low|node 19 | 20 | Adapted from https://github.com/python/cpython/blob/3.9/Lib/uuid.py 21 | Fixed Clock Sequence to be RFC4122 Compliant as per https://datatracker.ietf.org/doc/html/rfc4122#section-4.2.1 22 | 23 | Test Decode here: https://www.uuidtools.com/decode 24 | 25 | :param devDebugs: True, False 26 | :param returnType: bin, int, hex 27 | :return: bin, int, hex 28 | """ 29 | 30 | global _last_v1timestamp 31 | version = "0001" 32 | 33 | nanoseconds = time.time_ns() 34 | # 0x01b21dd213814000 is the number of 100-ns intervals between the 35 | # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00. 36 | timestamp = (nanoseconds // 100) + 0x01b21dd213814000 37 | 38 | ### Fixing Clock Sequence Counter 39 | # Sequence starts at 0, increments if timestamp is the same, the sequence increments by 1 40 | # Resets if timestamp int is larger than _last_v1timestamp used for UUID generation 41 | if timestamp <= _last_v1timestamp: 42 | sequenceCounter = int(sequenceCounter) + 1 43 | if devDebugs == True: 44 | print("Sequence: Incrementing Sequence to {0}".format(str(sequenceCounter))) 45 | if timestamp > _last_v1timestamp: 46 | sequenceCounter = 0 47 | if devDebugs == True: 48 | print("Sequence: Setting to {0}".format(str(sequenceCounter))) 49 | 50 | # Set these two before moving on 51 | _last_v1timestamp = timestamp 52 | _last_sequence = int(sequenceCounter) 53 | 54 | time_low = timestamp & 0xffffffff 55 | time_mid = (timestamp >> 32) & 0xffff 56 | time_hi_version = (timestamp >> 48) & 0x0fff 57 | time_hi_version = int(version + f'{time_hi_version:012b}', 2) 58 | clock_seq_low = sequenceCounter & 0xff 59 | clock_seq_hi_variant = (sequenceCounter >> 8) & 0x3f 60 | clock_seq_hi_variant = int(uuidVariant + f'{clock_seq_hi_variant:08b}'[-6:], 2) 61 | node = random.getrandbits(48) # Instead of MAC 62 | 63 | UUIDv1_bin = "{0}{1}{2}{3}{4}{5}".format(f'{time_low:032b}', 64 | f'{time_mid:016b}', 65 | f'{time_hi_version:016b}', 66 | f'{clock_seq_hi_variant:08b}', 67 | f'{clock_seq_low:08b}', 68 | f'{node:048b}') 69 | 70 | UUIDv1_int = int(UUIDv1_bin, 2) 71 | UUIDv1_hex = hex(int(UUIDv1_bin, 2))[2:] 72 | UUIDv1_formatted = '-'.join( 73 | [UUIDv1_hex[:8], UUIDv1_hex[8:12], UUIDv1_hex[12:16], UUIDv1_hex[16:20], UUIDv1_hex[20:32]]) 74 | 75 | if devDebugs: 76 | print("UUIDv1 Con: {0}|{1}|{2}|{3}|{4}|{5}".format(f'{time_low:032b}', 77 | f'{time_mid:016b}', 78 | f'{time_hi_version:016b}', 79 | f'{clock_seq_hi_variant:08b}', 80 | f'{clock_seq_low:08b}', 81 | f'{node:048b}')) 82 | print("UUIDv1 Bin: {0} (len: {1})".format(UUIDv1_bin, len(UUIDv1_bin))) 83 | print("UUIDv1 Int: {0}".format(UUIDv1_int)) 84 | print("UUIDv1 Hex: " + UUIDv1_formatted) 85 | print("\n") 86 | 87 | return UUIDv1_formatted 88 | 89 | 90 | def uuid6(devDebugs=False, returnType="hex"): 91 | """Generates a 128-bit version 6 UUID with 100-ns timestamp RFC 4122 Epoch without re-ordering bits 92 | 93 | example: 1eb22fe4-3f0c-62b1-a88c-8dc55231702f 94 | 95 | format: time_high|time_mid|version|time_low|variant|clock_seq_high|clock_seq_low|node 96 | 97 | :param devDebugs: True, False 98 | :param returnType: bin, int, hex 99 | :return: bin, int, hex 100 | """ 101 | 102 | global _last_v6timestamp 103 | global _last_sequence 104 | global sequenceCounter 105 | version = "0110" 106 | 107 | nanoseconds = time.time_ns() 108 | # 0x01b21dd213814000 is the number of 100-ns intervals between the 109 | # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00. 110 | timestamp = (nanoseconds // 100) + 0x01b21dd213814000 111 | 112 | ### Fixing Clock Sequence Counter 113 | # Sequence starts at 0, increments if timestamp is the same, the sequence increments by 1 114 | # Resets if timestamp int is larger than _last_v6timestamp used for UUID generation 115 | if timestamp <= _last_v6timestamp: 116 | sequenceCounter = int(sequenceCounter) + 1 117 | if devDebugs == True: 118 | print("Sequence: Incrementing Sequence to {0}".format(str(sequenceCounter))) 119 | if timestamp > _last_v6timestamp: 120 | sequenceCounter = 0 121 | if devDebugs == True: 122 | print("Sequence: Setting to {0}".format(str(sequenceCounter))) 123 | 124 | # Set these two before moving on 125 | _last_v6timestamp = timestamp 126 | _last_sequence = int(sequenceCounter) 127 | 128 | # Start Changes 129 | new_time_high_mid = f'{timestamp:060b}'[:48] # most-sig 48 time 130 | new_time_low_version = version + f'{timestamp:060b}'[-12:] # most-sig version + least-sig 12 time 131 | # Convert to fit the old UUIDv1 variable naming 132 | time_low = int(new_time_high_mid[:32], 2) 133 | time_mid = int(new_time_high_mid[-16:], 2) 134 | time_hi_version = int(new_time_low_version, 2) 135 | # End Changes 136 | 137 | clock_seq_low = sequenceCounter & 0xff 138 | clock_seq_hi_variant = (sequenceCounter >> 8) & 0x3f 139 | clock_seq_hi_variant = int(uuidVariant + f'{clock_seq_hi_variant:08b}'[-6:], 2) 140 | node = random.getrandbits(48) 141 | 142 | UUIDv6_bin = "{0}{1}{2}{3}{4}{5}".format(f'{time_low:032b}', 143 | f'{time_mid:016b}', 144 | f'{time_hi_version:016b}', 145 | f'{clock_seq_hi_variant:08b}', 146 | f'{clock_seq_low:08b}', 147 | f'{node:048b}') 148 | UUIDv6_int = int(UUIDv6_bin, 2) 149 | UUIDv6_hex = hex(int(UUIDv6_bin, 2))[2:] 150 | UUIDv6_formatted = '-'.join( 151 | [UUIDv6_hex[:8], UUIDv6_hex[8:12], UUIDv6_hex[12:16], UUIDv6_hex[16:20], UUIDv6_hex[20:32]]) 152 | 153 | if devDebugs: 154 | print("UUIDv6 Con: {0}|{1}|{2}|{3}|{4}|{5}".format(f'{time_low:032b}', 155 | f'{time_mid:016b}', 156 | f'{time_hi_version:016b}', 157 | f'{clock_seq_hi_variant:08b}', 158 | f'{clock_seq_low:08b}', 159 | f'{node:048b}')) 160 | print("UUIDv6 Bin: {0} (len: {1})".format(UUIDv6_bin, len(UUIDv6_bin))) 161 | print("UUIDv6 Int: {0}".format(UUIDv6_int)) 162 | print("UUIDv6 Hex: " + UUIDv6_formatted) 163 | print("\n") 164 | 165 | if returnType.lower() == "bin": 166 | return UUIDv6_bin 167 | if returnType.lower() == "int": 168 | return UUIDv6_int 169 | if returnType.lower() == "hex": 170 | return UUIDv6_formatted 171 | 172 | 173 | def uuid7(devDebugs=False, returnType="hex"): 174 | """Generates a 128-bit version 7 UUID with nanoseconds precision timestamp and random node 175 | 176 | example: 061cdd23-93a0-73df-a200-6ff3e72d92e9 177 | 178 | format: unixts|subsec_a|version|subsec_b|variant|subsec_seq_node 179 | 180 | :param devDebugs: True, False 181 | :param returnType: bin, int, hex 182 | :return: bin, int, hex 183 | """ 184 | 185 | global _last_v7timestamp 186 | global _last_uuid_int 187 | global _last_sequence 188 | global sequenceCounter 189 | global uuidVariant 190 | uuidVersion = '0111' # ver 7 191 | sec_bits = 36 # unixts at second precision 192 | subsec_bits = 30 # Enough to represent NS 193 | version_bits = 4 # '0111' for ver 7 194 | variant_bits = 2 # '10' Static for UUID 195 | sequence_bits = 8 # Enough for 256 UUIDs per NS 196 | node_bits = (128 - sec_bits - subsec_bits - version_bits - variant_bits - sequence_bits) # 48 197 | 198 | ### Timestamp Work 199 | # Produces unix epoch with nanosecond precision 200 | timestamp = time.time_ns() # Produces 64-bit NS timestamp 201 | # Subsecond Math 202 | subsec_decimal_digits = 9 # Last 9 digits of are subsection precision 203 | subsec_decimal_divisor = (10 ** subsec_decimal_digits) # 1000000000 NS in 1 second 204 | integer_part = int(timestamp / subsec_decimal_divisor) # Get seconds 205 | sec = integer_part 206 | # Conversion to decimal 207 | fractional_part = round((timestamp % subsec_decimal_divisor) / subsec_decimal_divisor, subsec_decimal_digits) 208 | subsec = round(fractional_part * (2 ** subsec_bits)) # Convert to 30 bit int, round 209 | 210 | if devDebugs == True: 211 | print("Timestamp: " + str(timestamp)) 212 | print("Sec: " + str(sec)) 213 | print("Subsec Int: " + str(subsec)) 214 | print("Subsec Dec: " + "{0:.9f}".format(fractional_part)) # Print with trailing 0s 215 | test_timestamp = str(sec) + str("{0:.9f}".format(fractional_part)[-9:]) # Concat and drop leading '0.' 216 | if test_timestamp == str(timestamp): # Quick test for subsec math 217 | print("Good subsec math") 218 | else: 219 | print("Bad Subsec Math") 220 | 221 | ### Binary Conversions 222 | ### Need subsec_a (12 bits), subsec_b (12-bits), and subsec_c (leftover bits starting subsec_seq_node) 223 | unixts = f'{sec:036b}' 224 | subsec_binary = f'{subsec:030b}' 225 | subsec_a = subsec_binary[:12] # Upper 12 226 | subsec_b_c = subsec_binary[-18:] # Lower 18 227 | subsec_b = subsec_b_c[:12] # Upper 12 228 | subsec_c = subsec_binary[-6:] # Lower 6 229 | 230 | ### Sequence Work 231 | # Sequence starts at 0, increments if timestamp is the same, the sequence increments by 1 232 | # Resets if timestamp int is larger than _last_v7timestamp used for UUID generation 233 | # Will be 8 bits for NS timestamp 234 | if timestamp <= _last_v7timestamp: 235 | sequenceCounter = int(sequenceCounter) + 1 236 | if devDebugs == True: 237 | print("Sequence: Incrementing Sequence to {0}".format(str(sequenceCounter))) 238 | if timestamp > _last_v7timestamp: 239 | sequenceCounter = 0 240 | if devDebugs == True: 241 | print("Sequence: Setting to {0}".format(str(sequenceCounter))) 242 | 243 | sequenceCounterBin = f'{sequenceCounter:08b}' 244 | 245 | # Set these two before moving on 246 | _last_v7timestamp = timestamp 247 | _last_sequence = int(sequenceCounter) 248 | 249 | ### Random Node Work 250 | randomInt = random.getrandbits(node_bits) 251 | randomBinary = f'{randomInt:048b}' 252 | 253 | # Create subsec_seq_node 254 | subsec_seq_node = subsec_c + sequenceCounterBin + randomBinary 255 | 256 | ### Formatting Work 257 | # Bin merge and Int creation 258 | UUIDv7_bin = unixts + subsec_a + uuidVersion + subsec_b + uuidVariant + subsec_seq_node 259 | UUIDv7_int = int(UUIDv7_bin, 2) 260 | if devDebugs == True: # Compare previous Int. Should always be higher 261 | if UUIDv7_int < _last_uuid_int and _last_uuid_int != 0: 262 | print("Error: UUID went Backwards!") 263 | print("UUIDv7 Last: " + str(_last_uuid_int)) 264 | print("UUIDv7 Curr: " + str(UUIDv7_int)) 265 | _last_uuid_int = UUIDv7_int 266 | 267 | # Convert Hex to Int then splice in dashes 268 | UUIDv7_hex = f'{UUIDv7_int:032x}' # int to hex 269 | UUIDv7_formatted = '-'.join( 270 | [UUIDv7_hex[:8], UUIDv7_hex[8:12], UUIDv7_hex[12:16], UUIDv7_hex[16:20], UUIDv7_hex[20:32]]) 271 | 272 | if devDebugs == True: 273 | print("UUIDv7 Con: {0}|{1}|{2}|{3}|{4}|{5}".format(unixts, 274 | subsec_a, 275 | uuidVersion, 276 | subsec_b, 277 | uuidVariant, 278 | subsec_seq_node)) 279 | print("UUIDv7 Bin: {0} (len: {1})".format(UUIDv7_bin, len(UUIDv7_bin))) 280 | print("UUIDv7 Int: " + str(UUIDv7_int)) 281 | print("UUIDv7 Hex: " + UUIDv7_formatted) 282 | print("\n") 283 | 284 | if returnType.lower() == "bin": 285 | return UUIDv7_bin 286 | if returnType.lower() == "int": 287 | return UUIDv7_int 288 | if returnType.lower() == "hex": 289 | return UUIDv7_formatted 290 | 291 | 292 | def uuid8(epochType="unix", timestampLength=64, customNode=None, devDebugs=False, returnType="hex"): 293 | """ Generates a 128-bit version 8 UUID with variable length timestamp, sequence and node. 294 | 295 | example: 1645f70f-066f-8a82-8413-f05792814889 296 | 297 | format: time_high|version|time_or_sequence|variant|node 298 | 299 | :param epochType: Unix or Gregorian 300 | :param timestampLength: Integer 32, 48, 64 (really 60, 4 least significant bits will be cut off) 301 | :param customNode = Integer value 302 | :param devDebugs: True, False 303 | :param returnType: bin, int, hex 304 | :return: bin, int, hex 305 | """ 306 | 307 | global _last_v8timestamp 308 | global _last_uuid_int 309 | global _last_sequence 310 | global sequenceCounter 311 | global uuidVariant 312 | uuidVersion = '1000' # ver 8 313 | 314 | if devDebugs == None: 315 | devDebugs = False 316 | 317 | ### Timestamp Work 318 | # TIMESTAMP NOTE as per Draft 01 319 | # If using Gregorian Timestamp please attempt to use UUIDv1 or UUIDv6 320 | # If using Unix Timestamp please attempt to use UUIDv7 321 | # This is a UUIDv8 demo detailing how one may format a UUIDv8 using well-known timestamps at different lengths 322 | # Other timestamps types and usage will be variable among application implementations 323 | epochTypes = ["unix", "gregorian"] 324 | if epochType.lower() not in epochTypes: 325 | raise ValueError("Epoch must either be Unix or Gregorian") 326 | 327 | timestampLengths = [32, 48, 64] 328 | if timestampLength not in timestampLengths: 329 | raise ValueError('timestampLength integer must be a value of 32, 48, 64') 330 | 331 | if timestampLength == 32: # Padded to 64 bits 332 | timestamp = int(time.time()) 333 | if epochType.lower() == "gregorian": 334 | raise ValueError('gregorian epoch cannot be used with 32-bit timestamp, change to unix') 335 | binaryTimestamp = f'{timestamp:032b}' + f'{int(0):032b}' # 64 1/0s 336 | time_high = binaryTimestamp[:48] # Most significant 48 337 | time_low = binaryTimestamp[-16:] # Least significant 16 338 | time_msb_low = time_low[:12] # Most significant 12 of those 16 339 | elif timestampLength == 48: # Trimmed to 48 bits 340 | timestamp = time.time_ns() 341 | if epochType.lower() == "gregorian": 342 | raise ValueError('gregorian epoch cannot be used with 48-bit timestamp, change to unix') 343 | binaryTimestamp = f'{timestamp:064b}' # 64 1/0s 344 | # time_high = binaryTimestamp[:48] # Most significant 48 345 | time_high = binaryTimestamp[-48:] # Least significant 48 346 | time_msb_low = f'{int(0):012b}' # 12 0s 347 | elif timestampLength == 64: # Trimmed to 60 bits 348 | timestamp = time.time_ns() 349 | if epochType.lower() == "gregorian": 350 | # 0x01b21dd213814000 is the number of 100-ns intervals between the 351 | # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00. 352 | timestamp = (timestamp // 100) + 0x01b21dd213814000 353 | binaryTimestamp = f'{timestamp:064b}' # 64 1/0s 354 | time_high = binaryTimestamp[:48] # Most significant 48 355 | time_low = binaryTimestamp[-16:] # Least significant 16 356 | time_msb_low = time_low[:12] # Most significant 12 of those 16 357 | 358 | if int(timestamp) < int(_last_v8timestamp) and _last_v8timestamp != 0: 359 | raise ValueError("Timestamp lower than previous known timestamp") 360 | 361 | if devDebugs == True: 362 | print("Curr Timestamp: {0}".format(int(timestamp))) 363 | print("Last Timestamp: {0}".format(int(_last_v8timestamp))) 364 | 365 | ### Sequence Work 366 | # Sequence starts at 0, increments if timestamp is the same the sequence increments by 1 367 | # Resets if timestamp int is larger than _last_v8timestamp used for UUID generation 368 | if timestamp <= _last_v8timestamp: 369 | sequenceCounter = int(sequenceCounter) + 1 370 | if devDebugs == True: 371 | print("Sequence: Incrementing Sequence to {0}".format(str(sequenceCounter))) 372 | if timestamp > _last_v8timestamp: 373 | sequenceCounter = 0 374 | if devDebugs == True: 375 | print("Sequence: Setting to {0}".format(str(sequenceCounter))) 376 | 377 | # 32 and 48 will use time_or_seq as sequence 378 | # 64 will use part of node as seq 379 | # 32 also require more sequence space per timestamp tick thus more bits 380 | if (timestampLength == 32) or (timestampLength == 48): 381 | sequenceCounterBin = f'{sequenceCounter:012b}' 382 | else: 383 | sequenceCounterBin = f'{sequenceCounter:08b}' 384 | 385 | # Set these two before moving on 386 | _last_v8timestamp = timestamp 387 | _last_sequence = int(sequenceCounter) 388 | 389 | ### Node Work 390 | if (timestampLength == 32) or (timestampLength == 48): 391 | time_or_seq = sequenceCounterBin # Set to sequence 392 | if customNode == None: 393 | randomInt = random.getrandbits(62) 394 | randomBinary = f'{randomInt:062b}' 395 | node = randomBinary 396 | else: 397 | node = f'{customNode:062b}' 398 | if timestampLength == 64: 399 | time_or_seq = time_msb_low 400 | if customNode == None: 401 | randomInt = random.getrandbits(54) 402 | randomBinary = f'{randomInt:054b}' 403 | node = sequenceCounterBin + randomBinary 404 | else: 405 | node = sequenceCounterBin + f'{customNode:054b}' 406 | 407 | ### Formatting Work 408 | # Bin merge and Int creation 409 | # UUIDv8_bin = time_high + uuidVersion + time_msb_low + uuidVariant + sequenceCounterBin + randomBinary 410 | UUIDv8_bin = time_high + uuidVersion + time_or_seq + uuidVariant + node 411 | UUIDv8_int = int(UUIDv8_bin, 2) 412 | if devDebugs == True: # Compare previous Int. Should always be higher 413 | if UUIDv8_int < _last_uuid_int and _last_uuid_int != 0: 414 | print("Error: UUID went Backwards!") 415 | print("UUIDv8 Last: " + str(_last_uuid_int)) 416 | print("UUIDv8 Curr: " + str(UUIDv8_int)) 417 | _last_uuid_int = UUIDv8_int 418 | 419 | # Convert Hex to Int then splice in dashes 420 | UUIDv8_hex = hex(int(UUIDv8_bin, 2))[2:] 421 | UUIDv8_formatted = '-'.join( 422 | [UUIDv8_hex[:8], UUIDv8_hex[8:12], UUIDv8_hex[12:16], UUIDv8_hex[16:20], UUIDv8_hex[20:32]]) 423 | 424 | if devDebugs == True: 425 | print("UUIDv8 Con: {0}|{1}|{2}|{3}|{4}".format(time_high, uuidVersion, time_or_seq, uuidVariant, node)) 426 | print("UUIDv8 Bin: {0} (len: {1})".format(UUIDv8_bin, len(UUIDv8_bin))) 427 | print("UUIDv8 Int: " + str(UUIDv8_int)) 428 | print("UUIDv8 Hex: " + UUIDv8_formatted) 429 | print("\n") 430 | 431 | if returnType.lower() == "bin": 432 | return UUIDv8_bin 433 | if returnType.lower() == "int": 434 | return UUIDv8_int 435 | if returnType.lower() == "hex": 436 | return UUIDv8_formatted 437 | -------------------------------------------------------------------------------- /python/testing_v6.py: -------------------------------------------------------------------------------- 1 | import new_uuid 2 | import random 3 | 4 | """ 5 | Testing: 6 | - Generate 1000 UUIDs 7 | - Add to dictionary with key, value being UUID and generation sequence counter 8 | - Add to unordered list 9 | - Shuffle the list 10 | - Sort the list 11 | - Iterate through the sorted list and compare current list position to dictionary's value to verify they match 12 | """ 13 | 14 | # Global Variables 15 | devDebugs = False # True to view generation process 16 | returnType = "hex" # String Values: bin, int, hex 17 | showUUIDs = False # True to view the generated UUID returnType and lists 18 | clock_seq = None # Set Clock Sequence 19 | 20 | def v6Tests(showUUIDs): 21 | counter = 0 22 | testList = [] 23 | masterDict = {} 24 | while counter < 1000: 25 | # UUIDv6 = new_uuid.uuid1(devDebugs, returnType) 26 | UUIDv6 = new_uuid.uuid6(devDebugs, returnType) 27 | testList.append(UUIDv6) 28 | masterDict[UUIDv6] = counter 29 | counter += 1 30 | 31 | if showUUIDs: 32 | print("\n") 33 | print("Start List") 34 | counter = 0 35 | for UUID in testList: 36 | print('{0}: {1}'.format(str(counter), UUID)) 37 | counter+= 1 38 | 39 | random.shuffle(testList) 40 | if showUUIDs: 41 | print("\n") 42 | print("Shuffled List") 43 | counter = 0 44 | for UUID in testList: 45 | print('{0}: {1}'.format(str(counter), UUID)) 46 | counter += 1 47 | 48 | print("\n") 49 | print("Post Sorted List Results") 50 | testList.sort() 51 | counter = 0 52 | failCount = 0 53 | for UUID in testList: 54 | if masterDict[UUID] != counter: 55 | failCount+=1 56 | print('{0}: {1}'.format(str(counter), UUID)) 57 | counter+= 1 58 | if failCount == 0: 59 | print("+ No Failures Observed") 60 | 61 | v6Tests(showUUIDs) -------------------------------------------------------------------------------- /python/testing_v7.py: -------------------------------------------------------------------------------- 1 | import new_uuid 2 | import random 3 | 4 | """ 5 | Testing: 6 | - Generate 1000 UUIDs 7 | - Add to dictionary with key, value being UUID and generation sequence counter 8 | - Add to unordered list 9 | - Shuffle the list 10 | - Sort the list 11 | - Iterate through the sorted list and compare current list position to dictionary's value to verify they match 12 | """ 13 | 14 | # Global Variables 15 | devDebugs = False # True to view generation process 16 | returnType = "hex" # String Values: bin, int, hex 17 | 18 | showUUIDs = False # True to view the generated UUID returnType and lists 19 | 20 | def v7Tests(showUUIDs): 21 | counter = 0 22 | testList = [] 23 | masterDict = {} 24 | while counter < 1000: 25 | UUIDv7 = new_uuid.uuid7(devDebugs, returnType) 26 | testList.append(UUIDv7) 27 | masterDict[UUIDv7] = counter 28 | counter += 1 29 | 30 | if showUUIDs: 31 | print("\n") 32 | print("Start List") 33 | counter = 0 34 | for UUID in testList: 35 | print('{0}: {1}'.format(str(counter), UUID)) 36 | counter+= 1 37 | 38 | random.shuffle(testList) 39 | if showUUIDs: 40 | print("\n") 41 | print("Shuffled List") 42 | counter = 0 43 | for UUID in testList: 44 | print('{0}: {1}'.format(str(counter), UUID)) 45 | counter += 1 46 | 47 | print("\n") 48 | print("Post Sorted List Results") 49 | testList.sort() 50 | counter = 0 51 | failCount = 0 52 | for UUID in testList: 53 | if masterDict[UUID] != counter: 54 | failCount+=1 55 | print('{0}: {1}'.format(str(counter), UUID)) 56 | counter+= 1 57 | if failCount == 0: 58 | print("+ No Failures Observed") 59 | 60 | v7Tests(showUUIDs) 61 | -------------------------------------------------------------------------------- /python/testing_v8.py: -------------------------------------------------------------------------------- 1 | import new_uuid 2 | import random 3 | 4 | """ 5 | Testing: 6 | - Generate 1000 UUIDs 7 | - Add to dictionary with key, value being UUID and generation sequence counter 8 | - Add to unordered list 9 | - Shuffle the list 10 | - Sort the list 11 | - Iterate through the sorted list and compare current list position to dictionary's value to verify they match 12 | """ 13 | 14 | # Global Variables 15 | epochType = "unix" # Unix or Gregorian 16 | timestampLength = 64 # Integer Values: 32, 48, 64 17 | customNode = None # Set integer for custom node value 18 | devDebugs = False # True to view generation process 19 | returnType = "hex" # String Values: bin, int, hex 20 | 21 | showUUIDs = False # True to view the generated UUID returnType and lists 22 | 23 | def v8Tests(showUUIDs): 24 | counter = 0 25 | testList = [] 26 | masterDict = {} 27 | while counter < 1000: 28 | UUIDv8 = new_uuid.uuid8(epochType, timestampLength, customNode, devDebugs, returnType) 29 | testList.append(UUIDv8) 30 | masterDict[UUIDv8] = counter 31 | counter += 1 32 | 33 | if showUUIDs: 34 | print("\n") 35 | print("Start List") 36 | counter = 0 37 | for UUID in testList: 38 | print('{0}: {1}'.format(str(counter), UUID)) 39 | counter+= 1 40 | 41 | random.shuffle(testList) 42 | if showUUIDs: 43 | print("\n") 44 | print("Shuffled List") 45 | counter = 0 46 | for UUID in testList: 47 | print('{0}: {1}'.format(str(counter), UUID)) 48 | counter += 1 49 | 50 | print("\n") 51 | print("Post Sorted List Results") 52 | testList.sort() 53 | counter = 0 54 | failCount = 0 55 | for UUID in testList: 56 | if masterDict[UUID] != counter: 57 | failCount+=1 58 | print('{0}: {1}'.format(str(counter), UUID)) 59 | counter+= 1 60 | if failCount == 0: 61 | print("+ No Failures Observed") 62 | 63 | v8Tests(showUUIDs) --------------------------------------------------------------------------------