tl;dr
Your Question is confusing, but you seem to claim the number 3_600_000L represents a count of milliseconds since the epoch reference of first moment of 1970 in UTC, 1970-01-01T00:00Z.
So parse as an Instant
.
Instant // Represent a moment in UTC, an offset of zero hours-minutes-seconds.
.ofEpochMilli( 3_600_000L ) // Parse a count of milliseconds since 1970-01-01T00:00Z. Returns a `Instant` object.
.toString() // Generate text representing this value, using standard ISO 8601 format.
The result is 1 AM on the first day of 1970 as seen in UTC. The Z
on the end means UTC.
1970-01-01T01:00:00Z
Get the date portion, as seen in UTC.
Instant // Represent a moment in UTC, an offset of zero hours-minutes-seconds.
.ofEpochMilli( 3_600_000L ) // Parse a count of milliseconds since 1970-01-01T00:00Z.
.atOffset( // Convert from `Instant` (always in UTC, an offset of zero) to `OffsetDateTime` which can have any offset.
ZoneOffset.UTC // A constant representing an offset of zero hours-minutes-seconds, that is, UTC itself.
) // Returns a `OffsetDateTime` object.
.toLocalDate() // Extract the date portion, without the time-of-day and without the offset-from-UTC.
.toString() // Generate text representing this value, using standard ISO 8601 format.
1970-01-01
Adjust that moment from UTC to the time zone Europe/Berlin
.
Instant // Represent a moment in UTC, an offset of zero hours-minutes-seconds.
.ofEpochMilli( 3_600_000L ) // Parse a count of milliseconds since 1970-01-01T00:00Z.
.atZone( // Convert from UTC to a particular time zone.
ZoneId.of( "Europe/Berlin" ) // A time zone is a history of the past, present, and future changes to the offset-from-UTC used by the people of a particular region.
) // Returns a `ZonedDateTime` object.
.toString() // Generate text representing this value, using standard ISO 8601 format wisely extended to append the name of the time zone in square brackets.
1970-01-01T02:00+01:00[Europe/Berlin]
Notice how that result has a different time-of-day, 2 AM in Berlin area rather than the 1 AM we saw in UTC. The Europe/Berlin
time zone was running an hour ahead of UTC at that moment then, so an hour ahead of 1 AM is 2 AM — same moment, same point on the timeline, different wall-clock time.
Get the date-only portion from that moment as seen in Europe/Berlin
.
Instant // Represent a moment in UTC, an offset of zero hours-minutes-seconds.
.ofEpochMilli( 3_600_000L ) // Parse a count of milliseconds since 1970-01-01T00:00Z.
.atZone( // Convert from UTC to a particular time zone.
ZoneId.of( "Europe/Berlin" ) // A time zone is a history of the past, present, and future changes to the offset-from-UTC used by the people of a particular region.
) // Returns a `ZonedDateTime ` object.
.toLocalDate() // Extract the date only, without the time-of-day and without the time zone. Returns a `LocalDate` object.
.toString() // Generate text representing this value, using standard ISO 8601.
1970-01-01
In this case, the date in Berlin area is the same as in UTC. But in other cases the date may vary. For example, 9 PM (21:00) on the 23rd of January in UTC is simultaneously “tomorrow” the 24th in Tokyo Japan.
java.time
Apparently, you use the term “Unix timestamp” to mean a count of milliseconds since first moment of 1970 UTC, 1970-01-01T00:00Z.
Parse that number into an Instant
object. The Instant
class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant instant = Instant.ofEpochMilli( 3_600_000L ) ;
instant.toString(): 1970-01-01T01:00:00Z
So very simple: An Instant
is always in UTC, always a moment, a point on the timeline.
when a timestamp was supposed to be just a date (without time).
For this, use the LocalDate
class. The LocalDate
class represents a date-only value without time-of-day and without time zone.
A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.
If no time zone is specified, the JVM implicitly applies its current default time zone. That default may change at any moment, so your results may vary. Better to specify your desired/expected time zone explicitly as an argument.
Specify a proper time zone name in the format of continent/region
, such as America/Montreal
, Africa/Casablanca
, or Pacific/Auckland
. Never use the 3-4 letter abbreviation such as EST
or IST
as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
Adjust your UTC value (Instant
) to another time zone by applying a ZoneId
to generate a ZonedDateTime
.
ZonedDateTime zdt = instant.atZone( z ) ;
From there we can extract the date-only portion as a LocalDate
object.
LocalDate ld = zdt.toLocalDate() ;
If you want the first moment of the day on that date, you must specify the context of a time zone. For any given moment, the date varies around the globe by time zone. When a new day dawns in India, it is still “yesterday” in France.
Always let java.time determine the first moment of the day. Do not assume 00:00. In some zones on some dates, the day may start at another time such as 01:00 because of anomalies such as Daylight Saving Time (DST).
ZonedDateTime zdtStartOfDay = ld.atStartOfDay( z ) ;
If you want to see that same moment as UTC, simply extract a Instant
.
Instant instant = zdtStartOfDay.toInstant() ;
The java.time classes also have a LocalDateTime
class. Understand that this class LocalDateTime
does not represent a moment! It does not represent a point on the timeline. It has no real meaning until you place it in the context of a time zone. This class is only used for two meanings:
- The zone/offset is unknown (a bad situation).
- Every/any zone/offset is intended. For example, "Christmas starts at 00:00 on December 25, 2018“, which means different moments in different places. The first Christmas happens in Kiribati. Then successive Christmases start after each successive midnight moving westward through Asia, then India, and onwards to Europe/Africa, and eventually the Americas. So it takes at least 26 hours for Santa to deliver all the presents.
Hopefully you can see this work is not at all as confusing once you understand the core concepts and use the excellent well-designed java.time classes.

About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date
, Calendar
, & SimpleDateFormat
.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.*
classes.
Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval
, YearWeek
, YearQuarter
, and more.