Guides

Timestamp to ISO 8601 Converter (Seconds, Millis, Nanos)

Why this page

You often get timestamps in seconds (10 digits), milliseconds (13), microseconds (16), or nanoseconds (19). This page gives you one-stop rules, ready-to-ship snippets, and CTAs to the full converter so you can turn any epoch into ISO 8601 / RFC 3339 without guessing the precision.

Quick start (MVP flow)

  1. Paste the timestamp (we auto-detect length and precision).
  2. Choose timezone: UTC or custom offset (e.g., UTC+08:00).
  3. Get ISO 8601 / RFC 3339 output, then one-click copy.
  4. Need to embed? Copy a language snippet below.

Try the interactive tool: Timestamp → ISO Converter and Format Builder.

Input rules and validation

  • Length detection: 10=seconds, 13=ms, 16=µs, 19=ns.
  • Allowed chars: digits only; reject whitespace and letters.
  • Range: must be within platform-safe bounds (check 32-bit vs 64-bit; see FAQ).
  • Timezone: default UTC; custom offsets like +08:00, -05:30.
  • DST: the output uses offset-aware ISO 8601; UTC avoids DST surprises.

Code snippets

JavaScript / Node.js

const detectPrecision = (raw) => {
  const len = raw.length;
  if (len === 10) return { value: Number(raw) * 1000, unit: "ms" };
  if (len === 13) return { value: Number(raw), unit: "ms" };
  if (len === 16) return { value: Number(raw) / 1000, unit: "ms" }; // µs → ms
  if (len === 19) return { value: Number(raw) / 1_000_000, unit: "ms" }; // ns → ms
  throw new Error("Invalid length");
};

const toIso = (raw, tz = "UTC") => {
  const { value } = detectPrecision(raw);
  // If you need a fixed offset, use luxon or temporal; here we show UTC
  return new Date(value).toISOString(); // RFC 3339 compliant
};

console.log(toIso("1704067200"));      // 10 digits -> seconds
console.log(toIso("1704067200000"));   // 13 digits -> milliseconds

Python

from datetime import datetime, timezone, timedelta

def to_iso(raw: str, offset_minutes: int = 0) -> str:
    length = len(raw)
    if length == 10:
        ts = int(raw)
    elif length == 13:
        ts = int(raw) / 1000
    elif length == 16:
        ts = int(raw) / 1_000_000
    elif length == 19:
        ts = int(raw) / 1_000_000_000
    else:
        raise ValueError("Invalid length")

    tz = timezone(timedelta(minutes=offset_minutes))
    return datetime.fromtimestamp(ts, tz=tz).isoformat()

print(to_iso("1704067200", 0))     # UTC
print(to_iso("1704067200000", 480)) # UTC+08:00

Go

package main

import (
	"fmt"
	"time"
)

func parseEpoch(raw string) (time.Time, error) {
	switch len(raw) {
	case 10:
		sec, _ := time.ParseInt(raw, 10, 64)
		return time.Unix(sec, 0).UTC(), nil
	case 13:
		ms, _ := time.ParseInt(raw, 10, 64)
		return time.Unix(0, ms*int64(time.Millisecond)).UTC(), nil
	case 16:
		us, _ := time.ParseInt(raw, 10, 64)
		return time.Unix(0, us*int64(time.Microsecond)).UTC(), nil
	case 19:
		ns, _ := time.ParseInt(raw, 10, 64)
		return time.Unix(0, ns).UTC(), nil
	default:
		return time.Time{}, fmt.Errorf("invalid length")
	}
}

func main() {
	t, err := parseEpoch("1704067200000")
	if err != nil {
		panic(err)
	}
	fmt.Println(t.Format(time.RFC3339))
}

Common pitfalls

  • Wrong precision: 13-digit JS timestamps treated as seconds = huge future date.
  • Missing offset: local parsing without specifying timezone yields machine-local time.
  • DST edges: if you must show local time, pick a real IANA zone (e.g., America/New_York) rather than a fixed offset.
  • Overflow: 32-bit systems will break after 2038 for second-level timestamps; prefer 64-bit.

FAQ

  • Q: Seconds vs milliseconds—how do I tell?
    A: Check digit length; if uncertain, run both and see which is in a plausible date window.
  • Q: Does ISO 8601 require Z?
    A: Z means UTC. Any offset like +08:00 is also valid ISO 8601 / RFC 3339.
  • Q: Can I batch convert?
    A: Use Batch Timestamp Converter.

Related tools and guides