{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreieowvrobuf46xgdeb36gprlxrnfxnl5bzesrawqpqsn62dzwhr2f4",
    "uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mommxlv5v4z2"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreiav7ar2fkqknhuhhqlr3oqfbiiitywvuzrs7fpv4az6j4x3jm36du"
    },
    "mimeType": "image/webp",
    "size": 113842
  },
  "path": "/_06a3df6b50aec966668fb/is-that-timestamp-in-seconds-or-milliseconds-i-built-a-zero-dep-cli-that-just-tells-you-both-3nh9",
  "publishedAt": "2026-06-19T05:29:05.000Z",
  "site": "https://dev.to",
  "tags": [
    "showdev",
    "cli",
    "productivity",
    "opensource",
    "proleptic-Gregorian civil-day algorithm",
    "https://www.npmjs.com/package/epochlens",
    "https://pypi.org/project/epochlens/",
    "https://github.com/jjdoor/epochlens",
    "https://github.com/jjdoor/epochlens-py"
  ],
  "textContent": "You find a timestamp in a log line: `1718750000123`. Is that seconds? Milliseconds? You reach for `date`... and on macOS it's `date -r`, on Linux it's `date -d @`, and _neither_ of them will tell you that you grabbed milliseconds and your \"date\" is now in the year 56435. So you give up and paste the number into the third epoch-converter website that Google hands you.\n\nI do this several times a week. So I built **epochlens** — one zero-dependency command that auto-detects the unit, works the same on every platform, and goes both directions:\n\n\n\n    $ npx epochlens 1718750000\n\n      input     1718750000  (unix seconds)\n\n      unix s    1718750000\n      unix ms   1718750000000\n      unix µs   1718750000000000\n      unix ns   1718750000000000000\n      iso utc   2024-06-18T22:33:20Z\n      iso local 2024-06-19T06:33:20+08:00\n      relative  2 minutes ago\n      rfc 2822  Tue, 18 Jun 2024 22:33:20 +0000\n\n\nIt guesses seconds vs millis vs micros vs nanos by magnitude and **echoes the guess** so you can catch it (`--unit ms` to override). It goes the other way too:\n\n\n\n    epochlens 2024-06-18T22:33:20Z   # any ISO 8601 / RFC 2822 date → every epoch precision\n    epochlens now                    # the current moment, all forms\n    echo 1718750000 | epochlens      # reads stdin\n\n\n`pip install epochlens` gets you the exact same tool in Python — the two builds print **byte-for-byte identical** output.\n\n##  The fun part: making two languages agree to the byte\n\nI wanted a Node build _and_ a Python build that produce identical output, because half of us live in `npx` and half in `pip`. That turned out to be the hard part — date handling is a minefield of disagreements, and not just across languages but within them:\n\n  * **`toISOString()` vs `isoformat()`**: Node gives `...20.000Z` (always 3 fractional digits, literal `Z`); Python gives `...20+00:00` (no fraction when zero, 6 digits otherwise, `+00:00` not `Z`). Three mismatches in one field.\n  * **Parsing** : `Date.parse(\"2024-06-18 12:00\")` is lenient and reads it as _local_ time; Python's `fromisoformat` reads it as naive; `\"June 18 2024\"` parses in Node and throws in Python.\n  * **Rounding** : `Math.round(2.5)` is `3`; Python's `round(2.5)` is `2` (banker's rounding).\n  * **Negative floor division** (pre-1970 timestamps): JS truncates toward zero, Python floors toward −∞, so `-3 % 1000` disagrees.\n  * **Nanoseconds** : a 19-digit ns value blows past `Number.MAX_SAFE_INTEGER`, so Node needs `BigInt` where Python's ints just work.\n  * **Sub-minute timezones** : for pre-1900 dates, `getTimezoneOffset()` (minutes) and `tm_gmtoff` (seconds) literally disagree about the local offset.\n\n\n\nThe fix was to delegate **nothing** to the runtime's date library. Every conversion is plain integer math over a proleptic-Gregorian civil-day algorithm, and every output field is hand-formatted. No `Date.parse`, no `toISOString`, no `fromisoformat`, no `strftime`. The reward: a property test that diffs the two builds over thousands of inputs — from year 1 to year 9999, every precision, every offset — and gets zero differences.\n\n##  What it deliberately doesn't do\n\n  * **No named`--tz America/New_York`** yet. Python's `zoneinfo` reads an OS tz database that Windows doesn't ship, and pulling in `tzdata` would break the zero-dependency promise. You get UTC, your local time, and a fixed `--offset ±HH:MM` (pure arithmetic, portable everywhere).\n  * **No natural-language input** (`\"3 days ago\"`). Relative time is output only.\n  * **Years are clamped to 0001–9999** (anything else is rejected rather than silently producing `+275760` on one platform and crashing on the other).\n\n\n\n##  Install\n\n\n    npx epochlens 1718750000     # Node, zero deps\n    pip install epochlens        # Python, zero deps, identical output\n\n\nMIT licensed, both builds open source:\n\n  * npm: https://www.npmjs.com/package/epochlens\n  * PyPI: https://pypi.org/project/epochlens/\n  * GitHub: https://github.com/jjdoor/epochlens (Node) · https://github.com/jjdoor/epochlens-py (Python)\n\n\n\nWhat's your most-hated timestamp footgun — the seconds/millis mixup, timezone math, or something worse? And does anyone actually remember `date -d @` vs `date -r` without looking it up?",
  "title": "Is that timestamp in seconds or milliseconds? I built a zero-dep CLI that just tells you — both directions."
}