"""ULID generation (Section 35). ULIDs are lexicographically sortable, timezone-independent identifiers linking logs, commits and monitoring. Implemented without an external dependency per the specification's "equivalent identifier" allowance. """ from __future__ import annotations import datetime as dt import secrets _ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" # Crockford base32 _RANDOM_LEN = 16 _TIME_LEN = 10 def _encode_time(timestamp_ms: int) -> str: value = max(timestamp_ms, 0) chars = [""] * _TIME_LEN for i in range(_TIME_LEN - 1, -1, -1): chars[i] = _ENCODING[value & 0x1F] value >>= 5 return "".join(chars) def _encode_random(count: int) -> str: # 5 bits per char; gather enough random bytes then fold bits = count * 5 nbytes = (bits + 7) // 8 raw = int.from_bytes(secrets.token_bytes(nbytes), "big") chars = [""] * count for i in range(count - 1, -1, -1): chars[i] = _ENCODING[raw & 0x1F] raw >>= 5 return "".join(chars) def new_ulid(timestamp: dt.datetime | None = None) -> str: """Generate a ULID string, e.g. 01J7K8HYV84ZKFH1J4T9R8DZQN.""" moment = timestamp or dt.datetime.now(dt.UTC) if moment.tzinfo is None: moment = moment.replace(tzinfo=dt.UTC) timestamp_ms = int(moment.timestamp() * 1000) return _encode_time(timestamp_ms) + _encode_random(_RANDOM_LEN)