问题
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?
回答1:
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.)
回答2:
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
回答3:
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.
来源:https://stackoverflow.com/questions/14075373/simpledateformat-behaves-differently-in-different-timezones-jvm