Closed stevesimmons closed 2 years ago
I think it might be a good idea to include code examples, at least in a few of the most common programming languages, how to encode and decode those timestamps. Basically every programming language I know expresses precision timestamps as integers. For example https://docs.python.org/3/library/time.html#time.time_ns
Python code example
# Enough to represent nanoseconds from time.time_ns()
SUBSEC_BITS = 30
SUBSEC_DECIMAL_DIGITS = 9
def subsec_encode(
value: int,
subsec_bits: int = SUBSEC_BITS,
subsec_decimal_digits: int = SUBSEC_DECIMAL_DIGITS,
) -> int:
return value * 2 ** subsec_bits // 10 ** subsec_decimal_digits
def subsec_decode(
value: int,
subsec_bits: int = SUBSEC_BITS,
subsec_decimal_digits: int = SUBSEC_DECIMAL_DIGITS,
) -> int:
return -(-value * 10 ** subsec_decimal_digits // 2 ** subsec_bits)
def test_millisecond():
print("Testing millisecond conversions.")
for i in range(10 ** 3):
if i % 10 ** 2 == 0:
print(f"{i=} ...")
assert i == subsec_decode(subsec_encode(i, 10, 3), 10, 3)
def test_microsecond():
print("Testing microsecond conversions.")
for i in range(10 ** 6):
if i % 10 ** 5 == 0:
print(f"{i=} ...")
assert i == subsec_decode(subsec_encode(i, 20, 6), 20, 6)
def test_nanosecond():
print("Testing nanosecond conversions.")
for i in range(10 ** 9):
if i % 10 ** 6 == 0:
print(f"{i=} ...")
assert i == subsec_decode(subsec_encode(i, 30, 9), 30, 9)
def main():
import time
timestamp = time.time_ns()
unixts, ns = divmod(timestamp, 10 ** SUBSEC_DECIMAL_DIGITS)
subsec = subsec_encode(ns)
subsec_to_ns = subsec_decode(subsec)
print(f"{timestamp=}")
print(f"{unixts=}")
print(f"{ns=}")
print(f"{subsec=}")
print(f"{subsec_to_ns=}")
test_millisecond()
test_microsecond()
test_nanosecond()
if __name__ == "__main__":
main()
Ouput is something like
timestamp=1641075060289465000
unixts=1641075060
ns=289465000
subsec=310810677
subsec_to_ns=289465000
Testing millisecond conversions.
i=0 ...
Those main()
and test_...
functions here are to show that it actually works and maybe you would just include subsec_encode()
and subsec_decode()
in the actual documentation. The encoding and decoding should work correctly even if you change SUBSEC_BITS
to something like the above mentioned 12 for a millisecond precision.
Great code example @oittaa !
Some of the UUIDv7 implementations I've seen on GitHub have not implemented fractional seconds correctly. For instance, putting the number of milliseconds directly into the 12 bit field
subsec_a
, thereby using a 0-999 subset of the full 0-4095 range.To prevent this, could the initial descriptions of the
subsec_a
andsubsec_b
fields (around L568 of the-02.txt
draft) make it more explicit that these must use the full range, with an example of what not to do.Currently this isn't spelled out (though discussed in issue #24). For instance:
Section 4.4.1. - UUIDv7 Timestamp Usage
Section 4.4.4.1. - UUIDv7 Encoding
It's only once the reader gets down to L845 that the requirements are spelled out:
Section 4.4.4.2. UUIDv7 Decoding
As an additional suggestion, it would be very helpful if the text could include some examples of uuids and their min/max implied timestamps, to serve as test cases in unit tests.