Source code for ursa.time

"""Canonical timestamp helpers for Ursa.

Absolute timestamps in the catalog are ``int64`` nanoseconds since the Unix
epoch (1970-01-01T00:00:00 UTC). This is the native unit for ``pa.timestamp(
"ns", tz="UTC")``, ``numpy.datetime64[ns]``, and ``pandas.Timestamp``, and
int64 covers 1677-09-21 through 2262-04-11.

See the Constellation Research Stack architecture plan ยง6.12 for the
cross-package convention.
"""

from __future__ import annotations

import time
from datetime import UTC, datetime

__all__ = [
    "INT64_MAX",
    "INT64_MIN",
    "datetime_to_ns",
    "now_ns",
    "ns_to_datetime",
]

INT64_MIN = -(2**63)
INT64_MAX = 2**63 - 1


[docs] def now_ns() -> int: """Current wall-clock time as int64 nanoseconds since the Unix epoch.""" return time.time_ns()
[docs] def ns_to_datetime(ns: int) -> datetime: """Convert int64 ns since Unix epoch to an aware UTC ``datetime``. Lossy past microsecond precision: Python's ``datetime`` cannot represent sub-microsecond values, so the final 3 digits of ``ns`` are truncated. For the ns-exact view, work with the integer directly. """ seconds, remainder_ns = divmod(ns, 1_000_000_000) # datetime takes microseconds; integer-divide off the last 3 ns digits. microseconds = remainder_ns // 1_000 return datetime.fromtimestamp(seconds, tz=UTC).replace(microsecond=microseconds)
_EPOCH = datetime(1970, 1, 1, tzinfo=UTC)
[docs] def datetime_to_ns(dt: datetime) -> int: """Convert an aware ``datetime`` to int64 ns since Unix epoch. Naive datetimes are rejected. Any aware timezone is normalized to UTC before the multiply. Pre-1970 datetimes produce negative ns values. """ if dt.tzinfo is None: raise ValueError(f"datetime must be timezone-aware to convert to ns; got naive {dt!r}") # ``timedelta`` arithmetic floors correctly for negative deltas (unlike # ``int(timestamp())`` which truncates toward zero). delta = dt.astimezone(UTC) - _EPOCH return (delta.days * 86_400 + delta.seconds) * 1_000_000_000 + delta.microseconds * 1_000