一尘不染

Java Instant和LocalDateTime有什么区别?

java

我知道:

即时是用于计算的“技术”时间戳表示(纳秒)。
LocalDateTime是日期/时钟表示形式,包括人类的时区。
最后,对于大多数应用程序用例,IMO都可以视为两种类型。例如:当前我正在运行一个批处理作业,我需要根据日期计算下一次运行,并且我正在努力寻找这两种类型之间的优缺点(除了Instant和时区部分的纳秒级精度优势)的LocalDateTime)。

您可以列举一些应用示例,仅应使用Instant或LocalDateTime吗?

编辑:提防有关精度和时区的LocalDateTime的误读文档


阅读 759

收藏
2020-02-29

共1个答案

一尘不染

tl; dr

InstantLocalDateTime是两个完全不同的动物:一个表示了一下,其他没有。

  • Instant 代表时刻,是时间轴中的特定点。
  • LocalDateTime代表日期和时间。但是由于缺少时区或UTC偏移量,因此此类无法表示一个时刻。它代表了大约26到27小时(全球所有时区的范围)内的潜在时刻。

Incorrect Presumption

LocalDateTime 而是日期/时钟表示,包括人类的时区。

你的说法是不正确:一个LocalDateTime有没有时区。没有时区是该课程的重点。

引用该类的文档:

此类不存储或表示时区。相反,它是对用于生日的日期的描述,以及在墙上时钟上看到的本地时间。如果没有其他信息(例如偏移或时区),则无法在时间线上表示时刻。

因此Local…意味着“不分区,不偏移”。

Instant

An Instant是UTC时间轴上的一刻,是自1970年UTC的第一刻的纪元以来的十亿分之一秒(基本上,请参阅class doc了解更多细节)。由于大多数业务逻辑,数据存储和数据交换都应使用UTC,因此这是经常使用的方便类。

Instant instant = Instant.now() ;  // Capture the current moment in UTC.

OffsetDateTime

class OffsetDateTime类将时刻表示为日期和时间,上下文比UTC早或晚一些小时-分钟-秒。偏移量(小时-分钟-秒的数量)由此ZoneOffset类表示。

如果时分秒数为零,则OffsetDateTime表示UTC中与相同的时刻Instant

ZoneOffset

所述ZoneOffset类表示偏移从-UTC,若干小时-分钟-秒比UTC或后面UTC。

A ZoneOffset仅是小时-分钟-秒的数量,仅此而已。区域更多,具有名称和偏移更改的历史记录。因此,使用区域总是优于仅使用偏移量。

ZoneId

在此处输入图片说明

阿时区是由表示ZoneId类。

例如,巴黎的新黎明比蒙特利尔早。因此,我们需要移动时钟指针以更好地反映给定区域的正午(当太阳直接在头顶上方)。西欧/非洲的UTC线向东/向西越远,偏移量越大。

时区是一组规则,用于处理本地社区或区域所实施的调整和异常情况。最常见的异常是称为“ 夏令时(DST)”的太流行的疯狂。

时区具有过去规则,当前规则和在不久的将来已确认的规则的历史。

这些规则更改的频率超出你的预期。确保保持日期时间库的规则(通常是’tz’数据库的副本)是最新的。通过Oracle发布Timezone Updater Tool,在Java 8中保持最新状态比以往更加容易。

指定适当的时区名称,格式Continent/Region,如America/Montreal,Africa/CasablancaPacific/Auckland。切勿使用2-4字母的缩写,例如EST或,IST因为它们不是真实的时区,不是标准化的,甚至不是唯一的(!)。

Time Zone = Offset + Rules of Adjustments

ZoneId z = ZoneId.of( “Africa/Tunis” ) ; 

ZonedDateTime

在此处输入图片说明

在ZonedDateTime概念上将其视为Instant具有分配的对象ZoneId。

ZonedDateTime = ( Instant + ZoneId )

要捕获特定区域(时区)的人们在挂钟时间中看到的当前时刻,请执行以下操作:

ZonedDateTime zdt = ZonedDateTime.now( z ) ;  // Pass a `ZoneId` object such as `ZoneId.of( "Europe/Paris" )`. 

几乎所有的后端,数据库,业务逻辑,数据持久性,数据交换都应使用UTC。但是要向用户演示,你需要调整到用户期望的时区。这是用于生成这些日期时间值的String表示形式的ZonedDateTime类和格式化程序类的目的。

ZonedDateTime zdt = instant.atZone( z ) ;
String output = zdt.toString() ;                 // Standard ISO 8601 format.

你可以使用生成本地化格式的文本DateTimeFormatter。

DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( Locale.CANADA_FRENCH ) ; 
String outputFormatted = zdt.format( f ) ;

mardi 30 avril 2019à23 h 22 min 55 s heure de l'Inde

LocalDate,LocalTime,LocalDateTime

“本地”的日期时间类,LocalDateTime,LocalDate,LocalTime,有一种别样的小动物的。不受任何地区或时区的限制。它们不受时间限制。除非你将它们应用于某个地方以在时间轴上找到一个点,否则它们没有真正的意义。

