Instant 和 LocalDateTime 之间有什么区别?tl;博士不正确的推定InstantOffsetDateTimeZoneOffsetZoneIdZonedDateTime LocalDate, ,LocalTimeLocalDateTime 所有日期时间类型关于 java.time

2022-08-31 04:30:09

我知道那件事:

  • Instant是用于计算的“技术”时间戳表示(纳秒)。
  • LocalDateTime是日期/时钟表示,包括人类的时区

最终,IMO仍然可以作为大多数应用程序用例的类型。举个例子:目前我正在运行一个批处理作业,我需要根据日期计算下一次运行,我正在努力寻找这两种类型之间的优缺点(除了Instant的纳秒精度优势和LocalDateTime的时区部分)。

您能举出一些应用程序示例,其中只应使用Instant或LocalDateTime吗?

编辑:当心误读LocalDateTime关于精度和时区的文档


答案 1

Table of all date-time types in Java, both modern and legacy

tl;博士

Instant并且是两种完全不同的动物:一种代表一刻,另一种则不然。LocalDateTime

  • Instant表示一个时刻,时间轴中的特定点。
  • LocalDateTime表示日期和时间。但是,如果没有时区或与 UTC 的偏移量,此类无法表示时刻。它代表了大约26到27小时范围内的潜在时刻,这是全球所有时区的范围。值本质上是模棱两可的LocalDateTime

不正确的推定

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

您的陈述不正确:LocalDateTime 没有时区。没有时区是该类的全部意义所在。

引用该类的文档:

此类不存储或表示时区。相反,它是对日期的描述,用于生日,并结合了挂钟上看到的当地时间。如果没有其他信息(如偏移量或时区),它就无法表示时间线上的某个时刻。

所以的意思是“不划区,没有偏移”。Local…

Instant

enter image description here

即时UTC时间轴上的一个时刻,自1970 UTC第一个时刻以来的纳秒计数(基本上,请参阅类文档以获取细节)。由于大多数业务逻辑、数据存储和数据交换都应采用 UTC 格式,因此这是一个方便的类,可以经常使用。

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

OffsetDateTime

enter image description here

OffsetDateTime 类将某个时刻表示为日期和时间,其上下文在 UTC 之前或之后的小时-分钟-秒数。偏移量,即小时-分钟-秒数,由 ZoneOffset 类表示。

如果小时-分钟-秒数为零,则 OffsetDateTime 表示 UTC 中与即时相同的时刻。

ZoneOffset

enter image description here

ZoneOffset 类表示与 UTC 的偏移量,即 UTC 之前或 UTC 之后的小时-分钟-秒数。

A只是几小时-分-秒,仅此而已。区域要多得多,具有名称和偏移更改历史记录。因此,使用区域总是比仅使用偏移量更可取。ZoneOffset

ZoneId

enter image description here

时区ZoneId 类表示。

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

时区是一组规则,用于处理当地社区或地区实施的调整和异常。最常见的异常现象是被称为夏令时(DST)的太流行的疯子。

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

这些规则的变化比您预期的要频繁。请务必使日期时间库的规则(通常是“tz”数据库的副本)保持最新。在Java 8中保持最新状态比以往任何时候都更容易,Oracle发布了时区更新器工具

指定格式为 的正确时区名称,例如 、 、 或 。切勿使用2-4个字母的缩写,例如或因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。Continent/RegionAmerica/MontrealAfrica/CasablancaPacific/AucklandESTIST

时区 = 偏移量 + 调整规则

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

ZonedDateTime

enter image description here

从概念上讲,可以将 ZonedDateTime 视为一个已分配的 .InstantZoneId

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, ,LocalTimeLocalDateTime

Diagram showing only a calendar for a LocalDate.

Diagram showing only a clock for a LocalTime.

Diagram showing a calendar plus clock for a LocalDateTime.

“本地”日期时间类,LocalDateTimeLocalDateLocalTime,是一种不同类型的小动物。它们不与任何一个地方或时区相关联。它们与时间线无关。它们没有真正的意义,直到您将它们应用于某个位置以在时间轴上找到一个点。

这些类名中的“本地”一词可能对外行人有悖常理。这个词的意思是任何地方,或每个地方,但不是一个特定的地方。

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

那么我们什么时候会使用?在三种情况下:LocalDateTime

  • 我们希望在多个位置应用特定的日期和时间。
  • 我们正在预约。
  • 我们有一个预期但尚未确定的时区。

请注意,这三种情况都没有涉及时间轴上的某个特定点,这些都不是时刻。

一天中的一个时间,多个时刻

有时,我们希望在某个日期表示一天中的某个时间,但希望将其应用于跨时区的多个位置。

例如,“圣诞节从 2015 年 12 月 25 日午夜开始”是 .午夜在巴黎和蒙特利尔的不同时刻发生,在西雅图奥克兰也有所不同。LocalDateTime

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

另一个例子,“Acme公司有一项政策,即午餐时间从全球每个工厂的下午12:30开始”是一个.要获得真正的意义,您需要将其应用于时间轴,以计算斯图加特工厂的12:30或拉巴特工厂的12:30或悉尼工厂的12:30的时刻。LocalTime

预约

另一种要使用的情况是预订未来的活动(例如:牙医预约)。这些任命在未来可能足够遥远,以至于你可能会冒着政治家重新定义时区的风险。政客们往往很少预先警告,甚至根本没有警告。如果你的意思是“明年1月23日下午3点”,无论政客们如何玩弄时钟,那么你不能记录一个时刻 - 例如,如果该地区采用或放弃夏令时,下午3点会变成下午2点或4点。LocalDateTime

对于约会,将 a 和 a 存储为单独的存储。稍后,在生成计划时,通过调用来生成对象来动态确定时刻。LocalDateTimeZoneIdLocalDateTime::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。从 中提取 。InstantZonedDateTime

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

未知区域

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

我认为这种情况不恰当和不明智。如果区域或偏移是有意的,但未确定,则数据有错误。这就像在不知道预期货币(美元,英镑,欧元等)的情况下存储产品的价格一样。不是一个好主意。

所有日期时间类型

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

Table of all date-time types in Java (both modern & legacy) as well as SQL standard.

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

但不要担心。我们可以很容易地来回转换。

转换。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.DateCalendarSimpleDateFormat

要了解更多信息,请参阅 Oracle 教程。搜索 Stack Overflow 以获取许多示例和解释。规格是JSR 310

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

您可以直接与数据库交换 java.time 对象。使用符合 JDBC 4.2 或更高版本的 JDBC 驱动程序。不需要字符串,不需要类。Hibernate 5 & JPA 2.2 支持 java.timejava.sql.*

从哪里获取 java.time 类?

Table of which java.time library to use with which version of Java or Android

ThreeTen-Extra 项目通过其他类扩展了 java.time。这个项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuarter


答案 2

一个主要区别是 的部分。如果您居住在德国并创建了一个实例,而其他人居住在美国并在同一时刻创建了另一个实例(前提是时钟设置正确), 则这些对象的值实际上会有所不同。这不适用于 独立于时区的 计算。LocalLocalDateTimeLocalDateTimeInstant

LocalDateTime存储不带时区的日期和时间,但它的初始值取决于时区。的不是。Instant

此外,还提供了用于操作日期组件(如天,小时,月)的方法。An 没有。LocalDateTimeInstant

除了 Instant 的纳秒精度优势和 LocalDateTime 的时区部分

这两个类具有相同的精度。 不存储时区。仔细阅读javadocs,因为你可能会犯一个重大的错误,这些无效的假设:InstantLocalDateTimeLocalDateTime