ChatGPT解决这个技术问题 Extra ChatGPT

Instant 和 LocalDateTime 有什么区别?

我知道:

Instant 是用于计算的“技术”时间戳表示(纳秒)。

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

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

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

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

Instant 更基本,包含 UTC 的标准 long。对于像批处理这样的 cron 来说不是那么合乎逻辑的选择。
不正确的定义。 LocalDateTime没有有时区!

W
William Lindblom

https://i.stack.imgur.com/QPhGW.png

tl;博士

InstantLocalDateTime 是两种完全不同的动物:一种代表片刻,另一种不代表。

Instant 代表时刻,时间线中的特定点。

LocalDateTime 表示日期和时间。但是由于缺少时区或与 UTC 的偏移量,此类不能代表片刻。它代表了大约 26 到 27 小时范围内的潜在时刻,即全球所有时区的范围。 LocalDateTime 值本质上是不明确的。

不正确的推定

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

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

引用该类的文档:

此类不存储或表示时区。相反,它是对日期的描述,用于生日,结合挂钟上的当地时间。如果没有诸如偏移或时区之类的附加信息,它就不能代表时间线上的瞬间。

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

立即的

https://i.stack.imgur.com/9c9c8.png

InstantUTC 中时间轴上的一个时刻,自 1970 UTC 第一时刻的纪元以来计数为 nanoseconds(基本上,请参阅课程文档以获取详细信息)。由于您的大部分业务逻辑、数据存储和数据交换都应该使用 UTC,因此这是一个经常使用的方便类。

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

偏移日期时间

https://i.stack.imgur.com/D3rQ0.png

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

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

区域偏移

https://i.stack.imgur.com/uw7es.png

ZoneOffset 类表示 offset-from-UTC,比 UTC 早或晚 UTC 小时-分钟-秒数。

ZoneOffset 只是几个小时-分钟-秒,仅此而已。一个区域要多得多,它有一个名称和一个偏移变化的历史。因此,使用区域总是比仅使用偏移量更可取。

区号

https://i.stack.imgur.com/fIsUK.jpg

time zoneZoneId 类表示。

例如,新的一天在 Paris 中比在 Montréal 中更早出现。因此,我们需要移动时钟的指针以更好地反映给定区域的 noon(当太阳直接位于头顶时)。从西欧/非洲的 UTC 线向东/向西越远,偏移量越大。

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

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

这些规则的变化比您预期的要频繁。请务必使您的日期时间库的规则(通常是 the 'tz' database 的副本)保持最新。在 Java 8 中,随着 Oracle 发布了 Timezone Updater Tool,现在比以往任何时候都更容易保持最新状态。

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

时区 = 偏移量 + 调整规则

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

分区日期时间

https://i.stack.imgur.com/2j5Ui.png

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

ZonedDateTime = ( Instant + ZoneId )

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

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

几乎所有的后端、数据库、业务逻辑、数据持久性、数据交换都应该使用 UTC。但是为了向用户展示,您需要调整到用户期望的时区。这是 ZonedDateTime 类和 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 ) ;

狂欢节 2019 年 4 月 30 日 à 23 h 22 min 55 s heure de l'Inde

本地日期、本地时间、本地日期时间

https://i.stack.imgur.com/PJSsX.jpg

https://i.stack.imgur.com/WXBy8.jpg

https://i.stack.imgur.com/cmEdq.png

“本地”日期时间类 LocalDateTimeLocalDateLocalTime 是一种不同的生物。它们不依赖于任何一个地区或时区。它们与时间线无关。 它们没有真正的意义,除非您将它们应用到某个地方以在时间轴上找到一个点。

这些类名中的“Local”这个词对于外行来说可能是违反直觉的。这个词的意思是任何地方,或每一个地方,但不是一个特定的地方。

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

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

我们希望在多个位置应用特定的日期和时间。

我们正在预约。

我们有一个预定但尚未确定的时区。

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

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

有时我们想表示某个日期的某个时间,但希望将其应用于跨时区的多个地点。

例如,“圣诞节从 2015 年 12 月 25 日午夜开始”是一个 LocalDateTime。巴黎的午夜罢工时间与蒙特利尔不同,SeattleAuckland 也不同。

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。要获得真正的意义,您需要将其应用到时间线以计算出 Stuttgart 工厂的 12:30 或 Rabat 工厂的 12:30 或 Sydney 工厂的 12:30 的时刻。

预约

使用 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

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

所有日期时间类型

为了完整起见,这里是所有可能的日期时间类型的表,包括 Java 中的现代和传统类型,以及 SQL 标准定义的类型。这可能有助于放置 Instant & LocalDateTime 更大范围内的类。

https://i.stack.imgur.com/GRtXz.png

请注意 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 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendar 和 & SimpleDateFormat

要了解更多信息,请参阅 Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

Joda-Time 项目现在位于 maintenance mode 中,建议迁移到 java.time 类。

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

从哪里获得 java.time 类?

Java SE 8、Java SE 9、Java SE 10、Java SE 11 及更高版本 - 标准 Java API 的一部分,具有捆绑的实现。 Java 9 带来了一些小功能和修复。

Java 9 带来了一些小功能和修复。

Java SE 6 和 Java SE 7 大多数 java.time 功能在 ThreeTen-Backport 中向后移植到 Java 6 和 7。

大多数 java.time 功能在 ThreeTen-Backport 中向后移植到 Java 6 和 7。

