适用于地理位置不同的用户的日期操作/存储的Java最佳实践

Joe*_*ore 7 java database datetime date

我已经阅读了关于日期操作的所有其他Q/A,但它们似乎都没有给我满意的答案.

我有一个地理位置不同的用户项目,它Date在一些类和数据中使用.问题是我正在寻找一种有效的方法来操纵各自时区中不同用户的日期,大多数答案建议使用Joda库进行Date操作,这还不太明白,因为我还没有找到任何操作你不能用传统的Java做,所以如果有人可以解释我用Joda做什么不能用传统的Java做,那么我可以考虑使用它.

我终于开始使用System.currentTimeMillis()将日期保存到数据库(任何数据库)的方法.这样可以避免让我担心使用数据库存储日期的时区.如果我想查询数据库中的特定日期或日期范围,我会使用我想要查询的longDate来执行查询:

SELECT * FROM table1 WHERE date1>=1476653369000
Run Code Online (Sandbox Code Playgroud)

然后,在检索时,ResultSet我会使用请求数据的用户的时区long将从数据库检索到的值格式化为可读Date.

Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(resultSet.getLong(1));
cal.setTimeZone(TimeZone.getTimeZone("Asia/Calcutta"));
Date myDate = cal.getTime();
Run Code Online (Sandbox Code Playgroud)

根据我读过的一些观点,有些人强调说存储System.currentTimeMillis()肯定不是最好的做法,然而,由于某种原因,他们都错过了说为什么它不值得推荐.我错过了什么吗?这会导致转换的性能问题Long->Date/ Date->Long?有没有使用时无法实现任何使用情况下Long,而不是Date在数据库?有人可以发布关于此的理由解释吗?

另一方面,假设我继续使用Date值来存储数据库中的日期,有没有办法避免在处理数据库时担心时区Date

提前致谢.

Bas*_*que 25

我已经阅读了关于日期操作的所有其他Q/A.

不,你当然也不能阅读所有.

  • 您可能已经了解到遗留日期时间类(例如java.util.Date&java.util.Calendar)和Joda-Time项目都被java.time类取代(搜索'java.time'时为1,890个结果).
  • 您可能已经学会了不将日期时间值作为从纪元开始的数量来跟踪.由于人类无法将长整数的含义解读为日期时间,因此调试和日志记录变得非常困难,因为未发现错误.并且因为在各种软件项目中使用了许多粒度的计数(整秒,毫秒,微秒,纳秒,整天等)以及至少几十个时期,所以假设导致错误,误解和错误,会导致数据模糊不清混乱.
  • 您可能已经学会在数据库中使用日期时间类型来跟踪日期时间值.
  • 您将学会以UTC格式工作和存储日期时间值.仅在逻辑需要或用户期望的情况下调整到时区进行演示."全球化思考,本地化."
  • 您可能已经了解到,虽然是勇敢的行业第一次努力,但遗留日期时间类设计糟糕,令人困惑且麻烦.请参阅Java Date&Time API有什么问题?进行一些讨论.Joda-Time是业界第一个很好的日期时间库,它启发了它的替代,即Java 8及更高版本中内置的java.time类.

我会稍微简短一点,因为所有这些已经在Stack Overflow上已经多次介绍过.

以UTC工作.在Java中,这意味着Instant该类是常用的.该Instant级表示时间轴上的时刻UTC,分辨率为纳秒(最多9个(9)小数的位数).

Instant instant = Instant.now();
Run Code Online (Sandbox Code Playgroud)

任何严肃的数据库(如Postgres)都会以UTC格式跟踪日期时间值.JDBC驱动程序处理从数据库内部存储数据转换为Java类型的详细信息.符合JDBC 4.2及更高版本的JDBC驱动程序可以通过PreparedStatement::setObject&ResultSet::getObjectmethods 直接处理java.time类型.

myPreparedStatement.setObject( … , instant );
Run Code Online (Sandbox Code Playgroud)

对于不兼容的驱动程序,可以回退到使用java.sql类型,例如java.sql.Timestamp与数据库通信,以及通过添加到旧类的新方法转换为java.time类型.数据库如何处理日期时间值的内部细节可能与java.time的完全不同.在大多数情况下,JDBC驱动程序会隐藏您的所有细节.但是一个关键问题是解决方案,您应该在数据库中学习.java.time类处理日期时间,分辨率高达纳秒,但您的数据库可能不会.例如,Postgres使用微秒的分辨率.因此,来回往往意味着数据丢失.您希望在java.time类上使用截断方法来匹配您的数据库.

