Timestamp converter · Guide
Convert a Timestamp to an ISO Date: Seconds, Milliseconds and Time Zones
Converting an epoch timestamp to an ISO date is simple once two facts are known: the timestamp's unit and the time zone the output should represent. The common failures are silent—a seconds value treated as milliseconds lands in January 1970, local-time formatting shifts the clock, and dropping an offset changes an instant into an ambiguous wall time. Keep the value numeric, identify its unit, convert the instant, and emit an ISO 8601 string with `Z` or an explicit offset.
Know what an ISO timestamp represents
A Unix timestamp counts elapsed seconds from 1970-01-01T00:00:00Z; common APIs also use milliseconds, microseconds or nanoseconds. It identifies an instant independent of location. An ISO 8601 date-time expresses that instant as calendar fields plus a zone designator.
1704067200 seconds
1704067200000 milliseconds
2024-01-01T00:00:00Z
2024-01-01T01:00:00+01:00
# Both ISO strings identify the same instant.Z means UTC, sometimes called Zulu time. An offset such as +01:00 states that the displayed local clock is one hour ahead of UTC. A string without either, such as 2024-01-01T00:00:00, is only a local wall time; consumers must guess which zone applies.
ISO 8601 covers dates, times, durations and several representations. For API interchange, the familiar extended calendar form compatible with RFC 3339—date, T, time and a numeric offset—is the least ambiguous choice.
Seconds versus milliseconds is the first check
Current epoch values are roughly ten digits in seconds and thirteen in milliseconds. Digit count is a useful diagnostic for contemporary dates, not a permanent type system: older dates, far-future dates, negatives and leading zeroes break the shortcut. Prefer an API contract or schema that names the unit.
// JavaScript Date expects milliseconds.
new Date(1704067200).toISOString();
// '1970-01-20T17:21:07.200Z' <-- seconds used as milliseconds
new Date(1704067200 * 1000).toISOString();
// '2024-01-01T00:00:00.000Z'A seconds-as-milliseconds error produces a plausible-looking 1970 date rather than an exception. The reverse usually produces a year outside the supported range. Sanity-check the result against the domain: a newly created order should not predate the web.
Do not infer solely from magnitude when a system stores historical or scientific dates. Carry the unit in the field name—created_at_ms—or use an ISO string at the boundary.
Convert to ISO 8601 in common languages
JavaScript's Date stores milliseconds since the epoch and toISOString() always emits UTC with Z. Python can construct an aware UTC datetime directly from seconds. PHP's @timestamp syntax also starts in UTC, after which an explicit formatter keeps the offset visible.
const seconds = 1704067200;
const iso = new Date(seconds * 1000).toISOString();
console.log(iso); // 2024-01-01T00:00:00.000Zfrom datetime import datetime, timezone
seconds = 1704067200
iso = datetime.fromtimestamp(seconds, tz=timezone.utc).isoformat()
print(iso) # 2024-01-01T00:00:00+00:00$seconds = 1704067200;
$date = new DateTimeImmutable('@' . $seconds);
echo $date->format('Y-m-d\TH:i:s.v\Z');
// 2024-01-01T00:00:00.000ZThe differing fractional parts do not represent different instants. JavaScript prints milliseconds, Python omits a zero fraction by default, and the PHP format above requests milliseconds explicitly. Choose a stable precision for your API so string comparisons and cache keys do not change between producers.
Preserve UTC and offsets
A timestamp has no time zone to preserve; it is already an absolute instant. The zone matters only when rendering calendar fields. Convert to UTC for storage and interchange, and convert to an IANA zone such as Europe/Paris only for display or a business rule tied to local civil time.
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
instant = datetime.fromtimestamp(1711848600, tz=timezone.utc)
print(instant.isoformat())
print(instant.astimezone(ZoneInfo('Europe/Paris')).isoformat())
# Offset is calculated for that date, including daylight-saving rules.Do not add a fixed number of hours to obtain local time. Daylight-saving transitions and historical rules mean a location is not a permanent offset. An offset records one relationship at one instant; an IANA zone supplies the rules needed to schedule future local times.
If input arrives as an ISO timestamp with an offset, parse it as aware time and convert the resulting instant. Removing +02:00 before parsing changes the instant by two hours rather than merely changing its display.
Fractions, negative timestamps and numeric precision
Milliseconds often arrive as an integer, while seconds may include a fraction such as 1704067200.125. Split or convert with care: binary floating-point cannot represent every decimal fraction exactly. For logs this is usually harmless; for ordering financial events or generating signatures, retain the integer unit supplied by the producer.
const milliseconds = 1704067200123n;
const wholeSeconds = milliseconds / 1000n;
const remainderMs = milliseconds % 1000n;
// 1704067200 seconds and 123 milliseconds, with no float roundingJavaScript numbers exactly represent integer millisecond timestamps for ordinary application dates, but nanosecond epoch values already exceed the safe-integer limit. Parse those as BigInt or text and use a library whose precision matches the input.
Negative timestamps represent instants before the epoch. Some platform date APIs support them and some legacy environments do not. Test the actual runtime if historical dates matter, especially on systems whose native time representation has a narrower range.
Parsing ISO back into a timestamp
Round trips are safe only when the ISO input includes a zone. JavaScript parses a trailing Z or explicit offset as an instant; Date.parse() returns milliseconds. Python's aware datetime can return fractional seconds. Never append Z to a local timestamp unless you know its fields already express UTC.
const iso = '2024-01-01T01:00:00+01:00';
const milliseconds = Date.parse(iso);
console.log(milliseconds); // 1704067200000
console.log(new Date(milliseconds).toISOString());
// 2024-01-01T00:00:00.000ZDate-only input introduces another semantic question. 2024-01-01 describes a calendar date, not necessarily midnight UTC. If the business value is a birthday or billing day, keep it as a date rather than manufacturing an instant that may display as the previous day in another zone.
Reject impossible or partially parsed dates. A lenient parser that turns an invalid day into the following month produces silent corruption. Validate the entire input against the format your API promises.
A reliable conversion checklist
Confirm whether the input is seconds, milliseconds, microseconds or nanoseconds. Parse it without losing integer precision. Construct an instant in UTC, then format it with Z or an explicit numeric offset. Finally, sanity-check the year and round-trip the output when correctness is important.
input: 1704067200000
unit: milliseconds (from API contract)
instant: epoch + 1704067200 seconds
output: 2024-01-01T00:00:00.000Z
check: Date.parse(output) === inputAt API boundaries, document both type and unit. integer, Unix milliseconds is actionable; timestamp is not. If clients do not need arithmetic, an ISO string is often clearer and avoids unit guessing, although you should still specify whether fractional seconds are required.
For user-facing display, keep the ISO UTC value as the source and format it in the user's chosen zone at the edge. Storing preformatted local strings loses the information required to handle daylight-saving changes or move the record between locales.
Frequently asked questions
How do I convert a Unix timestamp to an ISO date?
Determine the unit first, construct the corresponding instant, and format it in UTC. In JavaScript, multiply epoch seconds by 1,000 before passing them to new Date(), then call toISOString().
Is an ISO timestamp in seconds or milliseconds?
An ISO timestamp is a formatted date-time string, so it has neither epoch unit. Unix timestamps are commonly seconds or milliseconds; the API contract must say which. Fractional seconds in an ISO string represent precision, not an epoch unit.
Why does my converted timestamp show a date in 1970?
You probably passed epoch seconds to an API that expects milliseconds. JavaScript Date interprets numeric input as milliseconds, so multiply a seconds value by 1,000 before conversion.
What does Z mean in an ISO date?
Z means the time is expressed in UTC, with an offset of zero. It makes the string an unambiguous instant; without Z or another offset, the value is a local wall time whose zone must be supplied separately.
Should timestamps be stored in UTC?
Store event instants in UTC or as an epoch value and convert them for display. Future appointments tied to a local clock also need the IANA zone, because a fixed offset cannot encode future daylight-saving rules.
Can JavaScript safely handle nanosecond timestamps?
Not as ordinary numbers: current epoch nanoseconds exceed JavaScript's safe-integer range. Keep them as BigInt or decimal text and use a conversion library that preserves nanosecond precision.
Ready to try it?
Open the free browser-based Timestamp converter and apply what you just read — no sign-up, runs locally.
Open the Timestamp converter tool