Instant 和 LocalDateTime 有什么区别?

What's the difference between Instant and LocalDateTime?

我知道:

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

您能举出一些应用示例吗?在这些示例中,只应使用 Instant 或 LocalDateTime?

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

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

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

此外,LocalDateTime 提供了操作日期组件(如天、小时、月)的方法。 Instant 没有。

apart from the nanosecond precision advantage of Instant and the time-zone part of LocalDateTime

两者 类 具有相同的精度。 LocalDateTime 不存储时区。彻底阅读 javadocs,因为你可能会因这样无效的假设而犯下大错:Instant and LocalDateTime.

关于 LocalDateTime 你错了:它不存储任何时区信息并且它具有纳秒精度。引用 Javadoc(强调我的):

A date-time without a time-zone in the ISO-8601 calendar system, such as 2007-12-03T10:15:30.

LocalDateTime is an immutable date-time object that represents a date-time, often viewed as year-month-day-hour-minute-second. Other date and time fields, such as day-of-year, day-of-week and week-of-year, can also be accessed. Time is represented to nanosecond precision. For example, the value "2nd October 2007 at 13:45.30.123456789" can be stored in a LocalDateTime.

两者之间的区别在于 Instant 代表与纪元 (01-01-1970) 的偏移量,因此代表时间线上的特定时刻。在同一时刻在地球的两个不同位置创建的两个 Instant 对象将具有完全相同的值。

tl;博士

InstantLocalDateTime是两种完全不同的动物:一个代表瞬间,另一个不代表。

  • Instant代表一个时刻,时间轴上的一个特定点。
  • LocalDateTime 表示日期和时间。但是缺少时区或与 UTC 的偏移量,这个 class 不能 代表一个时刻 。它代表 潜在 时刻,时间跨度约为 26 到 27 小时,是全球所有时区的范围。 LocalDateTime 本质上是不明确的

假设不正确

LocalDateTime is rather date/clock representation including time-zones for humans.

您的陈述不正确:A LocalDateTime 没有 时区。没有时区是 class.

的全部意义所在

引用 class' 文档:

This class does not store or represent a time-zone. Instead, it is a description of the date, as used for birthdays, combined with the local time as seen on a wall clock. It cannot represent an instant on the time-line without additional information such as an offset or time-zone.

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

Instant

自 1970 年世界标准时间第一时刻以来的一个 Instant is a moment on the timeline in UTC, a count of nanoseconds(基本上,请参阅 class 文档了解细节)。由于您的大部分业务逻辑、数据存储和数据交换都应该使用 UTC,因此这是一个经常使用的便利 class。

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

OffsetDateTime

class OffsetDateTime class represents a moment as a date and time with a context of some number of hours-minutes-seconds ahead of, or behind, UTC. The amount of offset, the number of hours-minutes-seconds, is represented by the ZoneOffset class.

如果小时-分钟-秒数为零,OffsetDateTime represents a moment in UTC the same as an Instant

ZoneOffset

ZoneOffset class represents an offset-from-UTC,比 UTC 早或晚 UTC 的小时-分钟-秒数。

一个ZoneOffset仅仅是小时-分钟-秒的数字,仅此而已。区域远不止于此,它具有名称和偏移量更改历史记录。因此,使用区域总是比仅使用偏移更可取。

ZoneId

A time zone is represented by the ZoneId class.

对于给定区域,Paris than in Montréal, for example. So we need to move the clock’s hands to better reflect noon(当太阳直接在头顶上方时)新的一天黎明更早。 eastward/westward离UTC线越远Europe/Africa偏移量越大

时区是一组用于处理当地社区或地区实施的调整和异常情况的规则。最常见的异常是被称为 Daylight Saving Time (DST).

的非常流行的疯狂行为

一个时区有过去的规则、现在的规则和为不久的将来确认的规则。

这些规则的变化比您预期的要频繁。请务必遵守日期时间库的规则,通常是 the 'tz' database, up to date. Keeping up-to-date is easier than ever now in Java 8 with Oracle releasing a Timezone Updater Tool.

的副本

Continent/Region的格式指定一个proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 2-4 个字母的缩写,例如 ESTIST,因为它们 不是 真正的时区,不是标准化的,甚至不是唯一的(!)。

Time Zone = Offset + Rules of Adjustments

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

ZonedDateTime

在概念上将 ZonedDateTime 视为具有分配的 ZoneIdInstant

ZonedDateTime = ( Instant + ZoneId )

捕捉特定地区(时区)人们使用的挂钟时间中的当前时刻:

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

几乎所有的后端、数据库、业务逻辑、数据持久化、数据交换都应该使用 UTC。但是为了向用户展示,您需要调整到用户期望的时区。这就是 ZonedDateTime class 和 formatter classes 用于生成这些日期时间值的字符串表示形式的目的。

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

LocalDateLocalTimeLocalDateTime

“本地”日期时间 classes, LocalDateTime, LocalDate, LocalTime ,是另一种生物。它们与任何一个地方或时区无关。它们不受时间线的约束。 它们没有真正的意义,除非您将它们应用到某个地点以在时间轴上找到一个点。

这些 class 名称中的“本地”一词对于外行来说可能有悖常理。这个词的意思是任何地区,或每个地区,但不是特定地区。

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

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

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

请注意,这三个案例中 none 涉及时间轴上的某个特定点,其中 none 个是片刻。

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

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

例如,“圣诞节从 2015 年 12 月 25 日午夜开始”是 LocalDateTime。巴黎午夜敲响的时间与蒙特利尔不同,Seattle and in Auckland

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 下午开始”是 LocalTime。要具有实际意义,您需要将其应用于时间轴以计算 12:30 在 Stuttgart factory or 12:30 at the Rabat factory or 12:30 at the Sydney 工厂的时刻。

预约

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

对于约会,存储一个 LocalDateTime 和一个 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。从 ZonedDateTime.

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

未知区域

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

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

所有日期时间类型

为了完整起见,这里是 table 所有可能的日期时间类型,包括 Java 中的现代和遗留类型,以及 SQL 标准定义的类型。这可能有助于将 InstantLocalDateTime class 置于更大的上下文中。

请注意 Java 团队在设计 JDBC 4.2 时做出的奇怪选择。他们选择支持所有 java.time 次……除了两个最常用的 classes: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 framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

您可以直接与您的数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* classes。 Hibernate 5 和 JPA 2.2 支持 java.time.

在哪里获取 java.time classes?

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

LocalDateTime 没有 time-zone 信息:一个 LocalDateTime 可能代表世界各地不同机器的不同时刻。所以你不应该尝试将它与隐式 time-zone (系统默认的)一起使用。您应该根据它所代表的含义来使用它,例如“New-year 是 1 月 1 日,在 0:00”:这意味着地球上所有点的时间都不同,但在这种情况下需要它。

即时是格林威治time-zone的一个时间点。例如,除了用户的 time-zone 之外,还可以使用它来显示 him/her his/her time-zone 会议的开始。

如果这两个 class 不能代表您想要的 store/exchange,那么 ZonedDateTime 或另一个 class 可能会做得更好。

这是一个简单的合成模式,用于获取 java.time 包中 classes 的 big-picture 以及它们与用于可靠且轻松交换的 ISO-8601 标准的关系Java 与其他语言或框架之间的日期和时间:

架构在此处详细解释:http://slaout.linux62.org/java-date-time/