East coast of North America versus UTC
Your GregorianCalendar
carries a time zone. But the definition of your epoch is UTC. The first moment of 1970 on east coast of North America comes five hours later than the first moment of 1970 in UTC. So you are comparing apples to oranges.
Let's take another look, but use the modern java.time classes.
Instant instantEpochConstant = Instant.EPOCH ; // Expect 1970-01-01T00:00:00Z.
instantEpochConstant.toString(): 1970-01-01T00:00:00Z
Instant instantZeroMillis = Instant.ofEpochMilli( 0L ) ; // Expect 1970-01-01T00:00:00Z.
instantZeroMillis.toString(): 1970-01-01T00:00:00Z
long millisSinceEpoch = Instant.parse( "1970-01-01T00:00:00Z" ).toEpochMilli() ; // Expect zero.
millisSinceEpoch.toString(): 0
In contrast, guessing your time zone is America/New_York
, the first moment of 1970 in that region comes 5 hours (18,000,000 milliseconds) later than first moment of 1970 in UTC.
ZonedDateTime zdt = ZonedDateTime.of(
LocalDate.of( 1970 , Month.JANUARY , 1 ) ,
LocalTime.MIN ,
ZoneId.of( "America/New_York" )
);
Instant instantFromNewYork = zdt.toInstant() ;
long epochMillisNewYork = instantFromNewYork.toEpochMilli() ;
zdt.toString(): 1970-01-01T00:00-05:00[America/New_York]
instantFromNewYork.toString(): 1970-01-01T05:00:00Z
epochMillisNewYork: 18000000
5 * 60 * 60 * 1000 = 18,000,000 = five hours * 60 minutes/hour * 60 seconds/minute * 1,000 milliseconds/second
java.time
The GregorianCalendar
class is a flawed, poorly-designed, troublesome class. Avoid it. It is now replaced by java.time.ZonedDateTime
.
Instant
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.now() ;
instant.toString(): 2018-02-06T02:59:56.156787Z
ZonedDateTime
You can adjust from UTC into a particular time zone. You will end up with the same moment, the same point on the timeline, but a different wall-clock time.
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 pseudo-zones such as EST
or IST
as they are not true time zones, not standardized, and not even unique(!). No such thing as "Eastern time", use a time zone such as America/New_York
.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
Apply to the Instant
to get a ZonedDateTime
.
ZonedDateTime zdt = instant.atZone( z ) ;
zdt.toString(): 2018-02-06T03:59:56.156787+01:00[Africa/Tunis]
Count from epoch
Handling your date-time values as a count from epoch reference date is ill-advised. Such integer numbers are impossible for human eyes to read as date-times, so debugging and spotting invalid values is difficult or impossible.
But if you insist, you can extract a count of milliseconds from the epoch of first moment of 1970 in UTC, 1970-01-01T00:00Z.
Beware of data loss. Any microseconds or nanoseconds in your Instant
are ignored while generating this count in milliseconds.
long millisSinceEpoch = instant.toEpochMilli() ;
1517885996156
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.
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.