面向不同地域用户的日期操作/存储的 Java 最佳实践 [英] Java Best Practice for Date Manipulation/Storage for Geographically Diverse Users

查看:36
本文介绍了面向不同地域用户的日期操作/存储的 Java 最佳实践的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经阅读了所有其他关于日期操作的问答,但似乎没有一个对我的担忧提供满意的答案.

I have read all of the other Q/A about Date Manipulation, but none of them seems to deliver a satisfactory answer to my concern.

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

I have a project with geographically diverse users which uses Date in some of its classes and data. The thing is that I am looking for an efficient way to manipulate the Dates for the different users in their respective timezone, most of the answers suggest using Joda library for Date manipulation, which quite don't understand yet because I still have not found any operation you cannot do with traditional Java, so if someone can explain what can I do with Joda that can't be done with traditional Java, then I may consider using it.

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

I finally came to the approach of using System.currentTimeMillis() to save my dates into the database (any database). This would avoid me to worry about what timezone is using the database to store the dates. If I want to query the database for an specific date or range of dates, I would perform the queries using the long value of the Date I want to query:

SELECT * FROM table1 WHERE date1>=1476653369000

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

And when retrieving a ResultSet I would then format the long value retrieved from database to a readable Date using the timezone of the user requesting the data.

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

根据我读过的一些意见,有些人强调存储System.currentTimeMillis() 绝对不是最佳实践,然而,出于某种原因,他们都错过了说WHY 不值得推荐.我错过了什么吗?这是否会导致转换 Long->Date/Date->Long 的性能问题?在数据库中使用 Long 代替 Date 时,是否有无法完成的用例?有人可以对此发表一个合理的解释吗?

According to some opinions I have read, some people say emphatically that storing System.currentTimeMillis() is definitely not the best practice, nevertheless, for some reason they all miss to say WHY it is not recommendable. Am I missing something? Does this cause a performance issue for the conversions Long->Date/Date->Long? Is there any use case that cannot be accomplished when using Long instead Date in database? Can someone post a rationale explanation about this?

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

In the other hand, assuming that I keep using Date values to store dates in database, is there a way to avoid worrying about time-zones while handling database Date?

提前致谢.

推荐答案

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

I have read all of the other Q/A about Date Manipulation

不,您肯定没有阅读了所有内容.

No, you certainly did not read them all.

  • 您会了解到旧的日期时间类(例如 java.util.Date & java.util.Calendar)和 Joda-Time项目被 java.time 类取代(在java.time"上搜索 1,890 个结果).
  • 您应该已经学会了不要将日期时间值作为从纪元开始的计数来跟踪.由于人类无法破译长整数作为日期时间的含义,因此调试和日志记录变得非常困难,因为错误未被发现.并且因为计数的许多粒度(整秒、毫秒、微秒、纳秒、整天等等)和至少一个 几十个时代用于各种软件项目,通过假设导致错误、误解和混淆,使您的数据产生歧义.
  • 您应该已经学会了在数据库中使用日期时间类型来跟踪日期时间值.
  • 您将学会以 UTC 格式工作和存储日期时间值.仅在逻辑要求或用户预期的演示时调整到时区.放眼全球,立足本土."
  • 您会了解到,虽然这是一项勇敢的行业首创努力,但遗留的日期时间类设计不当、令人困惑且麻烦.请参见 Java 日期和代码有什么问题时间 API? 进行一些讨论.Joda-Time 是业界第一个优秀的日期时间库,并启发了它的替代品,Java 8 及更高版本中内置的 java.time 类.
  • You would have learned that both the legacy date-time classes (such as java.util.Date & java.util.Calendar) and the Joda-Time project are supplanted by the java.time classes (1,890 results for search on 'java.time').
  • You would have learned not to track date-time values as a count-from-epoch. Debugging and logging becomes very difficult with bugs going undiscovered as humans cannot decipher the meaning of a long integer as a date-time. And because many granularities of counting (whole seconds, milliseconds, microseconds, nanoseconds, whole days, and more) and at least a couple dozen of epochs are employed in various software projects create ambiguity about your data with assumptions leading to errors, misinterpretation, and confusion.
  • You would have learned to use date-time types in your database to track date-time values.
  • You would have learned to work and store date-time values in UTC. Adjust into a time zone only where required by logic or as expected by the user for presentation. "Think global, present local."
  • You would have learned that while a valiant industry-first effort, the legacy date-time classes are poorly designed, confusing, and troublesome. See What's wrong with Java Date & Time API? for some discussion. Joda-Time was the first good date-time library in the industry, and inspired its replacement, the java.time classes built into Java 8 and later.

我会简短一些,因为 Stack Overflow 上已经多次介绍了所有这些内容.

I'll be somewhat brief as all of this has been covered many times already on Stack Overflow.

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

Work in UTC. In Java that means the Instant class is commonly used. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).

