使用TimeZone和SimpleDateFormat进行日期解析/格式化会在DST开关周围产生不同的结果

我在谷歌和Stack Overflow上发布了关于TimeZone和SimpleDateFormat的多篇post,但仍然没有得到我做错的事情。 我正在研究一些遗留代码,并且有一个方法parseDate,它会产生错误的结果。

我附上了我试图使用的示例JUnit做调查问题。

第一种方法(testParseStrangeDate_IBM_IBM)使用IBM的实现来格式化parseDate方法的输出。 第二种格式输出与Sun的实现。

使用Sun的SimpleDateFormat可以让我们的时间相差一小时(这可能与Day Light Savings有关)。 将默认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); } 

麻烦的是,你已经指定了一个不存在的时间。 时钟前进使得凌晨2点变为凌晨3点 – 凌晨2:25从未发生过。

现在,这里可能会有各种各样的选择。 在野田时代,我相信我们会抛出exception(无论如何都是计划); 我相信Joda Time (一个比Date / Calendar / SimpleDateFormat好得多的Java API – 如果可能的话你应该考虑迁移到它)将在凌晨3:25给你,即转换后25分钟。

当您获得由于DST转换而无法实现的日期/时间组合时,您希望发生什么? 在这种情况下,很难确定“错误”结果的含义。 我会说你的unit testing有些缺陷 – 没有时间可以格式化到那个时候。

我猜测IBM时区“工作”的原因是它可能会使用旧的时区数据,从美国改变其DST转换之前。 尝试使用3月28日,也就是我认为它本来就是这样 – 你可能会发现测试失败的方式与IBM区域相同,但没有与Sun区域失败:)(因为Sun区域不会考虑这是一个DST过渡。)