script/base36-uuid.py: interpret timestamp with Gregorian calendar

UUID v1 uses an epoch derived frmo Gregorian calendar. but
base36-uuid.py interprets the timestamp with the UNIX epoch time.
that's why it prints a UUID like

```console
$ ./scripts/base36-uuid.py -d 3gbi_0mhs_4sjf42oac6rxqdsnyx
date = 2411-02-16 16:05:52
decimicro_seconds = 0x7ad550
lsb = 0xafe141a195fe0d59
```

even this UUID is generated on nov 30, 2023. so in this change,
we shift the time with the timestamp of UNIX epoch derived from
the Gregorian calendar's day 0. so, after this change, we have:

```console
$ ./scripts/base36-uuid.py -d 3gbi_0mhs_4sjf42oac6rxqdsnyx
date = 2023-11-30 16:05:52
decimicro_seconds = 0x7ad550
lsb = 0xafe141a195fe0d59
```

see https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.4

Signed-off-by: Kefu Chai <kefu.chai@scylladb.com>

Closes scylladb/scylladb#16235
This commit is contained in:
Kefu Chai
2023-11-30 16:29:35 +08:00
committed by Botond Dénes
parent 97244eb68e
commit 50332f796e

View File

@@ -132,9 +132,16 @@ class TimeUuid:
def lsb(self) -> int:
return int.from_bytes(self.uuid.bytes[8:], byteorder='big')
# the duration between 00:00 15 Oct 1582 and UNIX epoch
# see also utils/UUID_gen.hh
UNIX_EPOCH_SINCE_GREGORIAN_DAY0 = 122192928000000000
@property
def timestamp(self) -> (datetime.datetime, int):
seconds, decimicro_seconds = divmod(self.uuid.time, DECIMICRO_RATIO)
# UUID v1 uses a timestamp epoch derived from Gregorian calendar, so we
# need to translate the timetamp to the UNIX time
unix_time = self.uuid.time - self.UNIX_EPOCH_SINCE_GREGORIAN_DAY0
seconds, decimicro_seconds = divmod(unix_time, DECIMICRO_RATIO)
return datetime.datetime.fromtimestamp(seconds), decimicro_seconds
def print_field(self, field: str, print_in_hex: bool) -> None:
@@ -178,6 +185,10 @@ def test_dencode_base36() -> None:
assert timeuuid.lsb == expected_lsb
assert timeuuid.encode_with_base36() == encoded_uuid
timestamp, decimicro_seconds = timeuuid.timestamp
assert timestamp == datetime.datetime(2022, 5, 23, 18, 37, 52)
assert decimicro_seconds == 7040000
def main():
parser = argparse.ArgumentParser(