myPreparedStatement.setTimestamp( … , java.sql.Timestamp.from( instant ) );
Run Code Online (Sandbox Code Playgroud)

所以,没有涉及时区.所以没有"担心处理数据库日期时的时区".

当你想通过一个区域的挂钟时间镜头看同一时刻,应用a ZoneId来获得一个ZonedDateTime.

ZoneId z = ZoneId.of( "Asia/Kolkata" );
ZonedDateTime zdt = instant.atZone( z );
Run Code Online (Sandbox Code Playgroud)

将分区日期时间带回数据库时,提取一个Instant.

Instant instant = zdt.toInstant();
Run Code Online (Sandbox Code Playgroud)

请注意,在任何特定时刻,日期和时间在全球各个时区都有所不同.因此,如果确切的时刻很重要,例如合同到期时,请注意使用仅限日期的值.要么使用确切时刻的日期时间值,要么将预期时区与日期一起存储,以便稍后计算确切时刻.

LocalDate ld = LocalDate.of( 2016, 1 , 1 );
// Determine the first moment of 2016-01-01 as it happens in Kolkata.
ZonedDateTime zdt = ld.atStartOfDay( ZoneId.of( "Asia/Kolkata" ) ); 
Instant instant = zdt.toInstant();  // Adjust to UTC and store. 
Run Code Online (Sandbox Code Playgroud)

关于java.time

java.time框架是建立在Java 8和更高版本.这些类取代麻烦的老传统日期时间类,如java.util.Date,.Calendar,和java.text.SimpleDateFormat.

现在处于维护模式Joda-Time项目建议迁移到java.time.

要了解更多信息,请参阅Oracle教程.并搜索Stack Overflow以获取许多示例和解释.规范是JSR 310.

从哪里获取java.time类?

ThreeTen-额外项目与其他类扩展java.time.该项目是未来可能添加到java.time的试验场.您可以在此比如找到一些有用的类Interval,YearWeek,YearQuarter,和更多.


use*_*396 2

我可以用 Joda 做哪些传统 Java 做不到的事情

这并不是关于一般情况下您可以使用传统 Java 做什么或不能做什么。更多的是关于库 API 如何工作,使您比传统 Java 更容易编写更好(更健壮和正确)的代码。

以至于从 Java 8 开始,Joda API 或多或少被逐字复制/采用,只是更改了包名称并将其合并到 Java 8 SE 标准库中。

因此,如果您使用 Java 8,您应该选择新的 API,如果没有,您应该考虑使用 Joda 至少会为您提供一条升级/移植到 Java 8 的平滑路径(当您有能力时)。

举几个例子:

  • 日期和时间类型的一致 API。
  • 日期/时间对象是不可变的,操作返回表示更改值的类型的新实例。(如 Java 字符串)。这使得更容易推理日期/时间对象的重用。
  • 通过设计避免将 DST 和时区相关的值/操作与 DST 和时区无关的值/操作混合在一起。这使得编写一致且正确运行的代码变得更加容易,并且不存在依赖于时区/区域设置/日期/时间的极端情况。
  • 对于诸如序列化/反序列化之类的事情,Sane 默认值toString()预计可以以最小的努力正常工作。
  • 文化/区域设置相关的极端情况以及您尚未意识到的事情(例如,您知道传统的韩国日历吗?),这可以在区域设置/日历系统之间转换日期时间时为您省去很多麻烦。另外:丰富的格式选项。
  • s的概念Instant表示“绝对”时间戳,这在使用地理分布式系统(当系统默认时钟/时区和 DST 规则可能不同时)或互操作(因为它使用 UTC)时很有用。

编辑添加:

根据我读到的一些观点,有些人强调存储System.currentTimeMillis()绝对不是最佳实践,但是,由于某种原因,他们都没有说出为什么不推荐这样做。我错过了什么吗?

System.currentTimeMillis()有一些缺点。最大的缺点是时钟的类型定义不明确。它可以是单调时钟,可以是受 DST 和时区约束的时钟,也可以是 UTC 时间。它也不一定是准确的时钟,实际上不能保证精确到毫秒。基本上,无论发生什么,都可以作为当前时间的外观

这意味着,例如,如果您想使用多个服务器来处理传入请求,那么当您必须考虑在第二天在不同服务器上运行的程序的上下文中处理System.currentTimeMillis()from server的输出时,就会变得很棘手。AB