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

标签 java database date datetime

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

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

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

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 ?

提前致谢。

最佳答案

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



不,您当然没有全部阅读。
  • 您会了解到遗留日期时间类(例如 java.util.Datejava.util.Calendar )和 Joda-Time 项目都被 java.time 类(搜索“java.time”的 1,890 个结果)所取代。
  • 您将学会不要将日期时间值作为从纪元开始的计数来跟踪。由于人类无法破译长整数作为日期时间的含义,因此调试和日志记录变得非常困难,因为错误未被发现。并且因为计数的许多粒度(整秒、毫秒、微秒、纳秒、整日等等)和至少 couple dozen of epochs用于各种软件项目的假设会导致您的数据产生歧义,从而导致错误、误解和混淆。
  • 您将学会在数据库中使用日期时间类型来跟踪日期时间值。
  • 您将学会使用 UTC 工作和存储日期时间值。仅在逻辑要求或用户预期的演示时调整到时区。 “放眼全局,立足本土。”
  • 您会了解到,虽然这是一项勇敢的行业首创努力,但遗留的日期时间类设计不佳、令人困惑且麻烦。见 What's wrong with Java Date & Time API?进行一些讨论。 Joda-Time 是业界第一个优秀的日期时间库,并启发了它的替代品,Java 8 及更高版本中内置的 java.time 类。

  • 我会稍微简短一些,因为所有这些都已经在 Stack Overflow 上讲过很多次了。

    在 UTC 工作。在 Java 中,这意味着 Instant类是常用的。 Instant class 代表时间线上的一个时刻 UTC分辨率为 nanoseconds (最多九 (9) 位小数)。
    Instant instant = Instant.now();
    

    任何严肃的数据库(例如 Postgres)都以 UTC 跟踪日期时间值。您的 JDBC 驱动程序处理从数据库内部存储的数据转换为 Java 类型的细节。符合 JDBC 4.2 的 JDBC 驱动程序以后可以通过 PreparedStatement::setObject 直接处理 java.time 类型& ResultSet::getObject方法。
    myPreparedStatement.setObject( … , instant );
    

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

    因此,不涉及时区。所以不用“在处理数据库日期时担心时区”。

    当您想通过一个地区的镜头 wall-clock time 看到同一时刻时, 申请 ZoneId获得 ZonedDateTime .
    ZoneId z = ZoneId.of( "Asia/Kolkata" );
    ZonedDateTime zdt = instant.atZone( z );
    

    将分区日期时间带回数据库时,提取 Instant .
    Instant instant = zdt.toInstant();
    

    请注意,对于任何给定时刻,全局各地的日期和时间都会因时区而异。因此,如果某个确切时刻很重要,例如契约(Contract)到期时,请注意使用仅限日期的值。要么使用确切时刻的日期时间值,要么将预期时区与仅日期一起存储,以便稍后可以计算确切时刻。
    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 .

    Joda-Time项目,现在在 maintenance mode , 建议迁移到 java.time。

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

    从哪里获得 java.time 类?
  • Java SE 8SE 9然后
  • 内置。
  • 具有捆绑实现的标准 Java API 的一部分。
  • Java 9 添加了一些小功能和修复。
  • Java SE 6SE 7
  • ThreeTen-Backport 中,大部分 java.time 功能被反向移植到 Java 6 和 7。 .
  • Android
  • ThreeTenABP项目适应 ThreeTen-Backport (如上所述)专门针对Android。
  • How to use… .

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

    关于面向不同地域用户的日期操作/存储的 Java 最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40075780/

    相关文章:

    java - NoClassDefFoundError : JavacProcessingEnvironment at runtime after tomcat 8 upgrade

    java - 从 java.sql.date 获取日月年

    javascript - JavaScript 中的日期格式转换

    javascript - 在日期对象中插入时间戳时出现错误的日期输出 (JavaScript)

    json - 向 Grails 发送日期时的 GORM 默认日期格式

    java - 关于java中的非静态类

    java - 运行时中的远程 SSH 命令退出并显示代码 255

    database - BCNF分解?

    java - Android 数据库 SQLite 日期不能为空

    javascript - 如何将此自定义对象添加到 firebase