Java面向不同地理位置用户的数据处理/存储最佳实践

arknldoa  于 2023-11-15  发布在  Java
关注(0)|答案(2)|浏览(121)

我已经阅读了所有其他关于日期操纵的问答,但似乎没有一个对我的担忧给出满意的答案。
我有一个项目,地理位置不同的用户在它的一些类和数据中使用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时担心时区?
先谢了。

x8diyxa7

x8diyxa71#

我已阅读有关日期操纵的所有其他问答
不,你肯定没有把它们都读完。

  • 您可能已经了解到,遗留的日期-时间类(如java.util.Datejava.util.Calendar)和Joda-Time项目都被java.time类取代了(在“java. time”上搜索1,890个结果)。
  • 您可能已经学会了不要将日期-时间值作为一个从纪元开始的计数来跟踪。由于人类无法破译一个长整数作为日期-时间的含义,调试和日志记录会变得非常困难,因为bug会被发现。(整秒、毫秒、微秒、纳秒、整天、以及更多),至少有几十个纪元被用在各种软件项目中,用导致错误,误解和混乱的假设来创建关于数据的模糊性。
  • 您应该已经学会了在数据库中使用日期时间类型来跟踪日期时间值。
  • 您可能已经学会了使用UTC来工作和存储日期-时间值。仅在逻辑要求或用户期望的情况下才调整时区以进行演示。“考虑全局,演示本地”。
  • 您可能已经了解到,虽然这是业界第一个大胆的尝试,但遗留的日期-时间类设计得很差,令人困惑,而且麻烦。有关讨论,请参阅What's wrong with Java Date & Time API?。Joda-Time是业界第一个很好的日期-时间库,并启发了它的替代品,即内置于Java 8和更高版本中的java.time类。

我将稍微简短一点,因为所有这些都已经在堆栈溢出中讨论了 * 很多 * 次。
在UTC中工作。在Java中,这意味着通常使用Instant类。Instant类以UTC表示时间轴上的时刻,分辨率为nanoseconds(最多九(9)位小数)。

Instant instant = Instant.now();

字符串
任何严肃的数据库(如Posterre)都可以跟踪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类处理的日期时间的分辨率最高为nanoseconds,但你的数据库可能不是。例如,Posterre使用的分辨率为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();


请注意,对于任何给定的时刻,日期和一天中的时间在地球仪各地因时区而异。因此,如果某个确切的时刻很重要,例如合同到期时,请注意不要使用仅日期值。要么为确切的时刻使用日期-时间值,要么将所需的时区与仅日期值一起存储,以便以后可以计算确切的时刻。

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.时间

java.time框架内置于Java 8和更高版本中。这些类取代了麻烦的旧的legacy日期-时间类,如java.util.Date.Calendarjava.text.SimpleDateFormat
Joda-Time项目(现在在maintenance mode中)建议迁移到java. time。
要了解更多信息,请参阅Oracle Tutorial。并搜索堆栈溢出以获得许多示例和解释。规范是JSR 310
在哪里可以获得java.time类?

*Java SE 8SE 9及更高版本

  • 内建的。
  • 标准Java API的一部分,具有捆绑的实现。
  • Java 9添加了一些次要的特性和修复。
    *Java SE 6SE 7
  • 大多数java.time功能都是在ThreeTen-Backport中向后移植到Java 6和7的。
  • Android
  • ThreeTenABP项目专门针对Android调整了 ThreeTen-Backport(如上所述)。
  • 请参阅How to use…

ThreeTen-Extra项目使用其他类扩展了java.time。此项目是将来可能添加到java. time的试验场。您可能会在此处找到一些有用的类,如IntervalYearWeekYearQuartermore

3zwtqj6y

3zwtqj6y2#

我能用Joda做什么传统Java做不到的事情
这并不是说在一般情况下你能或不能用传统Java做什么,而是说库API如何工作,使你比传统Java更容易编写更好(更健壮和正确)的代码。
如此之多,以至于到Java 8时,Joda API或多或少被逐字复制/采用,只是改变了包名并合并到Java 8 SE标准库中。
因此,如果你正在使用Java 8,你应该选择新的API,如果不是,你应该考虑使用Joda至少会为你升级/移植到Java 8铺平道路。
举几个例子:

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

编辑以添加:
根据我读到的一些观点,有些人强调存储System.currentTimeMillis()绝对不是最佳实践,然而,出于某种原因,他们都没有说为什么它是不可存储的。
System.currentTimeMillis()有几个缺点。最大的缺点是时钟的类型定义不清。它可能是单调时钟,可能是受DST和时区影响的时钟,也可能是UTC时间。它也不一定是准确的时钟,它实际上并不能保证精确到毫秒。只是无论什么碰巧是手的东西,将工作作为一个语义的电流现在的时间,基本上。
这意味着,如果你想使用多个服务器来处理传入的请求,比如说,当你必须考虑在第二天在不同的服务器B上运行程序的上下文中处理来自服务器ASystem.currentTimeMillis()的输出时,这就变得很棘手了。

相关问题