Skip to content

Timestamp Converter — Python source

Convert Unix epoch timestamps to human-readable dates and back. See seconds, milliseconds, ISO 8601, UTC, local and relative time at once.

This is the Python implementation — the same logic the interactive tool runs, in a shareable, citable form.

"""Unix epoch (seconds & milliseconds) <-> human forms: ISO 8601, UTC
string, local date string, and relative time ("3 hr ago" / "in 2 day").

Language: Python
CosmoDev polyglot showcase port of the ``timestamp`` tool.
Ported from src/tools/TimestampConverter.tsx — display source, part of
CosmoDev's polyglot tool pages.

Every form derives from a single epoch-seconds value via the standard
``datetime`` module. ``relative_time`` buckets ``|now - t|`` into
sec/min/hr/day/month/yr using the same 60s / 60m / 24h / 30d / 365d
thresholds and signs the phrase ("ago" / "in …"). ``now_ms`` is a
parameter so the function is deterministic and unit-testable; it defaults
to :func:`time.time` multiplied to milliseconds.
"""

from __future__ import annotations

import time
from datetime import datetime, timezone

_INVALID = "—"


def relative_time(ms: int, now_ms: int | None = None) -> str:
    """Return "3 hr ago" / "in 2 day" / "42 sec ago", mirroring the island.

    Buckets the absolute distance from *now_ms* into sec/min/hr/day/month/
    yr, rounds to the nearest whole unit, and prefixes "in " (future) or
    suffixes " ago" (past).
    """
    if now_ms is None:
        now_ms = int(time.time() * 1000)
    diff = ms - now_ms
    abs_s = abs(diff) / 1000.0

    if abs_s < 60:
        n, unit = round(abs_s), "sec"
    elif abs_s < 3600:
        n, unit = round(abs_s / 60), "min"
    elif abs_s < 86400:
        n, unit = round(abs_s / 3600), "hr"
    elif abs_s < 86400 * 30:
        n, unit = round(abs_s / 86400), "day"
    elif abs_s < 86400 * 365:
        n, unit = round(abs_s / (86400 * 30)), "month"
    else:
        n, unit = round(abs_s / (86400 * 365)), "yr"

    phrase = f"{n} {unit}"
    return f"{phrase} ago" if diff < 0 else f"in {phrase}"


def _iso8601(dt: datetime) -> str:
    # Date.toISOString() renders UTC with milliseconds and a "Z".
    ms = dt.microsecond // 1000
    return dt.strftime("%Y-%m-%dT%H:%M:%S.") + f"{ms:03d}Z"


def format_timestamp(secs: int, now_ms: int | None = None) -> dict[str, str]:
    """Render every form the tool exposes from epoch seconds.

    Matches the island's row table: epoch (s), epoch (ms), ISO 8601,
    UTC string, local string, and relative time. Invalid instants
    render an em dash, exactly like the live UI.
    """
    try:
        utc = datetime.fromtimestamp(secs, tz=timezone.utc)
    except (OverflowError, OSError, ValueError):
        dash = _INVALID
        return {
            "Epoch (s)": str(secs),
            "Epoch (ms)": str(secs * 1000),
            "ISO 8601": dash,
            "UTC": dash,
            "Local": dash,
            "Relative": dash,
        }
    # toUTCString(): "Wed, 14 Jun 2017 07:00:00 GMT" (C locale = English).
    utc_str = utc.strftime("%a, %d %b %Y %H:%M:%S GMT")
    local = datetime.fromtimestamp(secs).strftime("%Y-%m-%d %H:%M:%S")
    return {
        "Epoch (s)": str(secs),
        "Epoch (ms)": str(secs * 1000),
        "ISO 8601": _iso8601(utc),
        "UTC": utc_str,
        "Local": local,
        "Relative": relative_time(secs * 1000, now_ms),
    }


def from_iso(iso: str) -> int | None:
    """Parse an ISO 8601 / datetime-local string into epoch seconds.

    Mirrors the island's ``<input type="datetime-local">`` handler: parse
    the string in the host zone and floor to seconds. Returns ``None`` for
    unparseable input (the UI silently no-ops).
    """
    text = iso.replace("Z", "+00:00") if iso.endswith("Z") else iso
    try:
        dt = datetime.fromisoformat(text)
    except ValueError:
        # Fall back to the datetime-local "YYYY-MM-DDTHH:mm" shape.
        for fmt in ("%Y-%m-%dT%H:%M", "%Y-%m-%d %H:%M:%S", "%Y-%m-%d"):
            try:
                dt = datetime.strptime(iso, fmt)
            except ValueError:
                continue
            return int(dt.timestamp())
        return None
    return int(dt.timestamp())


if __name__ == "__main__":
    now_ms = int(time.time() * 1000)
    for key, value in format_timestamp(int(time.time()), now_ms).items():
        print(f"{key+':':<11} {value}")

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →