JavaScript timestamp to ISO 8601 converter
Convert a JavaScript timestamp into a precise ISO 8601 datetime without depending on the current time, a browser locale, or a server clock.
Run — free
Supply one integer containing milliseconds since the Unix epoch, and the converter returns the corresponding UTC date and time with three-digit millisecond precision and a trailing Z. The calculation is deterministic: the same numeric input always produces the same string. It also handles negative timestamps before 1970 and the full JavaScript time-value range, making it useful for debugging, fixtures, logs, migrations, and reproducible tests.
Enter milliseconds, not seconds
JavaScript timestamps normally count milliseconds from 1970-01-01T00:00:00.000Z, while many Unix tools and APIs count seconds. That factor of one thousand is the most common source of surprising conversions. Paste the numeric value exactly as JavaScript would return it, with no quotes, commas, unit suffix, or arithmetic expression. For example, a value copied from an event record, database field, performance trace, or application log can be submitted directly when that field is documented in milliseconds. The converter accepts integer values because an ISO string with three fractional digits represents whole milliseconds exactly. It rejects decimals, non-finite numbers, and numeric text instead of silently rounding or guessing. This strictness is helpful in automated pipelines: malformed input becomes a clear validation error rather than a plausible but incorrect date. If your source is measured in seconds, multiply it by one thousand before submitting it, provided the resulting integer remains inside the supported JavaScript range. The returned timestamp_ms field repeats the accepted number so that logs and test reports retain an explicit link between the source value and its formatted representation.
Understand the deterministic UTC result
The result is an ISO 8601 datetime in UTC. Its trailing Z means zero offset from UTC, not the local timezone of the person or machine making the request. Hours, minutes, seconds, and milliseconds are therefore stable across regions, daylight-saving transitions, runtime settings, and deployment locations. Internally, the calculation does not read the clock and does not call the JavaScript Date object. It divides the timestamp into a whole UTC day and a nonnegative time within that day, then converts the day count with bounded Gregorian calendar arithmetic. This matters when you are creating golden fixtures or investigating a production incident: an identical input cannot drift because of locale parsing, timezone configuration, or the date when the conversion runs. Negative timestamps are handled with floor-based division, so the millisecond immediately before the Unix epoch correctly belongs to 1969-12-31 rather than being truncated toward the wrong day. Years from 0000 through 9999 use four digits. Dates outside that interval use the signed extended-year form used by JavaScript ISO strings, preserving a precise representation throughout the supported time-value domain.
Use the output safely in code and data workflows
Use the iso field wherever a canonical UTC label is more readable than an epoch number: structured logs, debugging consoles, audit exports, JSON fixtures, migration reports, monitoring annotations, or API responses. Keeping both the input number and output string is especially useful during unit changes, because reviewers can spot whether a value was treated as seconds or milliseconds. The conversion is formatting, not timezone localization. It does not infer where an event happened, apply a named timezone, account for a user preference, or turn the result into a regional display string. Perform those presentation steps separately when your product requires them, while retaining this UTC value as the transport and comparison format. The accepted interval matches the conventional JavaScript time-value boundary of plus or minus 8,640,000,000,000,000 milliseconds. Inputs outside it are rejected, as are NaN, Infinity, missing values, strings, and fractional milliseconds. No network request, random source, environment locale, or current-time lookup influences the answer. You can therefore call the endpoint for $0.002 in a build process or execute the browser version interactively and expect equivalent, reproducible output for every valid input.
What you can do with it
Read application logs
Turn an opaque millisecond field into a UTC instant that engineers can compare across services and regions.
Build stable test fixtures
Generate a canonical ISO value from a fixed timestamp without allowing the machine clock or timezone to affect a test.
Audit timestamp migrations
Keep the original millisecond number beside its ISO representation while checking database or API transformations.
FAQ
Does the input use seconds or milliseconds?
Milliseconds. JavaScript timestamps conventionally count milliseconds since the Unix epoch; second-based values must be multiplied by 1,000 first.
Does this use my local timezone?
No. The output is always UTC, indicated by the trailing Z, and is independent of browser or server timezone settings.
Can it convert dates before 1970?
Yes. Supply a negative millisecond timestamp and the converter will return the corresponding proleptic-Gregorian UTC datetime.
Why are fractional values rejected?
The contract represents exact whole milliseconds and emits exactly three fractional-second digits, so rejecting fractions prevents silent rounding.
What does it cost?
Each API conversion costs $0.002. The browser execution is available for interactive conversion without clock-dependent behavior.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/date/js-timestamp-to-iso \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"timestamp":1717245296789}'const res = await fetch("https://api.kit.forhosting.com/date/js-timestamp-to-iso", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"timestamp": 1717245296789
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/date/js-timestamp-to-iso",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"timestamp": 1717245296789
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/date/js-timestamp-to-iso", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"timestamp":1717245296789}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"timestamp":1717245296789}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/date/js-timestamp-to-iso", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"timestamp": 1717245296789
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "date.js_timestamp_to_iso",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |