What epoch milliseconds are
Epoch milliseconds are 13-digit Unix timestamps — the unit JavaScript Date, Java Instant, .NET DateTimeOffset, and most browser APIs use natively. The number counts thousandths of a second since 1970-01-01 00:00:00 UTC. JavaScript's Date constructor takes milliseconds directly; Java's System.currentTimeMillis() emits milliseconds; .NET's DateTimeOffset has dedicated FromUnixTimeMilliseconds. Every other major language can accept the value once you divide by 1000 (or by 1,000,000 for microsecond input).
- 10-digit number → Unix seconds (around 1.7×10^9 in 2026) — Python, PHP, Go, Linux, most APIs
- 13-digit number → Unix milliseconds (around 1.7×10^12 in 2026) — JavaScript, Java, .NET, MongoDB BSON Date
- 16-digit number → Unix microseconds (around 1.7×10^15 in 2026) — Postgres, BigQuery, ClickHouse
- 19-digit number → Unix nanoseconds (around 1.7×10^18 in 2026) — InfluxDB, OpenTelemetry
- Detection: count the digits before passing the number to any conversion function
13-digit timestamp
For modern dates, 13 digits almost always means milliseconds since the Unix epoch. That value can be passed directly to JavaScript Date, Java Instant.ofEpochMilli, and .NET FromUnixTimeMilliseconds.
Milliseconds to Unix seconds
When a tool asks for a Unix timestamp and documents seconds, divide milliseconds by 1000 and floor the result before converting.
1970 bug
A date near January 1970 usually means a 10-digit seconds value was sent to code that expected milliseconds. A far-future date usually means the reverse.
How to convert millis to date in JavaScript
JavaScript's Date constructor takes milliseconds directly — no division needed. The result is a Date object; format it with toISOString() for ISO 8601 UTC, or with Intl.DateTimeFormat for a timezone-aware string. The single most common JS timestamp bug is passing Unix seconds where milliseconds are expected — the result lands in 1970. Multiply by 1000 first if you have a 10-digit value.
- new Date(1700000000000).toISOString() // '2023-11-14T22:13:20.000Z' — ms input, no × 1000
- new Date(1700000000 * 1000).toISOString() // for a 10-digit seconds value
- new Date(Date.now()).toString() // current instant as a Date
- new Intl.DateTimeFormat('en-US', { timeZone: 'America/New_York' }).format(new Date(ms))
- Auto-detect helper: const toDate = ts => new Date(ts < 1e11 ? ts * 1000 : ts)
- Reverse: Math.floor(Date.now() / 1000) gives Unix seconds for a backend API
How to convert millis to date in Java (and on the JVM)
Java's java.time package replaced the legacy java.util.Date in Java 8 and is the canonical API. Instant.ofEpochMilli(long) takes a millisecond-precision long and returns a java.time.Instant — a moment in time without a zone. Pair it with .atZone(ZoneId.of("UTC")) or .atOffset(ZoneOffset.UTC) to format in a specific zone. System.currentTimeMillis() is the standard way to get the current instant as a long; Instant.now().toEpochMilli() returns the same value.
- Instant.ofEpochMilli(1700000000000L).toString() // '2023-11-14T22:13:20Z'
- Instant.ofEpochMilli(ms).atZone(ZoneId.of("America/New_York")) // zone-aware
- Instant.ofEpochMilli(ms).atOffset(ZoneOffset.UTC).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
- System.currentTimeMillis() // returns long ms — the canonical 'now' on the JVM
- Instant.now().toEpochMilli() // same as currentTimeMillis() but through java.time
- Legacy: new Date(ms) // works but mutable and timezone-aware in surprising ways
How to convert millis to date in Kotlin (server + Android)
On the JVM and modern Android (API 26+, Android 8+), Kotlin uses the same java.time API as Java. For older Android targets (API 21–25, still ~5% of devices in 2026), use the ThreeTenABP backport, or move to the kotlinx-datetime multiplatform library which works on JVM, Android, iOS, and JS. kotlinx-datetime is also the cleanest choice for Compose Multiplatform projects targeting more than one runtime.
- JVM / Android 26+: Instant.ofEpochMilli(ms).atZone(ZoneId.systemDefault())
- Older Android: org.threeten.bp.Instant.ofEpochMilli(ms) // ThreeTenABP backport
- kotlinx-datetime: Instant.fromEpochMilliseconds(ms)
- kotlinx-datetime + zone: instant.toLocalDateTime(TimeZone.of("America/New_York"))
- Coroutines: System.currentTimeMillis() works inside any suspend function on the JVM
- Compose: remember { Instant.ofEpochMilli(ms).toString() } // stable across recomposition
How to convert millis to date in C# / .NET
.NET 4.6+ ships DateTimeOffset.FromUnixTimeMilliseconds(long) and FromUnixTimeSeconds(long) as the dedicated converters. Both return a DateTimeOffset anchored at UTC — convert to a specific zone with .ToOffset(TimeSpan) or .ToLocalTime(). Pre-4.6 code should switch to these APIs rather than computing the offset by hand. .NET 6+ adds DateOnly and TimeOnly for date-only and time-only use cases.
- DateTimeOffset.FromUnixTimeMilliseconds(1700000000000L) // 2023-11-14T22:13:20+00:00
- DateTimeOffset.FromUnixTimeSeconds(1700000000L) // for a 10-digit seconds value
- DateTimeOffset.FromUnixTimeMilliseconds(ms).ToLocalTime() // server-local — explicit
- DateTimeOffset.FromUnixTimeMilliseconds(ms).ToOffset(TimeSpan.FromHours(-5)) // explicit offset
- Reverse: DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() // current ms
- Legacy: new DateTime(1970,1,1,0,0,0,DateTimeKind.Utc).AddMilliseconds(ms) // pre-.NET 4.6
How to convert millis to date in Python
Python's datetime.fromtimestamp expects seconds, so the canonical conversion for milliseconds is divide-then-call: datetime.fromtimestamp(ms / 1000, tz=timezone.utc). The function accepts fractional seconds, so the float division is precise. Always pass the tz argument — without it, Python interprets the result in the system's local timezone, producing different output on developer laptops versus production servers. Python 3.12 deprecated utcfromtimestamp because it returned a naive datetime that was easy to misuse.
- from datetime import datetime, timezone
- datetime.fromtimestamp(1700000000000 / 1000, tz=timezone.utc) # 2023-11-14 22:13:20+00:00
- datetime.fromtimestamp(ms / 1000, tz=ZoneInfo('America/New_York')) # zone-aware
- Reverse: int(datetime.now(timezone.utc).timestamp() * 1000) # current ms
- Avoid: datetime.utcfromtimestamp(ms / 1000) # deprecated in 3.12; returns naive
- Pandas: pd.Timestamp(ms, unit='ms', tz='UTC') # vectorized conversion
How to convert millis to date in Rust
Rust's standard library doesn't ship a calendar/timezone API — the ecosystem standard is the chrono crate, with the time crate as a smaller, std-only alternative for embedded targets. Both accept milliseconds, but chrono uses an Option return type to handle out-of-range values gracefully. For new code in 2026, chrono is still the most common choice; time::OffsetDateTime is the right pick when you want zero unsafe code and a smaller binary.
- chrono: DateTime::<Utc>::from_timestamp_millis(1700000000000).unwrap()
- chrono: DateTime::from_timestamp(secs, nanos).unwrap() // separate seconds + nanos
- chrono: Utc::now().timestamp_millis() // current ms as i64
- time crate: OffsetDateTime::from_unix_timestamp_nanos(ms as i128 * 1_000_000).unwrap()
- time crate: OffsetDateTime::now_utc().unix_timestamp() * 1000 // current ms
- From an ISO 8601 string: 'YYYY-MM-DDTHH:MM:SSZ'.parse::<DateTime<Utc>>().unwrap()
How to convert millis to date in Go
Go's standard library handles milliseconds explicitly through time.UnixMilli(int64), which returns a time.Time. The function was added in Go 1.17 — older code computes time.Unix(0, ms*1_000_000) by passing the nanosecond count. UnixMicro and UnixNano cover microsecond and nanosecond inputs respectively. Combine with .In(time.UTC) or time.LoadLocation for zone-aware display.
- time.UnixMilli(1700000000000) // Go 1.17+; returns a time.Time in local zone
- time.UnixMilli(ms).In(time.UTC) // force UTC display
- time.UnixMilli(ms).Format(time.RFC3339) // '2023-11-14T22:13:20Z' (when UTC)
- Pre-Go 1.17: time.Unix(0, ms*1_000_000) // nanoseconds path
- Reverse: time.Now().UnixMilli() // current ms as int64
- From string: time.Parse(time.RFC3339, '2023-11-14T22:13:20Z')
Millis to seconds — the divide-by-1000 case
Most server APIs, Unix tools, Linux commands, and SQL functions default to Unix seconds. When a JavaScript or Java client emits milliseconds, the boundary between the two systems is where the divide-by-1000 has to happen. The conversion is simple — math.floor(ms / 1000) in JavaScript, Math.floorDiv(ms, 1000) in Java, ms // 1000 in Python — but missing it produces dates 1000× too far in the future. Always do the division at the boundary, then never speak in the other unit inside that system.
- JavaScript: Math.floor(Date.now() / 1000) // current Unix seconds
- Java: System.currentTimeMillis() / 1000L // integer division on long
- Java preferred: Instant.now().getEpochSecond() // returns long seconds directly
- Python: int(time.time()) // already seconds; no division needed
- C#: DateTimeOffset.UtcNow.ToUnixTimeSeconds()
- Linux: date +%s // always seconds
- Detection: if your value is 13 digits and your target API expects 10, divide by 1000
Milliseconds to timestamp for APIs
If the API says Unix timestamp without mentioning milliseconds, assume seconds until the docs say otherwise.
Keep the unit at the boundary
Convert once where the JavaScript/Java side meets the server/SQL side, then use one unit inside that subsystem.
Long to date in Java and databases
Java's long type is 64-bit signed, so it comfortably holds Unix milliseconds (~292 million years in either direction from 1970). MongoDB BSON Date is also a signed 64-bit millisecond long under the hood — driver libraries map it to Date in JavaScript, datetime in Python, ISODate in the mongo shell, java.util.Date or java.time.Instant in Java. Storing milliseconds in a database BIGINT column gives you the same range without depending on driver-side conversions. The companion storage guide covers when BIGINT epoch is the right choice over a native datetime column.
- Java long range: ±9.2×10^18 — covers Unix milliseconds from year -292M to +292M
- MongoDB BSON Date: 8 bytes, signed 64-bit milliseconds since epoch — native type
- BIGINT in MySQL/Postgres: store milliseconds when the source system already produces them
- Postgres TIMESTAMPTZ stores microseconds internally — converts to/from epoch ms via EXTRACT
- Mongo driver round-trip: js Date → BSON Date → Java Date — milliseconds preserved exactly
Why milliseconds convert to strange dates
Three error modes account for almost every wrong-date bug in millisecond code: passing seconds to a function that expects milliseconds (date lands in 1970), passing milliseconds to a function that expects seconds (date lands in the year ~55,000), and reading the system timezone rather than UTC (date is correct but off by hours from what you expected). The 10 common timestamp bugs catalog covers each in detail with detection recipes; the same patterns appear in code that converts milliseconds for the first time.
- Date shows 1970 — you passed 10-digit seconds to a 13-digit-expecting function (e.g. new Date(1.7e9))
- Date is in the year 55,000 — you passed 13-digit milliseconds to a seconds-expecting function (e.g. datetime.fromtimestamp(1.7e12))
- Date is correct but off by hours — you forgot to specify UTC; the runtime used the system tz
- Date is one millennium too early — you forgot a × 1000 or ÷ 1000 multiplier
- Date round-trips through a string format and loses fractional precision — use ISO 8601 with .SSS suffix
Date shows 1970
Seconds were interpreted as milliseconds. Multiply the 10-digit value by 1000 before calling a millisecond API.
Date shows year 55,000
Milliseconds were interpreted as seconds. Divide the 13-digit value by 1000 before calling a seconds API.
Cross-language patterns for ms-to-date
The same conversion job in every common runtime, side by side. Each row maps a starting form to the canonical idiom for that language. Use this as a quick reference when porting code between stacks or when reviewing a PR that touches a millisecond timestamp at a system boundary.
- JavaScript: new Date(ms).toISOString()
- Java: Instant.ofEpochMilli(ms).toString()
- Kotlin: Instant.ofEpochMilli(ms).atZone(ZoneId.systemDefault()).toString()
- C#: DateTimeOffset.FromUnixTimeMilliseconds(ms).ToString("o")
- Python: datetime.fromtimestamp(ms / 1000, tz=timezone.utc).isoformat()
- Go: time.UnixMilli(ms).UTC().Format(time.RFC3339)
- Rust (chrono): DateTime::<Utc>::from_timestamp_millis(ms).unwrap().to_rfc3339()
- PHP: (new DateTime('@' . intval($ms / 1000)))->format('c')
- Ruby: Time.at(ms / 1000.0).utc.iso8601(3)
- Swift: Date(timeIntervalSince1970: Double(ms) / 1000.0) // formatted via ISO8601DateFormatter
FAQ
- Is epoch time in ms the same as Unix timestamp?
- Yes — both refer to the same instant, just in different units. Epoch milliseconds count thousandths of a second since 1970-01-01 UTC; Unix seconds count whole seconds. Divide ms by 1000 to get seconds, multiply seconds by 1000 to get ms.
- How many digits is an epoch millisecond timestamp?
- 13 digits for dates in the 21st century (around 1.7×10^12 in 2026). Unix seconds in the same range are 10 digits (around 1.7×10^9). If your number has 13 digits and you treat it as seconds, the resulting date lands around the year 55,000.
- How do I convert a 13-digit timestamp to a date?
- Treat it as milliseconds. JavaScript: new Date(ms).toISOString(). Java: Instant.ofEpochMilli(ms). Python: datetime.fromtimestamp(ms / 1000, tz=timezone.utc). The divide-by-1000 step is only for APIs that expect seconds.
- Does JavaScript use milliseconds or seconds?
- Milliseconds. Date.now(), new Date().getTime(), and performance.now() all return milliseconds. Most server APIs and Unix tools use seconds, so multiply by 1000 when passing JavaScript-side values down to them, and divide by 1000 in the other direction.
- How do I convert a Java long timestamp to a date?
- Use Instant.ofEpochMilli(long) for a millisecond-precision long, or Instant.ofEpochSecond(long) for a second-precision long. Both return a java.time.Instant — convert to a zoned representation with .atZone(ZoneId.of("UTC")) or any other IANA name.
- How do I convert millis to a Unix timestamp?
- Divide by 1000 — Unix timestamp typically means seconds when an API doesn't specify. In JavaScript: Math.floor(Date.now() / 1000). In Java: Instant.now().getEpochSecond(). In Python: int(time.time()).
- What does currentTimeMillis to timestamp mean?
- System.currentTimeMillis() is Java's call that returns Unix milliseconds. Converting that long to a 'timestamp' usually means turning it into a readable date — Instant.ofEpochMilli(...).toString() returns ISO 8601 UTC.
- How do I convert epoch milliseconds to a date in C# / .NET?
- Use DateTimeOffset.FromUnixTimeMilliseconds(long ms). It returns a DateTimeOffset anchored at UTC; call .LocalDateTime, .DateTime, or .ToOffset(TimeSpan) to display in a specific zone. For seconds input, use FromUnixTimeSeconds instead.
- How do I convert epoch milliseconds to a date in Kotlin?
- On the JVM and Android API 26+: Instant.ofEpochMilli(ms).atZone(ZoneId.systemDefault()) — same java.time API as Java. On older Android (API < 26), use the ThreeTenABP backport or kotlinx-datetime's Instant.fromEpochMilliseconds(ms).
- How do I convert epoch milliseconds to a date in Python?
- datetime.fromtimestamp(ms / 1000, tz=timezone.utc) — divide by 1000 first because Python's fromtimestamp expects seconds (it accepts fractional seconds, so the division is fine).
- How do I convert epoch milliseconds to a date in Rust?
- With the chrono crate: DateTime::from_timestamp_millis(ms) returns Option<DateTime<Utc>>. With the time crate: OffsetDateTime::from_unix_timestamp_nanos(ms as i128 * 1_000_000).