测试正确的时区处理

时间:2009-01-25 17:36:36

标签: java timezone utc

我们正在处理大量数据,所有数据都以UTC(Java)标记。在读取这些数据,将其存储在数据库中并再次将其取出之前,有些数据在夏令时期间关闭了一小时。由于UTC没有夏令时的概念,这显然是软件中的一个错误。一旦知道,它很容易修复。

但是,无论当前的时差如何,都可以使用一些单元/集成测试,例如,我想更改本地时区并在这些不同的时区内反复运行一些方法,以确保正确处理UTC。

由于测试应该自动运行 - 最好是 - 在一个Testsuite中,我想知道如何最好地测试正确的行为。在重新启动JVM时更改本地设置(如时区)很容易,但在测试套件中运行它并不容易。

是否有人知道支持此方案的测试环境,库或模式?我们通常使用JUnit,但如果有助于摆脱这样的问题,我们可以添加其他环境/技术。我想这是一个整合而非单元测试。

编辑:已经有两个非常有用的答案了,但我想必须有更多技巧。有没有人有关于调用TimeZone.getDefault的时间/频率的权威信息(请参阅Jon Skeets答案的评论)?

注意:即使这个问题有一个接受的答案,我也不完全确定,接受哪个答案。即使有了这种接受,我也希望看到更多的想法和技巧。

感谢您的投入!

4 个答案:

答案 0 :(得分:7)

Java允许您设置默认时区(java.util.TimeZone.setDefault)。我之前已经编写过测试,将时区设置为各种不同的选项,并检查一切是否仍然有效。但要小心 - 如果你要对大多数单元测试进行并行化,那么你需要将这些单元测试顺序化。

我建议你在一些时区进行测试,夏令时适用,有些没有。使用澳大利亚时区也很好,因为夏令时适用于一年中相反的时间到北半球。

答案 1 :(得分:6)

我建议您查看JodaTime,其中提供了一些糖,以帮助您在代码中更清晰地管理日期/时间/ TimeZone类型问题。

我们在整个测试和生产中使用这些,因为它如何为日期/时间问题提升原生Java API是无与伦比的。在测试中使用这些在JUnit中正常工作

答案 2 :(得分:1)

关于连接时间服务器和获取更新有什么意见?

答案 3 :(得分:1)

您的一小时休假问题确实可能是由于应用了时区调整而引起的。我猜您正在(a)与数据库交换字符串而不是对象,(b)使用可怕的旧式日期时间类,或(c)使用的工具或中间件对您说谎,它们是从数据库中检索到的值由于混淆了在数据库中检索之后和​​向您显示/报告给您之前应用默认时区的反特性的良好意图。

java.time

解决方案是始终使用现代的 java.time 类,并使用与JDBC 4.2或更高版本兼容的JDBC驱动程序。

捕获UTC中的当前时刻。

Instant instant = Instant.now() ;  

您的JDBC驱动程序可能支持也可能不支持Instant。 JDBC 4.2规范莫名其妙地要求支持OffsetDateTime,但不支持两种更常用的类型InstantZonedDateTime。没问题,因为我们可以轻松转换。

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;

由于我们指定了ZoneOffset.UTC常量,因此该OffsetDateTime仍采用UTC(零小时-分钟-秒的偏移量)。

保存到数据库。

myPreparedStatement.setObject( … , odt ) ;

检索。

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

调整到一个时区以呈现给用户。

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant​( z ) ;

为用户界面automatically localized生成输出。

Locale locale = Locale.JAPAN ;  // Specify a `Locale` to determine 
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.LONG ).withLocale( locale ) ;
String output = zdt.format( f ) ;

请注意,在所有代码中,我们都没有使用String作为日期时间值。如果您与数据库交换这些智能对象而不是哑字符串,则不会注入任何时区调整。


关于 java.time

java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参阅Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规范为JSR 310

Joda-Time项目(现在位于maintenance mode中)建议迁移到java.time类。

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。 Hibernate 5和JPA 2.2支持 java.time

在哪里获取java.time类?