SimpleDateFormat行为不一致

时间:2015-07-03 11:33:02

标签: java simpledateformat

请参阅以下代码:

String timeString = "1980-01-01T14:00:00+0300";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
Date date2 = sdf.parse(timeString);

// sdf.getCalendar().get(Calendar.ZONE_OFFSET);
System.out.println(sdf.format(date2));

现在,我来自一个+2h偏移,+1夏令时(目前)的国家/地区。 如果我按原样运行此代码,它将打印

1980-01-01T13:00:00+0200

如果我取消注释询问日历偏移的行,则该程序的输出为

1980-01-01T14:00:00+0300

知道为什么会发生这种情况,我怎样才能获得一致的输出?

为了避免任何不清楚的事情: 由于我正在处理一些遗留代码,因此java 8不是一个选项。是的,这里的关键点是为什么,而不是解决方法是什么? 有2个为什么:

  1. 为什么我传递+0300 TZ,默认情况下我会收到+0200? (SimpleDateFormat应始终使用TimeZone.getDefault,除非 另有规定)。
  2. 为什么它只是因为我在其日历实例上称为getter而给出了不同的答案。

3 个答案:

答案 0 :(得分:2)

问题是,Calendar#get不是一个简单的getter,它看起来像这样:

public int  get(int field)
{
    complete();
    return internalGet(field);
}

根据Javadoc,complete执行此操作:

  

填写日历字段中的任何未设置字段。首先,如果尚未根据日历字段值计算时间值(距Epoch的毫秒偏移量),则调用computeTime()方法。然后,调用computeFields()方法来计算所有日历字段值。

我查了源here,但最新的Java版本的代码也一样。

答案 1 :(得分:0)

sdf.parse将格式化程序的内部日历区域偏移更改为+0300

    System.out.println(sdf.getCalendar());
    sdf.parse(timeString);
    System.out.println(sdf.getCalendar());

你可以看到输出行末尾的差异

... ,ZONE_OFFSET=7200000,DST_OFFSET=0]
... ,ZONE_OFFSET=10800000,DST_OFFSET=0]

sdf.getCalendar().get(Calendar.ZONE_OFFSET);将日历区域偏移恢复为当前时区

答案 2 :(得分:0)

SimpleDateFormate使用Calendar创建日期。使用时间戳创建日期对象,其中计算方式为java.util.GregorianCalendar.computeTime()。在第2789行中使用了初始时区(已设置java.text.SimpleDateFormat.initializeCalendar(Locale))。这就是为什么你看到错误的时区。

当您致电get(Calendar.ZONE_OFFSET)时,会调用方法java.util.GregorianCalendar.computeFields(),该方法使用最初设置的时区。

我猜这是一个Bug。