From 50332f796ecd3b77b0b332397ae04f9951912f45 Mon Sep 17 00:00:00 2001 From: Kefu Chai Date: Thu, 30 Nov 2023 16:29:35 +0800 Subject: [PATCH] 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 Closes scylladb/scylladb#16235 --- scripts/base36-uuid.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/scripts/base36-uuid.py b/scripts/base36-uuid.py index 6c22268d7d..ed13c5515c 100755 --- a/scripts/base36-uuid.py +++ b/scripts/base36-uuid.py @@ -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(