Timestamp Tools Compared: CLI, Browser and Code

2026-03-27 · SPUNK13 · spunk.bet

Every timestamp bug I have ever debugged came down to one of three things: seconds confused with milliseconds, a naive datetime that silently assumed local time, or a formatter that emitted a string nobody could parse back. The tool you pick decides which of those you are exposed to. Here is what each option actually does.

Command Line: date and gdate

On GNU coreutils, converting an epoch to a readable date is one flag:

date -d @1767225600 -u +"%Y-%m-%dT%H:%M:%SZ"
# 2026-01-01T00:00:00Z

date -u +%s          # current epoch seconds
date -u +%s%3N       # current epoch milliseconds

macOS ships BSD date, where the syntax is different and %N does not exist at all: use date -r 1767225600, or install coreutils and call gdate. Scripts that work on your Linux CI and fail on a developer laptop are almost always hitting this. Always pass -u unless you specifically want local time; otherwise the machine's TZ setting silently changes your output.

Python: The Only Two Calls You Need

from datetime import datetime, timezone
datetime.fromtimestamp(1767225600, tz=timezone.utc)
datetime.now(timezone.utc).timestamp()

datetime.utcnow() is deprecated in Python 3.12 and for good reason: it returned a naive object that looked like UTC but compared incorrectly against aware objects. If you inherit code using it, replacing it with datetime.now(timezone.utc) is usually a one-line fix that removes a whole class of off-by-hours bugs.

JavaScript: Milliseconds, Always

Date.now() returns milliseconds, so any epoch you receive from a Unix-based API needs * 1000 before it goes into new Date(). The quick sanity check: a seconds-based epoch for the current era is 10 digits, a milliseconds one is 13. If you see a date in 1970, you passed seconds where milliseconds were expected. For anything involving timezone conversion, use the built-in Intl.DateTimeFormat with a timeZone option rather than pulling in a 70 kB library.

jq for Log Pipelines

jq '.ts |= todate' events.json
jq --arg t "2026-01-01T00:00:00Z" 'select(.ts > ($t|fromdate))'

todate and fromdate work in seconds and always UTC, which makes jq the least surprising option for filtering NDJSON logs. It has no millisecond helper, so divide by 1000 first.

Browser Converters

Web converters are fine for a one-off sanity check and terrible for anything repeated. They cannot be scripted, they usually guess your local timezone from the browser, and pasting production identifiers into a third-party page is a habit worth not forming. Use one to confirm a value; use the CLI when you need it twice.

The Precision Question

Postgres timestamptz stores microseconds. JavaScript's Date holds milliseconds. Go's time.Time and Rust's SystemTime hold nanoseconds. A round trip through JSON and JavaScript will therefore quietly truncate a Postgres timestamp, which is enough to break cursor-based pagination that uses WHERE created_at > $1. If you paginate on time, either store an explicit integer microsecond column or add a tiebreaker ID to the sort key.

Storage Rule That Prevents Most Bugs

Store UTC as an integer epoch or a timestamptz, never as a formatted local string. Convert to the user's timezone at the very edge, in the rendering layer, using an IANA zone name such as America/Chicago rather than a UTC offset. Offsets change twice a year; zone names do not.

Explore More

Free tools, guides, and resources.

Visit spunk.bet
400+ ToolsCasinoMemesAstrologyScam DB