SimpleDateFormat 在不同时区 JVM 中的行为不同
Posted
技术标签:
【中文标题】SimpleDateFormat 在不同时区 JVM 中的行为不同【英文标题】:SimpleDateFormat behaves differently in different timezones JVM 【发布时间】:2012-12-14 01:33:30 【问题描述】:我正在按照下面的代码在指定的 dateTime 上使用指定的时区创建一个 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() -- 2012 年 12 月 25 日星期二 10:00:00 IST 在 GMT --- 12/25/2012 04:30:00 GMT当我的 jvm 服务器在 EST 中运行时 parsedDate.getTime() -- 1356422400000 parsedDate.toString() -- 2012 年 12 月 25 日星期二 03:00:00 EST 格林威治标准时间 --- 2012 年 12 月 25 日 08:00:00 格林威治标准时间
我的两个系统时间是同步的 2012 年 12 月 24 日星期一 10:30:04 EST 2012 年 12 月 24 日星期一 21:00:48 IST 我期望在两台机器上我应该得到相同的 GMT 时间。 这里出了什么问题?
【问题讨论】:
我们能看到完整的代码吗?您的代码现在没有显示任何 System.outs 只有当它们的 GMT 时间相同时,机器才会同步。尝试在两台机器上使用 System.currentTimeMillis()。无论时区设置如何,这将为您提供 GMT 时间。如果这些不一样,您必须修复它。 BTW 三字母代码不是唯一的。美国、欧洲、巴西和澳大利亚都有 EST。 @PeterLawrey 实际上它不是两台不同的机器。我刚刚更改了单机的时区并重新启动了我的应用程序。 因此,如果您使用 setTimeZone 设置 timezoen 而不是将其附加到 String 并尝试将其解析回来,它应该忽略您的默认时区并使用您给它的任何内容。 【参考方案1】:我认为问题在于您正在尝试解析 IST,它具有不同的含义,具体取决于您的默认“位置”是什么。
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
【讨论】:
【参考方案2】:短名称不是识别时区的好方法,因为它不是唯一的; the Javadoc for java.util.TimeZone
举例说明“‘CST’可以是美国‘中部标准时间’和‘中国标准时间’”。
更一般地说。 . .而不是将时区作为字符串传递,以便您的DateFormat
必须解析它,而是通过使用您已经使用的TimeZone
实例告诉您的DateFormat
时区是什么更有意义有:
DateFormat dateFormatLocal = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
dateFormatLocal.setTimeZone(timeZone);
java.util.Date parsedDate = dateFormatLocal.parse(date);
(在可能的范围内,这也会自动处理夏令时。)
【讨论】:
我目前正在尝试您的解决方案。您能否在回答中告诉我如何在不检查“timeZone.inDaylightTime(parsedDate)”的情况下处理夏令时? @KanagaveluSugumar:因为您已将timeZone
传递给您的dateFormatLocal
实例,它已经知道有关夏令时的所有信息,因此它会在适当的时候推断它。 (在可能的范围内。夏令时的一个后果是某些时间戳确实是模棱两可的,因为它们在 DST 转换之前不久发生,然后在转换之后不久再次发生。但您绝对无能为力。)【参考方案3】:
这里有一个简短的Groovy 脚本来演示如何使用缩写的时区名称是一个问题,并且结果会因本地环境而异,尤其是本地默认的TimeZone
。
假设您要解析 2015-02-20T17:21:17.190EST
,并且您考虑美国东海岸意义上的 EST = 东部标准时间,即纽约市时间。因此,您希望纪元时间恰好是 1424470877190
或 GMT: Fri, 20 Feb 2015 22:21:17.190 GMT
,因为这个 EST
是 GMT-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 小时(在本例中是 EST
的 Australia/Sydney
变体),因为夏令时适用于该日期。
因此您可以看到EST
的解释依赖于默认TimeZone
在其构造时。在前两个转换批次中,默认的TimeZone
是Australia/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
的效果,正如最初预期的那样。
【讨论】:
以上是关于SimpleDateFormat 在不同时区 JVM 中的行为不同的主要内容,如果未能解决你的问题,请参考以下文章
设置时区的 SimpleDateFormat 获得正确的值但区域错误
Calendar日期类详解SimpleDateFormat时区Date夏令时常用方法,日期差获取当前时间
遇到的问题---java---使用SimpleDateFormat进行时区加8小时后出现24点的数据,导致时间异常无法入库