这些类名称中的“本地”一词可能与未使用的单词相反。该词表示任何地点,或每个地点,但不是特定地点。

因此,对于商务应用程序,“本地”类型不常用,因为它们仅表示可能的日期或时间的一般概念,而不是时间轴上的特定时刻。商业应用程序倾向于关心发票到达的确切时间,运送产品的运输,雇用员工或出租车离开车库的确切时间。因此,业务应用程序开发人员最常使用InstantZonedDateTime分类。

那我们LocalDateTime什么时候用?在以下三种情况下:我们要在多个位置应用特定的日期和时间,我们要预约的位置,或者我们有一个尚未确定的时区。请注意,这三种情况都不是时间轴上的某个特定点,所有这些都不是片刻。

One time-of-day, multiple moments

有时我们想代表某个日期的某个特定时间,但又想将其应用于跨时区的多个地区。

例如,“圣诞节从2015年12月25日午夜开始”是一个LocalDateTime。巴黎的午夜罢工与蒙特利尔的罢工时间不同,西雅图和奥克兰的午夜罢工又有所不同。

LocalDate ld = LocalDate.of( 2018 , Month.DECEMBER , 25 ) ;
LocalTime lt = LocalTime.MIN ;   // 00:00:00
LocalTime ldt = LocalDateTime.of( ld , lt ) ;  // Xmas morning anywhere. 

另一个示例是“ Acme公司有一项政策,即在全球每个工厂的午餐时间开始于12:30 PM” LocalTime。要具有实际意义,你需要将其应用于时间轴,以计算斯图加特工厂的12:30时刻或拉巴特工厂的12:30时刻或悉尼工厂的12:30时刻。

预约预约

使用的另一种情况LocalDateTime是用于预订将来的事件(例如:牙医约会)。这些任命将来可能远远不够,你可能会冒着政治家重新定义时区的风险。政客们很少发出警告,甚至根本没有警告。如果你的意思是“明年1月23日下午3点”,无论政客如何打钟,那么你就无法记录时刻-如果该地区采用或取消了夏时制,那将导致下午3点变成下午2点或下午4点,例如。

对于约会,请分别存储a LocalDateTimea ZoneId。稍后,在生成时间表时,通过调用LocalDateTime::atZone( ZoneId )生成ZonedDateTime对象来即时确定时刻。

ZonedDateTime zdt = ldt.atZone( z ) ;  // Given a date, a time-of-day, and a time zone, determine a moment, a point on the timeline.

如果需要,你可以调整为UTC。Instant从中提取ZonedDateTime

Instant instant = zdt.toInstant() ;  // Adjust from some zone to UTC. Same moment, same point on the timeline, different wall-clock time.

未知区域

某些人可能会LocalDateTime在时区或偏移量未知的情况下使用。

我认为这种情况是不适当和不明智的。如果要确定区域或偏移量但不确定,则数据有误。这就像在不知道预期货币的情况下存储产品的价格一样。这不是一个好主意。

所有日期时间类型

为了完整起见,这是一张表,列出了所有可能的日期时间类型,包括Java的现代和旧式以及SQL标准定义的日期和时间。这可能有助于将Instant&LocalDateTime类放在更大的上下文中。

Java(现代和传统)以及SQL标准中所有日期时间类型的表格。

注意Java团队在设计JDBC 4.2时所做出的奇怪选择。他们选择支持所有java.time时间……,除了两个最常用的类:Instant&ZonedDateTime

但是不用担心。我们可以轻松地来回转换。

正在转换Instant

// Storing
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
myPreparedStatement.setObject( … , odt ) ;

// Retrieving
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
Instant instant = odt.toInstant() ;

正在转换ZonedDateTime。

// Storing
OffsetDateTime odt = zdt.toOffsetDateTime() ;
myPreparedStatement.setObject( … , odt ) ;

// Retrieving
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = odt.atZone( z ) ; 

关于java.time

该java.time框架是建立在Java 8和更高版本。这些类取代麻烦的老传统日期时间类,如java.util.DateCalendar,和SimpleDateFormat

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

要了解更多信息,请参见Oracle教程。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310。

你可以直接与数据库交换java.time对象。使用与JDBC 4.2或更高版本兼容的JDBC驱动程序。不需要字符串,不需要类。java.sql.*

在哪里获取java.time类?

  • Java SE 8, Java SE 9, Java SE 10和更高版本
  • 内置的
  • 标准Java API的一部分,具有捆绑的实现。
  • Java 9添加了一些次要功能和修复。
  • Java SE 6和 Java SE 7
  • java.time的许多功能在ThreeTen- Backport中都被反向移植到Java 6和7 。
  • 安卓系统
  • 更高版本的Android捆绑了java.time类的实现。
  • 对于早期的Android(<26),ThreeTenABP项目改编了ThreeTen-Backport(如上所述)。请参阅如何使用ThreeTenABP…。

该ThreeTen-额外项目与其他类扩展java.time。该项目是将来可能向java.time添加内容的试验场。你可能在这里找到一些有用的类,比如Interval,YearWeek,YearQuarter,和更多。

2020-02-29