繁体   English   中英

Java最佳实践,适用于不同地理位置的用户进行日期处理/存储

[英]Java Best Practice for Date Manipulation/Storage for Geographically Diverse Users

我已经阅读了有关日期操作的所有其他问题,但是似乎都没有一个令人满意的答案。

我有一个具有不同地理位置用户的项目,该项目在某些类和数据中使用Date 事实是,我正在寻找一种有效的方式来为不同用户在各自的时区中操作日期,大多数答案建议使用Joda库进行Date操作,由于我仍然没有发现任何东西,所以这还不太了解您无法使用传统Java执行此操作,因此,如果有人可以解释使用传统Java无法完成的Joda功能 ,那么我可以考虑使用它。

我终于找到了使用System.currentTimeMillis()将日期保存到数据库(任何数据库)中的方法。 这样可以避免我担心使用数据库存储日期的时区。 如果要在数据库中查询特定日期或日期范围,我将使用要查询的Datelong值执行查询:

SELECT * FROM table1 WHERE date1>=1476653369000

当检索ResultSet我将使用请求数据的用户所在的时区将从数据库中检索到的long值格式化为可读的Date

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

根据我已阅读的一些意见,有人强调说,存储System.currentTimeMillis()绝对不是最佳实践,但是由于某些原因,他们都错过了为什么不推荐这样做的原因 我想念什么吗? 这是否会导致Long->Date / Date->Long转换的性能问题? 在数据库中使用Long代替Date时,是否有任何用例无法完成? 有人可以对此发表基本解释吗?

另一方面,假设我一直使用Date值在数据库中存储日期,是否有一种方法可以避免在处理数据库Date时担心时区?

提前致谢。

我已经阅读了有关日期操作的所有其他问答。

不,你肯定看过他们所有。

  • 您将了解到,遗留的日期时间类(例如java.util.Datejava.util.Calendar )和Joda-Time项目都被java.time类所替代(在'java.s中搜索的结果为1,890个)。时间')。
  • 您将学到不要将日期时间值跟踪为从时计数。 由于人类无法理解长整数作为日期时间的含义,因此调试和日志记录变得非常困难,而bug却未被发现。 而且由于在各种软件项目中采用了许多粒度的计数(整秒,毫秒,微秒,纳秒,整天等等),并且至少使用了几十个纪元,因此在数据假设上会造成歧义,并会导致错误,误解和错误。混乱。
  • 您将已经学会在数据库中使用日期时间类型来跟踪日期时间值。
  • 您将学会在UTC中工作和存储日期时间值。 仅在逻辑要求或用户希望演示时调整到时区。 “想全球化,就本地。”
  • 您将了解到,尽管英勇尝试了行业先机,但传统的日期时间类设计不良,混乱且麻烦。 请参阅Java日期和时间API有什么问题? 进行一些讨论。 Joda-Time是业界第一个好的日期时间库,它启发了Java内置于Java 8及更高版本中的java.time类的替换。

我会有些简短,所有这一切都已经覆盖了很多次了堆栈溢出。

在UTC工作。 在Java中,这意味着通常使用Instant类。 Instant类以UTC表示时间轴上的时刻,分辨率为纳秒 (最多十进制的九(9)位数字)。

Instant instant = Instant.now();

任何严肃的数据库,例如Postgres,都会以UTC跟踪日期时间值。 JDBC驱动程序处理从数据库内部存储的数据转换为Java类型的细节。 符合JDBC 4.2及更高版本的JDBC驱动程序可以通过PreparedStatement::setObjectResultSet::getObject方法直接处理java.time类型。

myPreparedStatement.setObject( … , instant );

对于不符合标准的驱动程序,回落到使用的java.sql类型,如java.sql.Timestamp与数据库进行通信,并通过加入到老班新方法转换到/自java.time类型。 数据库如何处理日期时间值的内部细节可能与java.time的方法完全不同。 在大多数情况下,JDBC驱动程序会向您隐藏所有细节。 但是一个关键问题是分辨率,您应该在数据库中进行研究。 java.time类处理的日期时间的分辨率最高为纳秒,但数据库可能不会。 例如,Postgres使用微秒的分辨率。 因此来回意味着数据丢失。 您想在java.time类上使用截断方法来匹配您的数据库。

myPreparedStatement.setTimestamp( … , java.sql.Timestamp.from( instant ) );

因此,不涉及任何时区。 因此,无需“在处理数据库日期时担心时区”。

当您想通过某个区域的时钟时钟看到同一时刻 ,请应用ZoneId来获取ZonedDateTime

ZoneId z = ZoneId.of( "Asia/Kolkata" );
ZonedDateTime zdt = instant.atZone( z );

将带区域的日期时间带回到数据库时,请提取Instant

Instant instant = zdt.toInstant();

请注意,在任何给定时刻,日期和一天中的时间在全球范围内随时区变化。 因此,如果确切的时刻很重要,例如合同到期时,请注意不要使用仅日期的值。 可以将日期时间值用于确切的时刻,或者将预期的时区与仅日期一起存储,以便稍后可以计算出准确的时刻。

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. 

关于java.time

java.time框架内置于Java 8及更高版本中。 这些类取代了麻烦的旧式旧式日期时间类,例如java.util.Date.Calendarjava.text.SimpleDateFormat

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

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

在哪里获取java.time类?

  • Java SE 8SE 9及更高版本
    • 内置的
    • 标准Java API的一部分,具有捆绑的实现。
    • Java 9添加了一些次要功能和修复。
  • Java SE 6SE 7
    • java.time的许多功能在ThreeTen- Backport中都被反向移植到Java 6和7。
  • 安卓系统

ThreeTen-Extra项目使用其他类扩展了java.time。 该项目为将来可能在java.time中添加内容提供了一个试验场。 您可以在这里找到一些有用的类,比如IntervalYearWeekYearQuarter ,和更多

我可以用Joda做什么,而传统Java则无法做到

通常情况下,这与使用传统Java可以做什么或不能做什么有关。 与库API的工作方式有关的更多信息是如何使您比传统Java更好地编写更好(更健壮和更正确)的代码。

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

因此,如果您使用的是Java 8,则应该选择新的API,否则,您应该考虑使用Joda至少会为您提供一条通向Java 8升级/移植的平稳之路。

一些例子:

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

编辑添加:

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

System.currentTimeMillis()有一些缺点。 最大的缺点是时钟的类型定义不正确。 它可以是单调时钟,也可以是受DST和时区限制的时钟,也可以是UTC时间。 它也不一定是准确的时钟,实际上并不能保证精确到毫秒。 基本上,将发生什么事情都可以像当前时间一样进行

这意味着,例如,如果您想使用多个服务器来处理传入请求,则当您不得不考虑在另一个服务器上运行的程序的上下文中使用服务器ASystem.currentTimeMillis()的输出时,这将变得很棘手。 B第二天说。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM