我已经阅读了关于日期操作的所有其他问答,但是他们似乎都没有给我一个满意的答案。
我有一个地理位置不同的用户项目,在其某些类和数据中使用Date
。问题是我正在寻找一种有效的方法来操纵各自时区中不同用户的日期,大多数答案建议使用Joda库进行Date
操作,这完全不明白但是因为我还没有找到任何传统Java无法执行的操作,所以如果有人能解释我用Joda无法用传统Java做什么,那么我可以考虑使用它。
我终于找到了使用System.currentTimeMillis()
将日期保存到数据库(任何数据库)的方法。这样可以避免让我担心使用数据库存储日期的时区。如果我想查询数据库中的特定日期或日期范围,我会使用我要查询的long
的{{1}}值来执行查询:
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
时担心时区?
提前致谢。
答案 0 :(得分:23)
我已阅读有关日期操作的所有其他问答
不,你确实没有全部阅读。
java.util.Date
& java.util.Calendar
)和Joda-Time项目都被java.time类取代(搜索结果为1,890条) on' java.time')。 我有点简短,因为所有这些已经在Stack Overflow上已经涵盖很多次
次。以UTC身份工作。在Java中,这意味着常用Instant
类。 Instant
类代表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.Timestamp
之类的java.sql类型与数据库通信,并通过添加到旧类的新方法转换为/从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();
请注意,在任何特定时刻,日期和时间在全球各个时区都有所不同。因此,如果确切的时刻很重要,例如合同到期时,请注意使用仅限日期的值。要么使用确切时刻的日期时间值,要么将预期时区与日期一起存储,以便稍后计算确切时刻。
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 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,.Calendar
和& java.text.SimpleDateFormat
现在位于Joda-Time的maintenance mode项目建议迁移到java.time。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。
答案 1 :(得分:2)
我可以用Joda做些什么来完成传统的Java
在一般情况下,它并不是关于传统Java能做什么或不做什么的。它更多地是关于库API如何使您编写比传统Java更好(更健壮和更正确)的代码。
从Java 8开始,Joda API或多或少被复制/采用,只更改了包名称并将其合并到Java 8 SE标准库中。
因此,如果您使用的是Java 8,那么您应该选择新的API,如果没有,您应该考虑使用Joda至少可以为您提供一条平滑的升级/移植到Java 8的途径。
一些例子:
toString()
这样的内容,因此序列化/反序列化可以用最少的工作量正常工作。Instant
代表'绝对'的概念使用地理分布式系统时(当系统默认时钟/时区和DST规则可能不同时)或互操作因为它使用UTC时有用的时间戳。编辑添加:
根据我读过的一些观点,有些人强调说,存储
System.currentTimeMillis()
绝对不是最佳做法,但是,出于某种原因,他们都错过了为什么不推荐这样做。我错过了什么吗?
System.currentTimeMillis()
有一些缺点。最大的缺点是时钟类型定义不明确。它可能是一个单调时钟,它可能是受DST和时区影响的时钟,也可能是UTC时间。它也不一定是精确的时钟,实际上并不能保证精确到毫秒级。无论发生什么事情都可以作为当前时间在当前时间的外表,基本上。
这意味着,如果您想使用多个服务器来处理传入的请求,那么当您必须考虑在服务器System.currentTimeMillis()
的上下文中使用A
的输出时,它会变得棘手。第二天,你的程序在不同的服务器B
上运行,比如说。