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



我已经阅读了所有其他关于日期操作的问答,但似乎没有一个能给我一个满意的答案。

我有一个具有不同地理位置用户的项目,该项目在其一些类和数据中使用Date。事情是,我正在寻找一种有效的方法来操作日期为不同的用户在各自的时区,大多数的答案建议使用Joda库Date操作,这很不理解,因为我仍然没有发现任何操作你不能做传统的Java,所以如果有人可以解释我可以用Joda做什么,不能用传统的Java,然后我可能会考虑使用它。

我最终采用了使用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.Date&java.util.Calendar)和Joda-Time项目被java。时间类(在'java.time'上搜索1,890个结果)。
  • 您将学会不将日期时间值作为从epoch计数来跟踪。调试和日志记录变得非常困难,因为错误无法被发现,因为人类无法破译长整数作为日期-时间的含义。而且,由于在各种软件项目中使用了许多计数粒度(整秒、毫秒、微秒、纳秒、全天等等)和至少几十个epoch,因此会对数据产生歧义,并通过假设导致错误、误解和混淆。
  • 您应该已经学会在数据库中使用日期时间类型来跟踪日期时间值。
  • 你应该学会用UTC来工作和存储日期时间值。仅在逻辑需要或用户期望显示时调整为时区。"放眼全球,立足本地。">
  • 您可能已经了解到,虽然这是一个勇敢的行业首创的努力,但遗留的日期时间类设计得很差,令人困惑,而且很麻烦。参见Java Date &API ?进行一些讨论。Joda-Time是业界第一个优秀的日期时间库,并启发了它的替代品java。

我将稍微简短一些,因为所有这些已经在Stack Overflow中讨论过很多次了。

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

Instant instant = Instant.now();

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

myPreparedStatement.setObject( … , instant );

对于不兼容的驱动程序,退回使用java。sql类型,如java.sql.Timestamp与数据库通信,并转换为/从java。时间类型通过添加到旧类中的新方法进行。数据库如何处理日期时间值的内部细节可能与java的处理方式大不相同。时间。在大多数情况下,JDBC驱动程序向您隐藏了所有的基本细节。但是一个关键问题是解决方案,您应该在数据库中研究这个问题。java。时间类处理日期时间的分辨率最高可达纳秒,但您的数据库可能没有。例如,Postgres使用微秒级的分辨率。所以来回移动意味着数据丢失。您希望在java上使用截断方法。设置类的时间以匹配数据库。

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。时间框架内置于Java 8及以后版本中。这些类取代了麻烦的旧的遗留日期时间类,如java.util.Date,.Calendar, &java.text.SimpleDateFormat.

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

要了解更多信息,请参阅Oracle教程。并搜索Stack Overflow以获得许多示例和解释。规格为JSR 310。

从哪里获取java。时间类?

  • Java SE 8andSE 9后来
    • 内置。
    • 标准Java API的一部分,捆绑实现。
  • Java 9增加了一些小的特性和修复。
  • Java SE 6andSE 7
    • 大部分java。时间功能向后移植到Java 6;7 inThreeTen-Backport
  • Android
    • ThreeTenABP项目适应ThreeTen-Backport
    • 参见如何使用…
  • ThreeTen-Extra项目扩展了java。有额外课程的时间。这个项目是将来可能添加到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 &与时区无关的。这使得编写一致和正确工作的代码变得容易得多,并且没有依赖于时区/地区/日期/时间的极端情况。
    • toString()之类的默认设置,因此序列化/反序列化可以期望以最小的努力正确工作。
    • 文化/地区相关的角落情况和你还没有意识到的事情(例如,你知道传统的韩国日历吗?),当你在地区/日历系统之间转换日期时间时,这为你节省了很多麻烦。还有:丰富的格式化选项。
    • Instants表示"绝对"时间戳的概念在处理地理分布的系统时很有用(当系统默认时钟/时区&DST规则可能不同)或互操作,因为它使用UTC。

    编辑添加:

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

    System.currentTimeMillis()有一些缺点。最大的缺点是时钟的类型定义不清。它可以是一个单调的时钟,它可以是受夏令时和时区约束的时钟,也可以是一个UTC时间。它也不一定是一个精确的时钟,它实际上不能保证精确到毫秒。只要手边有什么东西就可以作为当前时间当前时间的表象。

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

    相关内容

    • 没有找到相关文章

    最新更新