Search code examples
javasimpledateformatepoch

Why is Java epoch time off by 30 minutes when parsing via SimpleDateFormat


Somehow parsing date time strings and converting them to milliseconds after the epoch works differently in different environments. It would seem something is off with the timezone or something. In theory, this string should represent 0 seconds since the epoch: "1970-01-01T00:00:00Z"

In practice, on developer machines, it is mysteriously 30 minutes (18000000 ms).

    /**
     * This really ought to return 0, but locally it returns 30 minutes worth of milliseconds.
     * @return The milliseconds since the common epoch. Really ought to be zero, but isn't always.
     */
    public long determineMysteriousMachineTimeDelta()  {
        String strDateOfEpochStart = "1970-01-01T00:00:00Z";
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        Date dateStartOfEpoch = null;
        try {
            dateStartOfEpoch = format.parse(strDateOfEpochStart);

        } catch (ParseException e) {
            return -1;
        }
        return dateStartOfEpoch.getTime();
    }

Thanks!


Solution

  • tl;dr

    Instant.parse( "1970-01-01T00:00:00Z" )
    

    Z, never 'Z'

    Never put quote marks around the Z in a formatting pattern.

    That letter indicates an offset from UTC of zero hours-minutes-seconds. Pronounced “Zulu”.

    Your quote marks say “expect this text, but ignore it”. So your formatting pattern discards crucial information.

    Avoid legacy date-time classes

    Never use SimpleDateFormat, Calendar, Date classes. These are legacy classes, and terribly flawed. They were years ago supplanted by the modern java.time classes defined in JSR 310.

    java.time

    Your input string complies with the ISO 8601 standard.

    That standard is used by default in the java.time classes when parsing/generating text. So no need to specify a formatting pattern.

    To represent a moment as seen in UTC, use Instant class.

    Instant instant = Instant.parse( "1970-01-01T00:00:00Z" ) ;
    

    Count from epoch

    Interrogate for a count of milliseconds since the epoch reference of first moment of 1970 as seen in UTC (1970-01-01T00:00Z).

    So in your example, we expect a count of zero milliseconds.

    long millis = instant.toEpochMilli() ;
    

    See this code run at Ideone.com.

    By the way, beware of data loss here — an Instant resolves to nanoseconds, much finer than mere milliseconds.