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

时间:2010-03-01 15:04:24

标签: java timezone simpledateformat

我在谷歌和Stack Overflow上发布了关于TimeZone和SimpleDateFormat的多篇帖子,但仍然没有得到我做错的事。 我正在研究一些遗留代码,并且有一个方法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);
}

1 个答案:

答案 0 :(得分:2)

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

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

当您获得由于DST转换而无法实现的日期/时间组合时,您会想要会发生什么?在这种情况下,很难确定“错误”结果的含义。我会说你的单元测试存在一些缺陷 - 应该格式化到那个时间是没有可能的。

我猜测IBM时区“有效”的原因在于它可能会使用旧的时区数据,而不是在美国改变其DST转换之前。尝试使用3月28日,也就是我认为 已经不存在了 - 你可能会发现测试以与IBM区域相同的方式失败,但是没有与Sun一起失败:)(As太阳区不会认为它是DST过渡。)