Instant instant = Instant.now();

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

Any serious database such as Postgres tracks date-time values in UTC. Your JDBC driver handles the detail of converting from database internally-stored data to a Java type. JDBC drivers that comply with JDBC 4.2 and later can directly handle java.time types via PreparedStatement::setObject & ResultSet::getObject methods.

myPreparedStatement.setObject( … , instant );

对于不合规的驱动程序,回退到使用 java.sql 类型(例如 java.sql.Timestamp)与数据库通信,并通过添加到的新方法与 java.time 类型相互转换旧课.数据库如何处理日期时间值的内部细节可能与 java.time 的处理方式大不相同.在大多数情况下,JDBC 驱动程序对您隐藏了所有细节.但一个关键问题是分辨率,您应该在数据库中研究它.java.time 类处理日期时间,分辨率高达 纳秒,但您的数据库可能不会.例如,Postgres 使用 微秒 的分辨率.所以来回意味着数据丢失.您想使用 java.time 类上的截断方法来匹配您的数据库.

For non-compliant drivers, fall back to using java.sql types such as java.sql.Timestamp to communicate with database, and convert to/from java.time types via new methods added to the old classes. The internal details of how the database handles date-time values may be quite different than how java.time does. For the most part the JDBC driver hides all the nitty-gritty details from you. But one critical issue is resolution, which you should study in your database. The java.time classes handle date-times with a resolution up to nanoseconds but your database may not. For example, Postgres uses a resolution of microseconds. So going back-and-forth means data-loss. You want to use the truncation methods on the java.time classes to match your database.

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

因此,不涉及时区.所以不必在处理数据库日期时担心时区".

So, no time zone involved. So no "worrying about time-zones while handling database Date".

当您想通过一个地区的挂钟时间的镜头看到同一时刻时,应用 ZoneId 以获得 ZonedDateTime.

When you want to see the same moment through the lens of a region’s wall-clock time, apply a ZoneId to get a ZonedDateTime.

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

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

When taking a zoned date-time back to the database, extract an Instant.

Instant instant = zdt.toInstant();

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

Be aware that for any given moment, the date as well as the time-of-day varies around the globe by time zone. So if an exact moment matters, such as when a contract expires, beware of using a date-only value. Either use a date-time value for the exact moment, or store the intended time zone alongside the date-only so the exact moment can be calculated later.

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 及更高版本中.这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date, .Calendar, &java.text.SimpleDateFormat.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.

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

The Joda-Time project, now in maintenance mode, advises migration to java.time.

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

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

从哪里获取 java.time 类?

Where to obtain the java.time classes?

  • Java SE 8SE 9 及更高版本
    • 内置.
    • 具有捆绑实现的标准 Java API 的一部分.
    • Java 9 添加了一些小功能和修复.

    ThreeTen-Extra 项目使用附加类扩展了 java.time.该项目是未来可能添加到 java.time 的试验场.您可能会在这里找到一些有用的类,例如 Interval, YearWeek, YearQuarter更多.

    The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

    这篇关于面向不同地域用户的日期操作/存储的 Java 最佳实践的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