使用 TimeZone 和 SimpleDateFormat 进行日期解析/格式化会在 DST 切换时给出不同的结果
我在 Google 和 Stack Overflow 上浏览了多篇有关 TimeZone 和 SimpleDateFormat 的帖子,但仍然不明白我做错了什么。 我正在处理一些遗留代码,并且有一个方法 parseDate,它给出了错误的结果。
我附上了示例 JUnit,我正在尝试使用它来调查问题。
第一个方法(testParseStrangeDate_IBM_IBM)使用IBM的实现来格式化parseDate方法的输出。 Sun 实现的第二种格式输出。
使用 Sun 的 SimpleDateFormat 可以使我们的时间相差一个小时(这可能与夏令时有关)。将默认 TimeZone 设置为 IBM 的实现修复了 parseDate 方法(只需取消 setupDefaultTZ 方法中的 3 行注释即可)。
我确信这不是一个错误,但我做错了什么。
@Test
public void testParseStrangeDate_IBM_IBM() {
setupDefaultTZ();
Calendar date = parseDate("2010-03-14T02:25:00");
com.ibm.icu.text.SimpleDateFormat dateFormat = new com.ibm.icu.text.SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
// PASSES:
assertEquals("2010-03-14 02:25:00", dateFormat.format(date.getTime()));
}
@Test
public void testParseStrangeDate_SUN_SUN() {
setupDefaultTZ();
Calendar date = parseDate("2010-03-14T02:25:00");
java.text.SimpleDateFormat dateFormat = new java.text.SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
// FAILS:
assertEquals("2010-03-14 02:25:00", dateFormat.format(date.getTime()));
}
public static Calendar parseDate(String varDate) {
Calendar cal = null;
try {
// DOES NOT MAKE ANY DIFFERENCE:
// com.ibm.icu.text.SimpleDateFormat simpleDateFormat = new
// com.ibm.icu.text.SimpleDateFormat(
// "yyyy-MM-dd'T'HH:mm:ss");
java.text.SimpleDateFormat simpleDateFormat = new java.text.SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss", Locale.US);
Date date = simpleDateFormat.parse(varDate);
cal = GregorianCalendar.getInstance();
cal.setTimeInMillis(date.getTime());
System.out.println("CAL: [" + cal + "]");
} catch (ParseException pe) {
pe.printStackTrace();
}
return cal;
}
private void setupDefaultTZ() {
java.util.TimeZone timeZoneSun = java.util.TimeZone.getTimeZone("America/Chicago");
java.util.TimeZone.setDefault(timeZoneSun);
// UNCOMMENTING THIS ONE FIXES SUN PARSING ??
// com.ibm.icu.util.TimeZone timeZoneIbm = com.ibm.icu.util.TimeZone
// .getTimeZone("America/Chicago");
// com.ibm.icu.util.TimeZone.setDefault(timeZoneIbm);
Locale.setDefault(Locale.US);
}
I went throe multiple posts about TimeZone and SimpleDateFormat on Google and Stack Overflow, but still do not get what I'm doing wrong.
I'm working on some legacy code, and there is a method parseDate, which gives wrong results.
I attached sample JUnit which I'm trying to use do investigate issue.
First method (testParseStrangeDate_IBM_IBM) uses IBM's implementation to format output of parseDate method.
Second formats output with Sun's implementation.
Using Sun's SimpleDateFormat gives us time different by an hour (which might be related to Day Light Savings). Setting default TimeZone to IBM's implementation fixes parseDate method (simply uncomment 3 lines in setupDefaultTZ method).
I am sure it's not a bug, but I am doing something wrong.
@Test
public void testParseStrangeDate_IBM_IBM() {
setupDefaultTZ();
Calendar date = parseDate("2010-03-14T02:25:00");
com.ibm.icu.text.SimpleDateFormat dateFormat = new com.ibm.icu.text.SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
// PASSES:
assertEquals("2010-03-14 02:25:00", dateFormat.format(date.getTime()));
}
@Test
public void testParseStrangeDate_SUN_SUN() {
setupDefaultTZ();
Calendar date = parseDate("2010-03-14T02:25:00");
java.text.SimpleDateFormat dateFormat = new java.text.SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
// FAILS:
assertEquals("2010-03-14 02:25:00", dateFormat.format(date.getTime()));
}
public static Calendar parseDate(String varDate) {
Calendar cal = null;
try {
// DOES NOT MAKE ANY DIFFERENCE:
// com.ibm.icu.text.SimpleDateFormat simpleDateFormat = new
// com.ibm.icu.text.SimpleDateFormat(
// "yyyy-MM-dd'T'HH:mm:ss");
java.text.SimpleDateFormat simpleDateFormat = new java.text.SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss", Locale.US);
Date date = simpleDateFormat.parse(varDate);
cal = GregorianCalendar.getInstance();
cal.setTimeInMillis(date.getTime());
System.out.println("CAL: [" + cal + "]");
} catch (ParseException pe) {
pe.printStackTrace();
}
return cal;
}
private void setupDefaultTZ() {
java.util.TimeZone timeZoneSun = java.util.TimeZone.getTimeZone("America/Chicago");
java.util.TimeZone.setDefault(timeZoneSun);
// UNCOMMENTING THIS ONE FIXES SUN PARSING ??
// com.ibm.icu.util.TimeZone timeZoneIbm = com.ibm.icu.util.TimeZone
// .getTimeZone("America/Chicago");
// com.ibm.icu.util.TimeZone.setDefault(timeZoneIbm);
Locale.setDefault(Locale.US);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
问题是,您指定的时间并不存在。时钟向前推进,这样凌晨 2 点就会变成凌晨 3 点 - 凌晨 2:25 永远不会发生。
现在,这里可能发生的事情有多种选择。在Noda Time中,我相信我们会抛出一个异常(无论如何,这就是计划);我相信 Joda Time (一个比 Date/Calendar/SimpleDateFormat 更好的 Java API - 你应该考虑迁移到如果可以的话)将为您提供凌晨 3:25,即转换后 25 分钟。
当您获得因 DST 转换而无法实现的日期/时间组合时,您希望发生什么情况?在这种情况下,很难确定“错误”结果的含义。我想说你的单元测试有些缺陷——没有可能的时间应该被格式化为那个时间。
我对 IBM 时区“有效”的原因的猜测是,它可能使用美国更改 DST 转换之前的旧时区数据。尝试使用 3 月 28 日,我认为那时会出现其他情况 - 您可能会发现测试以与 IBM 区域相同的方式失败,但与 Sun 区域不同:)(如太阳区不会将其视为 DST 过渡。)
The trouble is, you've specified a time which doesn't exist. The clocks go forward such that 2am becomes 3am - 2:25am never happens.
Now, there are various options for what could happen here. In Noda Time I believe we'd throw an exception (that's the plan anyway); I believe Joda Time (a far better Java API than Date/Calendar/SimpleDateFormat - you should consider migrating to it if you possibly can) will give you 3:25am, i.e. 25 minutes after the transition.
What would you want to happen when you're given a date/time combination which is impossible due to the DST transition? In this situation it's hard to know for sure what you mean by the "wrong" results. I would say your unit test is somewhat flawed - there is no possible time which should be formatted to that time.
My guess as to why the IBM time zone "works" is that it may use old time zone data, from before the US changed its DST transitions. Try using March 28th, which is when I think it would have been otherwise - you'll probably find the tests fail in the same way with the IBM zone, but not with the Sun one :) (As the Sun zone won't consider it a DST transition.)