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.
I 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:
$ npx epochlens 1718750000
input 1718750000 (unix seconds)
unix s 1718750000
unix ms 1718750000000
unix µs 1718750000000000
unix ns 1718750000000000000
iso utc 2024-06-18T22:33:20Z
iso local 2024-06-19T06:33:20+08:00
relative 2 minutes ago
rfc 2822 Tue, 18 Jun 2024 22:33:20 +0000
It 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:
epochlens 2024-06-18T22:33:20Z # any ISO 8601 / RFC 2822 date → every epoch precision
epochlens now # the current moment, all forms
echo 1718750000 | epochlens # reads stdin
pip install epochlens gets you the exact same tool in Python — the two builds print byte-for-byte identical output.
The fun part: making two languages agree to the byte
I 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:
-
toISOString()vsisoformat(): Node gives...20.000Z(always 3 fractional digits, literalZ); Python gives...20+00:00(no fraction when zero, 6 digits otherwise,+00:00notZ). Three mismatches in one field. -
Parsing:
Date.parse("2024-06-18 12:00")is lenient and reads it as local time; Python'sfromisoformatreads it as naive;"June 18 2024"parses in Node and throws in Python. -
Rounding:
Math.round(2.5)is3; Python'sround(2.5)is2(banker's rounding). -
Negative floor division (pre-1970 timestamps): JS truncates toward zero, Python floors toward −∞, so
-3 % 1000disagrees. -
Nanoseconds: a 19-digit ns value blows past
Number.MAX_SAFE_INTEGER, so Node needsBigIntwhere Python's ints just work. -
Sub-minute timezones: for pre-1900 dates,
getTimezoneOffset()(minutes) andtm_gmtoff(seconds) literally disagree about the local offset.
The 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.
What it deliberately doesn't do
-
No named
--tz America/New_Yorkyet. Python'szoneinforeads an OS tz database that Windows doesn't ship, and pulling intzdatawould break the zero-dependency promise. You get UTC, your local time, and a fixed--offset ±HH:MM(pure arithmetic, portable everywhere). -
No natural-language input (
"3 days ago"). Relative time is output only. -
Years are clamped to 0001–9999 (anything else is rejected rather than silently producing
+275760on one platform and crashing on the other).
Install
npx epochlens 1718750000 # Node, zero deps
pip install epochlens # Python, zero deps, identical output
MIT licensed, both builds open source:
- npm: https://www.npmjs.com/package/epochlens
- PyPI: https://pypi.org/project/epochlens/
- GitHub: https://github.com/jjdoor/epochlens (Node) · https://github.com/jjdoor/epochlens-py (Python)
What'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?
Top comments (0)