我在一些日期解析和从格式中获取正确的日期/时间方面遇到了一个相当麻烦的问题。这是进行格式化的代码(浓缩版)
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
log.debug("Trying to convert {} using format {}.", val, "yyyy-MM-dd");
Date toDate = formatter.parseDateTime(inputText).toDate();
log.debug("Converted value to {}", toDate);
以下是值不正确的一些输出。此处的时区设置为EDT。(通过date
命令和/etc/localtime符号链接进行检查。
13:14:53.618 [http-bio-8080-exec-13] DEBUG c.s.e.p.j.AbstractParamToObjectProvider - Trying to convert 2013-07-08 using format yyyy-MM-dd.
13:14:53.619 [http-bio-8080-exec-13] DEBUG c.s.e.p.j.AbstractParamToObjectProvider - Converted value to Sun Jul 07 20:00:00 EDT 2013
这是在Tomcat 7上使用OpenJDK 7的AWS-LINUX上运行的。我们有另一个在AWS上运行的实例,相同的代码产生了这个:
17:22:46.164 [http-bio-8080-exec-239] DEBUG c.s.e.p.j.AbstractParamToObjectProvider - Trying to convert 2013-07-08 using format yyyy-MM-dd.
17:22:46.165 [http-bio-8080-exec-239] DEBUG c.s.e.p.j.AbstractParamToObjectProvider - Converted value to Mon Jul 08 00:00:00 UTC 2013
这台机器上的时区设置为UTC。
在我的本地机器上,输出也是正确的(回到EDT,在OSX上运行oracles JDK):
13:24:55.967 [ajp-bio-8009-exec-176] DEBUG c.s.e.p.j.AbstractParamToObjectProvider - Trying to convert 2013-07-08 using format yyyy-MM-dd.
13:24:56.089 [ajp-bio-8009-exec-176] DEBUG c.s.e.p.j.AbstractParamToObjectProvider - Converted value to Mon Jul 08 00:00:00 EDT 2013
同样,三个位置的代码都相同。我一辈子都不明白为什么我们有一个例子的行为与其他人不同。我将添加更多的调试输出,试图缩小它的范围,但现在我运气不好。
另外,还有一个有趣的地方。在Tomcat之外的错误机器上运行,使用相同的jdk和以下代码,我得到了我所期望的:
DateTimeFormatter f = DateTimeFormat.forPattern("yyyy-MM-dd");
DateTime parseDateTime = f.parseDateTime("2013-07-08");
System.out.println(parseDateTime.toDate());
输出:
Mon Jul 08 00:00:00 EDT 2013
更新
Joda似乎没有使用系统默认时区。以下是我输出数据的方式:
log.debug("Using joda timezone of: {}", DateTimeZone.getDefault());
log.debug("Default timezone of: {}", TimeZone.getDefault());
这是输出:
AbstractParamToObjectProvider - Using joda timezone of: UTC
AbstractParamToObjectProvider - Default timezone of: sun.util.calendar.ZoneInfo[id="America/New_York",offset=-18000000,dstSavings=3600000,useDaylight=true,transitions=235,lastRule=java.util.SimpleTimeZone[id=America/New_York,offset=-18000000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]
其中一个与另一个不匹配。。。有什么想法吗?
解决方案
乔恩的回答是正确的,但这是最后的结论。
不知怎的,user.timezone被设置为UTC,Joda正在使用它,而默认的timezone没有(使用系统时区)。对于一个补丁,我做了以下操作:
DateTimeZone.setDefault(DateTimeZone.forTimeZone(TimeZone.getDefault()));
只是为了确保匹配。"正确"的解决方案是在应用程序启动时显式设置系统默认时区。
Joda Time经过以下步骤:
- 使用
user.timezone
系统属性 - 根据
TimeZone.getDefault()
的ID创建时区 - 回退到UTC
TimeZone.getDefault()
可能稍微复杂一点,尽管它在许多情况下也使用user.timezone
。
一种选择是在应用程序启动时显式将TimeZone
和DateTimeZone
的默认时区设置为UTC:对于web服务器,这是最合理的默认使用方式(尽管我个人更喜欢在适当的情况下显式说明时区)。