Richeek
Richeek

Reputation: 2220

UTC date parsing inconsistency in Java

Something weird is happening while parsing a UTC/GMT date. I set the date format as

"yyyy-MM-dd'T'HH:mm:ss'Z'"

where Z is for UTC. And I give following date string to parse:

String startTimestampString = "2013-10-02T00:00:00Z";

I hope to get same date as output but instead it shows

2013-10-01 17:00:00.0

Now sure from where this 7 hour lag coming from?

Code:

import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.TimeZone;

public class DateTest {

    public static void main(String[] args) throws ParseException {
       SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
       date.setTimeZone(TimeZone.getTimeZone("UTC"));
       System.out.println(TimeZone.getTimeZone("UTC").toString());

       String startTimestampString = "2013-10-02T00:00:00Z";
       long startTimestamp = date.parse(startTimestampString).getTime();
       System.out.println(String.format("Long %d and timestamp %s", startTimestamp, new Timestamp(startTimestamp).toString()));        
    }
}

Output:

sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
Long 1380672000000 and timestamp 2013-10-01 17:00:00.0    // ERROR timestamp should have been 2013-10-02 00:00:00.0

Upvotes: 0

Views: 597

Answers (1)

Basil Bourque
Basil Bourque

Reputation: 338785

java.util.Date Has No Time Zone

As the comments said, your problem is not understanding the confusing way in which java.util.Date works.

A Date object has no time zone, but seems to have one because its toString method applies your JVM's default time zone when generating the textual representation (the String being returned).

This poor design choice by the Java team has caused so much confusion, including countless similar Questions on StackOverflow.

The Date Is Not The String

A key idea here is that the String generated by the toString method is an entirely new object. This string is not the Date. The string is a particular representation of that moment in history as seen from you default time zone. The same moment in history appears as two different time-of-day values when seen from the Paris or Montréal or Kolkata time zones.

Avoid java.util.Date

Do not waste your time with java.util.Date and .Calendar and SimpleDateFormat. They are notoriously troublesome. Use Joda-Time or new java.time package in Java 8 (inspired by Joda-Time).

Joda-Time

Example code in Joda-Time 2.3. Your format is in the standard ISO 8601 format. Joda-Time uses ISO 8601 as its defaults, so no need for parsers/formatters in your case. Joda–Time automatically uses built-in formatters to parse ISO 8601 compliant strings.

String input = "2013-10-02T00:00:00Z";
DateTimeZone timeZoneParis = DateTimeZone.forID( "Europe/Paris" );
DateTime dateTimeParis = new DateTime( input, timeZoneParis );
DateTime dateTimeMontréal = dateTimeParis.withZone( DateTimeZone.forID( "America/Montreal" ) );
DateTime dateTimeIndia = dateTimeParis.withZone( DateTimeZone.forID( "Asia/Kolkata" ) );
DateTime dateTimeUtc = dateTimeParis.withZone( DateTimeZone.UTC );

DateTimeFormatter formatter = DateTimeFormat.forStyle( "FF" ).withLocale( Locale.CANADA_FRENCH );

Dump to console…

System.out.println( "dateTimeParis: " + dateTimeParis );
System.out.println( "dateTimeMontréal: " + dateTimeMontréal );
System.out.println( "dateTimeMontréal formatted: " + formatter.print( dateTimeMontréal ) );
System.out.println( "dateTimeIndia: " + dateTimeIndia );
System.out.println( "dateTimeUtc: " + dateTimeUtc );

When run…

dateTimeParis: 2013-10-02T02:00:00.000+02:00
dateTimeMontréal: 2013-10-01T20:00:00.000-04:00
dateTimeMontréal formatted: mardi 1 octobre 2013 20 h 00 EDT
dateTimeIndia: 2013-10-02T05:30:00.000+05:30
dateTimeUtc: 2013-10-02T00:00:00.000Z

Upvotes: 4

Related Questions