Java
currentTimeMillis
date conversion
programming tutorial
Java date handling

How to convert currentTimeMillis to a date in Java?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

The modern way to convert System.currentTimeMillis() to a date in Java is through the java.time API introduced in Java 8. Use Instant.ofEpochMilli() to create an instant from the millisecond value, then convert to a ZonedDateTime or LocalDateTime for display and formatting. The legacy Date class still works but should be avoided in new code.

java
1long millis = System.currentTimeMillis();
2Instant instant = Instant.ofEpochMilli(millis);
3ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
4System.out.println(zdt);
5// Output: 2026-06-18T10:30:00.123-04:00[America/New_York]

What currentTimeMillis Returns

System.currentTimeMillis() returns a long representing the number of milliseconds elapsed since the Unix epoch (1970-01-01T00:00:00Z). This value is always UTC-based and timezone-independent. It is commonly used for timestamps, performance measurement, and cache expiration.

java
long now = System.currentTimeMillis();
System.out.println(now);
// Output: 1718700000123

The value itself has no timezone information. Converting it to a human-readable date requires choosing a timezone explicitly or using the system default.

Modern Approach: java.time (Java 8+)

Instant

Instant represents a point on the UTC timeline. It is the natural first step when converting from epoch milliseconds.

java
1import java.time.Instant;
2
3long millis = System.currentTimeMillis();
4Instant instant = Instant.ofEpochMilli(millis);
5System.out.println(instant);
6// Output: 2026-06-18T14:30:00.123Z

Instant always prints in UTC (the trailing Z stands for Zulu/UTC). This is useful for logging and storage because it removes timezone ambiguity.

ZonedDateTime

To display the timestamp in a specific timezone:

java
1import java.time.Instant;
2import java.time.ZoneId;
3import java.time.ZonedDateTime;
4
5long millis = System.currentTimeMillis();
6Instant instant = Instant.ofEpochMilli(millis);
7
8ZonedDateTime eastern = instant.atZone(ZoneId.of("America/New_York"));
9ZonedDateTime tokyo = instant.atZone(ZoneId.of("Asia/Tokyo"));
10
11System.out.println(eastern); // 2026-06-18T10:30:00.123-04:00[America/New_York]
12System.out.println(tokyo);   // 2026-06-18T23:30:00.123+09:00[Asia/Tokyo]

LocalDateTime

LocalDateTime drops the timezone information entirely. Use it when you need a date and time without timezone context (for example, a user-facing display where the timezone is handled elsewhere).

java
1import java.time.Instant;
2import java.time.LocalDateTime;
3import java.time.ZoneId;
4
5long millis = System.currentTimeMillis();
6LocalDateTime ldt = LocalDateTime.ofInstant(
7    Instant.ofEpochMilli(millis),
8    ZoneId.systemDefault()
9);
10System.out.println(ldt);
11// Output: 2026-06-18T10:30:00.123

DateTimeFormatter

For custom output formats:

java
1import java.time.Instant;
2import java.time.ZoneId;
3import java.time.ZonedDateTime;
4import java.time.format.DateTimeFormatter;
5
6long millis = System.currentTimeMillis();
7ZonedDateTime zdt = Instant.ofEpochMilli(millis).atZone(ZoneId.systemDefault());
8
9DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
10System.out.println(zdt.format(formatter));
11// Output: 2026-06-18 10:30:00 EDT

DateTimeFormatter is thread-safe and immutable. You can declare it as a static final field and reuse it safely across threads.

Legacy Approach: java.util.Date and SimpleDateFormat

These classes still work and appear frequently in older codebases.

Date

java
1import java.util.Date;
2
3long millis = System.currentTimeMillis();
4Date date = new Date(millis);
5System.out.println(date);
6// Output: Wed Jun 18 10:30:00 EDT 2026

SimpleDateFormat

java
1import java.text.SimpleDateFormat;
2import java.util.Date;
3
4long millis = System.currentTimeMillis();
5Date date = new Date(millis);
6
7SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
8System.out.println(sdf.format(date));
9// Output: 2026-06-18 10:30:00

API Comparison

ClassPackageThread-SafeTimezone HandlingJava Version
Instantjava.timeYesUTC only8+
ZonedDateTimejava.timeYesExplicit timezone8+
LocalDateTimejava.timeYesNo timezone8+
DateTimeFormatterjava.timeYesConfigurable8+
Datejava.utilNoSystem default1.0+
SimpleDateFormatjava.textNoConfigurable1.1+

