测试正确的时区处理

问题描述 投票:7回答:4

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

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

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

有人知道支持这种情况的测试环境,库或模式吗?我们通常与JUnit合作,但愿意添加其他环境/技术,以帮助消除此类问题。我想这是一个集成而不是单元测试。

Edit:已经有两个非常有用的答案,但是我想这里肯定还有更多的技术。是否有人有关于何时/多久调用一次TimeZone.getDefault的权威信息(请参阅有关Jon Skeets答案的评论)?

:即使该问题的答案已被接受,我也不确定要接受哪个答案。即使获得了这种认可,我仍然希望看到更多的想法和技术。

感谢您的输入!

java timezone utc
4个回答
7
投票

我建议您签出JodaTime,它提供了一些糖来帮助您在代码中更清晰地管理Date / Time / TimeZone类型问题。

我们在测试和生产过程中都使用了这些,因为它如何增强本机Java API来解决日期/时间问题是无与伦比的。在测试中使用它们可以在JUnit中正常运行


8
投票

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

我建议您在某些时区进行夏令时测试,而某些时区则不这样做。使用澳大利亚时区也很好,因为夏令时适用于每年的相反时间到北半球。


1
投票

关于连接到时间服务器并获取更新有什么看法?


0
投票

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

java.time

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

UTC中捕获当前时刻。

Instant instant = Instant.now() ;

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

Instant

由于我们指定了ZonedDateTime常量,所以此ZonedDateTime仍采用UTC(零小时-分钟-秒的偏移量。)>

保存到数据库。

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

检索。

OffsetDateTime

调整到显示给用户的时区。

ZoneOffset.UTC

为用户界面生成输出,ZoneOffset.UTC

myPreparedStatement.setObject( … , odt ) ;

[注意,在所有代码中均未使用OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
作为日期时间值。如果与数据库交换这些智能对象而不是哑字符串,则不会注入任何时区调整。 


关于

java.time

ZoneId z = ZoneId.of( "Africa/Tunis" ) ; ZonedDateTime zdt = odt.atZoneSameInstant​( z ) ; 框架内置于Java 8及更高版本中。这些类取代了麻烦的旧automatically localized日期时间类,例如Locale locale = Locale.JAPAN ; // Specify a `Locale` to determine DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.LONG ).withLocale( locale ) ; String output = zdt.format( f ) ; Stringjava.time

要了解更多信息,请参见legacy。并在Stack Overflow中搜索许多示例和说明。规格为java.util.Date

java.util.Date项目现在位于Calendar中,建议迁移到Calendar类。

您可以直接与数据库交换

java.time

对象。使用兼容SimpleDateFormat或更高版本的SimpleDateFormat。不需要字符串,不需要Oracle Tutorial类。 Hibernate 5和JPA 2.2支持java.time
从哪里获取java.time类?

  • [JDBC driverJDBC 4.2
    • << java.time的大多数功能>在java.sql.*中被反向移植到Java 6和7。
  • Java SE 8
    • java.time
    类的Android捆绑包实现的最新版本。
  • 对于较早的Android(<26),Java SE 9项目改编为Java SE 10(如上所述)。参见Java SE 11
  • © www.soinside.com 2019 - 2024. All rights reserved.