【问题标题】:Java timezone issue with Joda TimeJoda Time 的 Java 时区问题
【发布时间】:2013-09-03 12:19:38
【问题描述】:

我在一些日期解析和从我的格式中获取正确的日期/时间方面遇到了一个相当棘手的问题。这是进行格式化的代码(精简版)

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

这是使用 OpenJDK 7 在 Tomcat 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 这里在 OS X 上运行 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()));

只是为了确保匹配。一个“正确”的解决方法是在应用启动时明确设置系统默认时区。

【问题讨论】:

  • 尝试打印已解析的DateTime 的时区:DateTime parsed = formatter.parseDateTime(inputText); System.out.println(parsed.getZone()); 另外,如果您正在解析日期,为什么还要将其解析为DateTime 而不是LocalDate
  • 另外,尝试在每台机器上打印出DateTimeZone.getDefault - 我想知道它是否没有正确地选择它,所以它使用了与 JDK 不同的默认值。
  • @JonSkeet 这就是我想知道的,为什么它只在 tomcat 中这样做。刚刚部署了一些带有更多调试信息的更改(带有 tz 吐出),所以会看到它说了什么。
  • @JonSkeet 查看我的更新,Joda 似乎没有选择与 Java 类相同的时区。
  • user.timezone 系统属性的值是多少?还有其他什么设置吗?

标签: java date tomcat timezone jodatime


【解决方案1】:

Joda Time 经过以下步骤:

  • 使用user.timezone系统属性
  • 根据TimeZone.getDefault()的ID创建时区
  • 回退到 UTC

TimeZone.getDefault() 可以稍微复杂一些,尽管它在很多情况下也使用user.timezone

一种选择是明确在应用程序启动时将TimeZoneDateTimeZone 的默认时区设置为UTC:对于Web 服务器,这是最合理的默认时区(虽然我个人更喜欢在适当的情况下明确说明时区)。

【讨论】:

  • 似乎是 user.timezone 属性导致了问题。但是,我不知道这是从哪里设置的 Tomcat(找不到任何应该引用和实际设置的地方)。
  • 我同意 Jon Skeets 的建议,即在您的代码中指定所需的时区,而不是依赖于任何默认值。 Joda-Time 为 UTC 提供了一个常量:DateTimeZone.UTC。例如,`DateTime dateTime = new DateTime( "2013-07-08", DateTimeZone.UTC );'
猜你喜欢
  • 2012-07-22
  • 1970-01-01
  • 2016-07-21
  • 2014-08-29
  • 1970-01-01
  • 2013-01-26
  • 1970-01-01
  • 1970-01-01
  • 2014-03-24
相关资源
最近更新 更多