The critical difference is thread safety. SimpleDateFormat is not thread-safe, meaning sharing a single instance across threads produces corrupt output or exceptions. DateTimeFormatter has no such issue.

Converting Between Legacy and Modern APIs

When working with libraries that still use java.util.Date, convert at the boundary:

java
1import java.time.Instant;
2import java.time.ZoneId;
3import java.time.ZonedDateTime;
4import java.util.Date;
5
6// Date -> Instant -> ZonedDateTime
7Date legacyDate = new Date(System.currentTimeMillis());
8Instant instant = legacyDate.toInstant();
9ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
10
11// ZonedDateTime -> Instant -> Date
12Date backToLegacy = Date.from(zdt.toInstant());

This pattern keeps your internal code on java.time while maintaining compatibility with older APIs.

Handling Timestamps from External Systems

When receiving epoch milliseconds from APIs, databases, or message queues, always clarify whether the value is in milliseconds or seconds. A common mistake is passing seconds to Instant.ofEpochMilli(), which produces a date in January 1970.

java
1// Correct: value is in milliseconds
2Instant fromMillis = Instant.ofEpochMilli(1718700000123L);
3
4// Correct: value is in seconds
5Instant fromSeconds = Instant.ofEpochSecond(1718700000L);
6
7// Wrong: passing seconds to ofEpochMilli
8Instant wrong = Instant.ofEpochMilli(1718700000L);
9// This is Jan 20, 1970, not June 2024

A quick check: if the value has 13 digits, it is likely milliseconds. If it has 10 digits, it is likely seconds.

Formatting Common Date Patterns

Here are the most frequently needed date format patterns with DateTimeFormatter:

java
1import java.time.Instant;
2import java.time.ZoneId;
3import java.time.ZonedDateTime;
4import java.time.format.DateTimeFormatter;
5
6long millis = System.currentTimeMillis();
7ZonedDateTime zdt = Instant.ofEpochMilli(millis).atZone(ZoneId.of("UTC"));
8
9// ISO 8601 (API responses, logs)
10zdt.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
11// 2026-06-18T14:30:00.123+00:00
12
13// Human-readable
14zdt.format(DateTimeFormatter.ofPattern("MMM dd, yyyy hh:mm a z"));
15// Jun 18, 2026 02:30 PM UTC
16
17// Date only
18zdt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
19// 2026-06-18
20
21// Compact timestamp for filenames
22zdt.format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
23// 20260618_143000

For ISO 8601 output, prefer the built-in DateTimeFormatter.ISO_INSTANT or ISO_OFFSET_DATE_TIME constants over custom patterns. They handle edge cases like leap seconds and timezone offset formatting correctly.

Common Pitfalls

Sharing a SimpleDateFormat instance across threads is the most common production bug related to date formatting. The formatter maintains internal mutable state, and concurrent access corrupts it silently. Use DateTimeFormatter or create a new SimpleDateFormat per thread.

Using LocalDateTime to store timestamps loses timezone information. If two servers in different timezones both store LocalDateTime, you cannot compare their timestamps correctly. Use Instant or ZonedDateTime for storage and interchange.

Confusing milliseconds with seconds when calling Instant.ofEpochMilli() versus Instant.ofEpochSecond() produces dates 50 years in the past.

Calling new Date() instead of new Date(millis) captures the current time, not the time represented by a specific millisecond value. This is obvious in isolation but causes bugs in refactored code where the millis variable is no longer passed through.

Ignoring timezone when formatting dates for users in different regions produces incorrect local times. Always use an explicit ZoneId rather than relying on the server's system default.

Mixing java.util.Date and java.time types without clear boundary conversion leads to code that is difficult to reason about. Establish a project convention: use java.time internally and convert to Date only at the edges where legacy APIs require it.

Summary

  • Use Instant.ofEpochMilli(millis) to convert currentTimeMillis to a point in time.
  • Use ZonedDateTime when you need timezone-aware display; use Instant for UTC storage.
  • Format output with DateTimeFormatter, which is thread-safe and reusable.
  • Avoid SimpleDateFormat in new code due to thread-safety issues.
  • Convert between Date and Instant at API boundaries using toInstant() and Date.from().
  • Verify whether external timestamps are in milliseconds or seconds before converting.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.