Java SimpleDataFormat的行为不一致
请参阅以下代码:Java SimpleDataFormat的行为不一致,java,simpledateformat,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))
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
你知道为什么会这样吗?我怎样才能得到一致的输出
为了避免任何不清楚的事情:
因为我正在处理一些遗留代码,所以Java8不是一个选项。是的,这里的关键是为什么,而不是解决办法是什么?
有两个原因:
问题是,
Calendar\get
不是一个简单的getter,它看起来是这样的:
public int get(int field)
{
complete();
return internalGet(field);
}
其中,complete
根据Javadoc执行此操作:
填写日历字段中任何未设置的字段。首先,如果没有从日历字段值计算时间值(从历元的毫秒偏移量),则调用computeTime()
方法。然后,调用computeFields()
方法来计算所有日历字段值
我查找了源代码,但最新Java版本的代码也相同。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)代码>将日历的区域偏移恢复到当前时区SimpleDataFormat使用日历创建日期。Date对象是用时间戳创建的,它是在java.util.Gregoriacalendar.computeTime()中计算的。第2789行使用了初始时区(设置为java.text.simpleDataFormat.initializeCalendar(Locale)
)。这就是为什么你看到了错误的时区
调用get(Calendar.ZONE\u OFFSET)
时,将调用方法java.util.Gregoriacalendar.computeFields()
,该方法使用最初设置的时区
我想这是一个Bug。其中有什么不一致之处?当您不使用偏移量时,它会按原样给出日期,当您使用它时,它会根据时区的不同而变化。@RamanShrivastava调用一次getter不会改变format函数的输出。是的,我知道这一点。但这只是一种“它这样做是因为它这样做”。对我来说,这绝对像一个bug。我会说,这是一个有争议的设计决策。Java 8有一个新的日期和时间API是有原因的:)这个答案非常完美,只需一个小片段,说明OP如何更改他的代码,以避免complete()
破坏他的代码(例如,在日历实例上设置一些特定字段等),而这并不能解释它SimpleDataFormat.format()
实际上也调用了Calendar.get()
。问题是:它为什么这样做?我的直觉是:日历引用了一个时区对象。解析日期时,无法从“+0300”确定时区。它只在日历的ZONE\u offset
字段中存储3小时的偏移量。get()
方法在尚未对所有字段(包括ZONE\u OFFSET
字段)进行内部计算时,会对日历的所有字段进行计算。日历使用它知道的时区来计算偏移量并确定不同的偏移量。是的,我假设这是最准确的答案。事实上,我现在所在的位置,正如我在最初的帖子中所写的,我处于+2偏移量+1 dst区域。在SimpleDataFormat模式中,这是不可重复的。这就是为什么我们将其合并为+3。但是,Calendar和SimpleDataFormat所做的整个内部处理中仍然存在一个bug,因为在(+2+1)中转换(+3+0)或保持原样的行为应该是一致的,并且不受get的影响。因为我在创建后并没有以任何方式修改sdf,所以sdf.format(sdf.parse(txt))应该始终是txt。