SimpleDateFormat在不同的时区JVM中表现不同

时间:2012-12-28 20:28:26

标签: java date timezone simpledateformat

我按照以下代码在指定的dateTime上使用指定的Timezone创建Date对象 注意:我没有为jvm设置任何时区;但是使用不同的Linux服务器时区测试此代码。

    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));
    }


现在parsedDate对象的行为不同了 当我的jvm服务器在IST中运行时
parsedDate.getTime() - 1356409800000
parsedDate.toString() - 12月25日星期二10:00:00 IST 2012
在GMT --- 12/25/2012 04:30:00 GMT
当我的jvm服务器在EST中运行时
parsedDate.getTime() - 1356422400000
parsedDate.toString() - 2012年12月25日星期二03:00:00 在GMT --- 12/25/2012 08:00:00 GMT

我的两个系统时间都在同步中 2012年12月24日星期一10:30:04 2012年 2012年12月24日星期一21:00:48 我期待两台机器都能获得相同的GMT时间 这里出了什么问题?

3 个答案:

答案 0 :(得分:4)

我认为问题在于你试图解析IST,它具有不同的含义,具体取决于你的默认值" location"是

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

如果您所在的位置是印度人,则会按照您的预期对待IST,但如果您使用美国,则会猜到不同的时区。

解决方案是不使用三个字母的时区并明确设置它们。

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

答案 1 :(得分:3)

短名称不是识别时区的好方法,因为它不是唯一的; the Javadoc for java.util.TimeZone举例说明“'CST'可能是美国'中央标准时间'和'中国标准时间'”。

更一般。 。 。而不是将时区作为字符串传递,以便您的DateFormat必须解析它,通过使用{{1}告诉您的DateFormat时区是更有意义的你已经拥有的实例:

TimeZone

(这也将在可能的范围内自动处理夏令时。)

答案 2 :(得分:1)

这里有一个快速的Groovy脚本来演示如何使用缩写的时区名称是一个问题,结果将根据本地环境而有所不同,尤其是本地默认TimeZone

假设您要解析2015-02-20T17:21:17.190EST,并且您认为美国东海岸意义上的EST = 东部标准时间,那么纽约时间。因此,您希望纪元时间准确为1424470877190GMT: Fri, 20 Feb 2015 22:21:17.190 GMT,因为此ESTGMT-0500。这是一个测试脚本,显示当前默认TimeZone如何影响EST的解释。

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" : ":"))
}

输出:

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

在上面的输出中,意外/不正确的日期时间为1424413277190 = GMT: Fri, 20 Feb 2015 06:21:17.190 GMT,比解析的EST时间 11 小时(在这种情况下为Australia/Sydney变种EST),因为夏令时适用于该日期。

因此,您可以看到EST的解释取决于构建时的默认TimeZone。在前两个转换批次中,默认TimeZoneAustralia/Sydney,其本身缩写为EST,因此Java将缩写解释为此。直到在默认TZ改变之后构造新的SDF之后,我们才看到缩写应用为预期。

作为设置默认时区的替代方法,您还可以在SDF上设置Calendar实例:

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

这会产生解释EST的效果。