Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SimpleDateFormat behaves differently in different timezones JVM

I am following the below code to create a Date object on specified dateTime with a specified Timezone.
Note: I haven't set any timezone for jvm; But testing this code with different linux server timezones.

    String date = "20121225 10:00:00";
    String timeZoneId = "Asia/Calcutta";
    TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);

    DateFormat dateFormatLocal = new SimpleDateFormat("yyyyMMdd HH:mm:ss z");
                //This date object is given time and given timezone
    java.util.Date parsedDate = dateFormatLocal.parse(date + " "  
                     + timeZone.getDisplayName(false, TimeZone.SHORT));

    if (timeZone.inDaylightTime(parsedDate)) {
        // We need to re-parse because we don't know if the date
        // is DST until it is parsed...
        parsedDate = dateFormatLocal.parse(date + " "
                + timeZone.getDisplayName(true, TimeZone.SHORT));
    }


Now parsedDate object behaves differently
When my jvm Server is running in IST
parsedDate.getTime() -- 1356409800000
parsedDate.toString() -- Tue Dec 25 10:00:00 IST 2012
in GMT --- 12/25/2012 04:30:00 GMT
When my jvm Server is running in EST
parsedDate.getTime() -- 1356422400000
parsedDate.toString() -- Tue Dec 25 03:00:00 EST 2012
in GMT --- 12/25/2012 08:00:00 GMT

My both system times are in sync
Mon Dec 24 10:30:04 EST 2012
Mon Dec 24 21:00:48 IST 2012
I am expecting in both machine i should get the same GMT time.
What was wrong here?

like image 228
Kanagavelu Sugumar Avatar asked Dec 28 '12 20:12

Kanagavelu Sugumar


People also ask

Is SimpleDateFormat deprecated?

Class SimpleDateFormat. Deprecated. A class for parsing and formatting dates with a given pattern, compatible with the Java 6 API.

What is the use of SimpleDateFormat in Java?

SimpleDateFormat is a concrete class for formatting and parsing dates in a locale-sensitive manner. It allows for formatting (date -> text), parsing (text -> date), and normalization. SimpleDateFormat allows you to start by choosing any user-defined patterns for date-time formatting.


3 Answers

I believe the problem is that you are attempting to parse the IST and it has different meanings depending on what you default "location" is.

Time Zone Abbreviation  Zone Description    Relative UTC
IST     Irish Summer Time   UTC+01
IST     Israeli Standard Time   UTC+02
IST     Iran Standard Time  UTC+0330
IST     Indian Standard Time    UTC+0530

If your location is Indian it treats IST as you expect, but if you use the USA, it guesses a different time zone.

The solution is to not use three letter timezone and set them explicitly.

String date = "20121225 10:00:00";
String timeZoneId = "Asia/Calcutta";
TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);

DateFormat dateFormatLocal = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
dateFormatLocal.setTimeZone(timeZone);

Date parsedDate = dateFormatLocal.parse(date);

http://www.worldtimezone.com/wtz-names/wtz-ist.html

like image 153
Peter Lawrey Avatar answered Oct 16 '22 21:10

Peter Lawrey


The short-name is not a good way to identify a time-zone, because it's not unique; the Javadoc for java.util.TimeZone gives the example that "'CST' could be U.S. 'Central Standard Time' and 'China Standard Time'".

More generally . . . instead of passing the time-zone as a string, so that your DateFormat has to parse it, it makes more sense to just tell your DateFormat what the time-zone is, by using the TimeZone instance that you already have:

    DateFormat dateFormatLocal = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
    dateFormatLocal.setTimeZone(timeZone);
    java.util.Date parsedDate = dateFormatLocal.parse(date);

(This will also take care of daylight-savings time automatically, to the extent that that's possible.)

like image 39
ruakh Avatar answered Oct 16 '22 22:10

ruakh


Here's a quick bit of Groovy script to demonstrate how using an abbreviated timezone name is a problem and that results will vary according to the local environment, and especially the local default TimeZone.

Say you want to parse 2015-02-20T17:21:17.190EST, and you consider EST = Eastern Standard Time in the US East Coast sense, so New York City time. You therefore expect the epoch time to be exactly 1424470877190, or GMT: Fri, 20 Feb 2015 22:21:17.190 GMT since this EST is GMT-0500. Here's a test script that shows how the current default TimeZone effects how EST is interpreted.

import java.util.*
import java.text.*

SimpleDateFormat sdf

TimeZone.setDefault(TimeZone.getTimeZone("Australia/Sydney"))
printDefaultTimeZone()

sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSz")

checkTime(sdf.parse("2015-02-20T17:21:17.190EST").getTime())
checkTime(sdf.parse("2015-02-20T17:21:17.190-0500").getTime())

TimeZone.setDefault(TimeZone.getTimeZone("EST"))
printDefaultTimeZone()

// same SDF

checkTime(sdf.parse("2015-02-20T17:21:17.190EST").getTime())
checkTime(sdf.parse("2015-02-20T17:21:17.190-0500").getTime())

printDefaultTimeZone()

// new SDF
sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSz")

checkTime(sdf.parse("2015-02-20T17:21:17.190EST").getTime())
checkTime(sdf.parse("2015-02-20T17:21:17.190-0500").getTime())

void printDefaultTimeZone() {
    println(TimeZone.getDefault().getDisplayName() + ":" +     TimeZone.getDefault().getRawOffset() / 3600 / 1000)
}

void checkTime(long time) {
    println(time + (time == 1424470877190L ? ": CORRECT" : ":"))
}

Output:

Eastern Standard Time (New South Wales):10 1424413277190: 1424470877190: CORRECT Eastern Standard Time:-5 1424413277190: 1424470877190: CORRECT Eastern Standard Time:-5 1424470877190: CORRECT 1424470877190: CORRECT

In the above output, the unexpected/incorrect datetime is 1424413277190 = GMT: Fri, 20 Feb 2015 06:21:17.190 GMT, which is 11 hours earlier than the parsed EST time (in this case the Australia/Sydney variant of EST), because daylight-savings time applies to that date.

So you can see that the interpretation of EST depends on the default TimeZone at the time of its construction. In the first two conversion batches, the default TimeZone was Australia/Sydney, which is itself abbreviated as EST, so Java interprets the abbreviation as just that. Not until after the new SDF is constructed after the default TZ has changed do we see the abbreviation applied as-expected.

As an alternative to setting the default timezone, you could also set a Calendar instance on the SDF:

sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSz")
sdf.setCalendar(Calendar.getInstance(TimeZone.getTimeZone("America/New_York"), new Locale("en_US")))

This will have the effect of interpreting EST as was originally expected.

like image 1
javabrett Avatar answered Oct 16 '22 21:10

javabrett