import base64
import time
import secrets
import threading
class RandomUidGenerator:
__UID_VERSION = 0b10
def __init__(self):
self.__lock = threading.Lock()
self.__timestamp_ms = None
self.__seed = None
self.__counter = None
def generate(self, now_dt = None):
now_ms = (RandomUidGenerator.__now_ms()
if now_dt is None
else int(now_dt.timestamp() * 1000))
timestamp, counter = self.__advance(now_ms)
return RandomUidGenerator.__encode(timestamp, counter)
def __advance(self, now_ms):
with self.__lock:
if self.__timestamp_ms is None or now_ms > self.__timestamp_ms:
self.__reseed(now_ms)
else:
self.__counter = self.__counter + 1 & (1 << 33) - 1
if self.__counter == self.__seed:
self.__reseed(self.__timestamp_ms + 1)
return self.__timestamp_ms, self.__counter
def __reseed(self, timestamp_ms):
if timestamp_ms >> 45:
raise ValueError(f"Timestamp out of range: {timestamp_ms}")
self.__counter = self.__seed = secrets.randbits(33)
self.__timestamp_ms = timestamp_ms
@staticmethod
def __now_ms():
return time.time_ns() // 1_000_000
@staticmethod
def __encode(timestamp, counter):
uid_mid = counter >> 13
uid_low = RandomUidGenerator.__UID_VERSION << 13 | counter & (1 << 13) - 1
uid_bigint = timestamp << 35 | uid_mid << 15 | uid_low
return base64.b32hexencode(uid_bigint.to_bytes(10)).decode('utf-8').lower()
import datetime
import pytz
generator = RandomUidGenerator()
print(generator.generate(pytz.timezone('America/Chicago').localize(datetime.datetime(2010, 4, 1, 1, 28, 38, 422000))))
print(generator.generate())
print(generator.generate())
Click Run or press shift + ENTER to run code