Android 更高版本的 Android (26+) 捆绑了 java.time 类的实现。对于早期的 Android (<26),称为 API 脱糖的过程带来了最初未内置于 Android 中的 java.time 功能的子集。如果脱糖不能提供您所需要的,ThreeTenABP 项目会将 ThreeTen-Backport(上面提到的)适配到 Android。请参阅如何使用 ThreeTenABP……。

更高版本的 Android (26+) 捆绑了 java.time 类的实现。

对于早期的 Android (<26),称为 API 脱糖的过程带来了最初未内置于 Android 中的 java.time 功能的子集。如果脱糖不能提供您所需要的,ThreeTenABP 项目会将 ThreeTen-Backport(上面提到的)适配到 Android。请参阅如何使用 ThreeTenABP……。

如果脱糖不能提供您所需要的,ThreeTenABP 项目会将 ThreeTen-Backport(上面提到的)适配到 Android。请参阅如何使用 ThreeTenABP……。

https://i.stack.imgur.com/Sksw9.png

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


很好的答案。我认为一些混淆(至少我的)来自 Local 命名。我对 Local 的直觉意味着与我在哪里以及我在什么时候(?!)有关,这让我相信它实际上就是 ZonedDateTime
是的,这令人困惑。这就是为什么 java.time 巧妙地在其前身 Joda-Time(产生 ZonedDateTime)使用的 DateTime 类名中添加了单词“Zoned”,以强调与“Local”类的区别。将名称“本地”视为“需要应用于某个特定位置”的简写。
以单词 Local 为前缀也可能是区别于 java.util 包的一种方式,尽管不知何故我觉得可能会有更好的单词选择。
@simonh 相反......当新员工签署他/她的招聘文件,定义他们的福利包括人寿保险,然后新员工走出去喝咖啡却被卡车撞死时,会有很多人这样作为人力资源经理、保险代理人和律师,他们想知道新工作生效的确切时间。
@simonh 是的,在某些情况下“本地”日期时间是合适的。除了我的回答中提到的那些之外,商业中另一个常见的情况是在未来几个月内进行约会,远远超过政客可能会改变时区规则,通常几乎没有预先警告。政客经常进行这些更改,例如更改开启/关闭夏令时 (DST) 的日期或永久开启/关闭 DST。
D
Dariusz

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

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

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

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

两个类具有相同的精度。 LocalDateTime 不存储时区。仔细阅读 javadocs,因为您可能会因以下无效假设而犯大错误:InstantLocalDateTime


抱歉误读了区域+精度的部分。很抱歉从上面的帖子重复:考虑一个单一的时区应用程序,在哪些用例中你会喜欢 LocalDateTime 或反之亦然?
每当我需要日期和/或时间时,我都会使用 LocalDateTime。以小时、分钟左右为单位。例如,我会使用 Instant 来测量执行时间,或者存储当时和那里发生的某事的内部字段。计算下一次运行,就像你的情况一样? LocalDateTime 似乎合适,但这是一种意见。正如你所说,两者都可以使用。
T
Tunaki

您对 LocalDateTime 的看法是错误的:它不存储任何时区信息,并且具有纳秒精度。引用 Javadoc(强调我的):

ISO-8601 日历系统中没有时区的日期时间,例如 2007-12-03T10:15:30。 LocalDateTime 是一个不可变的日期时间对象,它表示日期时间,通常被视为年-月-日-时-分-秒。也可以访问其他日期和时间字段,例如一年中的某一天、一周中的某一天和一周中的一周。时间以纳秒精度表示。例如,值“2007 年 10 月 2 日 13:45.30.123456789”可以存储在 LocalDateTime 中。

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


考虑单个时区应用程序,在哪些用例中您更喜欢 LocalDateTime 或反之亦然?
@manuelaldana 这更多的是品味问题。对于任何与用户相关的内容(生日......),我更喜欢 LocalDateTime,而对于任何与机器相关的内容(执行时间......),我更喜欢 Instant。
@manuelaldana 单个时区应用程序即使不是不存在也很少见。您可能会忽略时区来为您为当地的巴洛克音乐俱乐部准备的一个小应用程序而侥幸。但是,一旦您需要向旅行(和跨时区)的人发布事件,他们就会希望这些数据与时区相关联,以便他们的日历应用程序可以根据需要进行调整。我建议您学习在所有应用程序中正确使用时区。
@Tunaki您在最后一段中使用“偏移”一词令人分心。该词在日期时间工作中具有一定的含义,因此在此上下文中使用它可能无济于事。
S
Sebien

LocalDateTime 没有时区信息:一个 LocalDateTime 可能代表世界各地不同机器的不同时刻。因此,您不应尝试将其与隐式时区(系统的默认时区)一起使用。您应该将它用于它所代表的内容,例如“新年是 1 月 1 日,0:00”:这意味着地球上所有时间点的时间不同,但在这种情况下需要它。

Instant 是格林威治时区的一个时间点。例如,除了用户的时区之外,还可以使用它来向他/她显示在他/她的时区中的会议开始时间。

如果这两个类不能代表您想要存储/交换的内容,那么 ZonedDateTime 或其他类可能会做得更好。

这是一个简单的综合模式,用于了解 java.time 包中的类及其与 ISO-8601 标准的关系,该标准用于在 Java 和其他语言或框架之间可靠且轻松地交换日期和时间:

https://i.stack.imgur.com/UF4J9.png

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