🚧 WIP
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user