我们正在处理大量数据,所有数据都以UTC(Java)标记。在读取这些数据,将其存储在数据库中并再次将其取出之前,有些数据在夏令时期间关闭了一小时。由于UTC没有夏令时的概念,这显然是软件中的一个错误。一旦知道,它很容易修复。
但是,无论当前的时差如何,都可以使用一些单元/集成测试,例如,我想更改本地时区并在这些不同的时区内反复运行一些方法,以确保正确处理UTC。
由于测试应该自动运行 - 最好是 - 在一个Testsuite中,我想知道如何最好地测试正确的行为。在重新启动JVM时更改本地设置(如时区)很容易,但在测试套件中运行它并不容易。
是否有人知道支持此方案的测试环境,库或模式?我们通常使用JUnit,但如果有助于摆脱这样的问题,我们可以添加其他环境/技术。我想这是一个整合而非单元测试。
编辑:已经有两个非常有用的答案了,但我想必须有更多技巧。有没有人有关于调用TimeZone.getDefault的时间/频率的权威信息(请参阅Jon Skeets答案的评论)?
注意:即使这个问题有一个接受的答案,我也不完全确定,接受哪个答案。即使有了这种接受,我也希望看到更多的想法和技巧。
感谢您的投入!
答案 0 :(得分:7)
Java允许您设置默认时区(java.util.TimeZone.setDefault)。我之前已经编写过测试,将时区设置为各种不同的选项,并检查一切是否仍然有效。但要小心 - 如果你要对大多数单元测试进行并行化,那么你需要将这些单元测试顺序化。
我建议你在一些时区进行测试,夏令时适用,有些没有。使用澳大利亚时区也很好,因为夏令时适用于一年中相反的时间到北半球。
答案 1 :(得分:6)
我建议您查看JodaTime,其中提供了一些糖,以帮助您在代码中更清晰地管理日期/时间/ TimeZone类型问题。
我们在整个测试和生产中使用这些,因为它如何为日期/时间问题提升原生Java API是无与伦比的。在测试中使用这些在JUnit中正常工作
答案 2 :(得分:1)
关于连接时间服务器和获取更新有什么意见?
答案 3 :(得分:1)
您的一小时休假问题确实可能是由于应用了时区调整而引起的。我猜您正在(a)与数据库交换字符串而不是对象,(b)使用可怕的旧式日期时间类,或(c)使用的工具或中间件对您说谎,它们是从数据库中检索到的值由于混淆了在数据库中检索之后和向您显示/报告给您之前应用默认时区的反特性的良好意图。
解决方案是始终使用现代的 java.time 类,并使用与JDBC 4.2或更高版本兼容的JDBC驱动程序。
捕获UTC中的当前时刻。
Instant instant = Instant.now() ;
您的JDBC驱动程序可能支持也可能不支持Instant
。 JDBC 4.2规范莫名其妙地要求支持OffsetDateTime
,但不支持两种更常用的类型Instant
和ZonedDateTime
。没问题,因为我们可以轻松转换。
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 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和SimpleDateFormat
。
要了解更多信息,请参阅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类?