Instant和LocalDateTime有什么区别?


255

我知道:

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

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

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

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


Instant更基本,为UTC包装了很长时间的标准。对于像cron这样的批处理来说,不是那么合理的选择。
乔普·艾根

37
定义不正确LocalDateTime不能有一个时区!
罗勒·布尔克

Answers:


828

Java中所有日期和时间类型的表,包括现代的和传统的

tl; dr

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

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

不正确的推定

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

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

引用该类的文档:

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

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

Instant

在此处输入图片说明

An InstantUTC时间轴上的一刻,是自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/MontrealAfrica/CasablancaPacific/Auckland。切勿使用2-4字母的缩写,例如EST或,IST因为它们不是真实的时区,不是标准化的,甚至不是唯一的(!)。

时区=偏移量+调整规则

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

ZonedDateTime

在此处输入图片说明

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

ZonedDateTime =(即时+ 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

LocalDateLocalTimeLocalDateTime

该图仅显示<code> LocalDate </ code>的日历。

该图仅显示<code> LocalTime </ code>的时钟。

该图显示了<code> LocalDateTime </ code>的日历和时钟。

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

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

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

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

一天中的多个时间

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

例如,“圣诞节从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 LocalDateTime和a 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标准定义的日期时间类型。这可能有助于将InstantLocalDateTime类放在更大的上下文中。

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

注意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.DateCalendar,和SimpleDateFormat

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

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

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

在哪里获取java.time类?

与哪个版本的Java或Android一起使用的哪个java.time库的表

ThreeTen-额外项目与其他类扩展java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。你可能在这里找到一些有用的类,比如IntervalYearWeekYearQuarter,和更多


39
好答案。我认为Local命名有些混乱(至少是我的)。我的直觉Local意味着与我所在的位置以及我何时所在的位置(?!),这使我相信这实际上就是a ZonedDateTime
mkobit

4
是的,这令人困惑。这就是为什么java.time巧妙地在DateTime其前身Joda-Time(产生ZonedDateTime)使用的类名中添加了“ Zoned”一词,以强调与“ Local”类的区别。可以将名称“ Local”视为“需要应用于某些特定位置”的简写。
罗勒·布尔克

2
以单词开头Local也可能是与java.util包区分开的一种方法,尽管我以某种方式认为可以选择一个更好的单词。
vphilipnyc '16

2
@simonh相反...当新员工签署其聘用文件以定义其福利(包括人寿保险),然后新员工走到外面喝咖啡而被卡车撞伤时,将会有很多这样的人作为人力资源经理,保险代理人和律师,他们想知道新工作生效的确切时间。
罗勒·布尔克

2
@simonh是的,在某些情况下,“本地”日期时间合适。除了“我的答案”中提到的那些事项外,业务中的另一个常见情况是,约会要在未来数月之后进行,这远远超出了政治家可能会更改时区规则的时间,通常很少发出警告。政客经常进行这些更改,例如更改开/关夏令时(DST)的日期或永久性开/关DST的日期。
罗勒·布尔克

20

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

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

此外,LocalDateTime提供了用于处理日期组件(例如天,小时,月)的方法。一个Instant没有。

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

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


很抱歉误读了区域+精度部分。很抱歉在上面的帖子中重复:考虑单个时区应用程序,在哪种用例中,您偏爱LocalDateTime,反之亦然?
曼努埃尔·奥尔达娜2015年

1
每当需要日期和/或时间时,我都会选择LocalDateTime。在几小时,几分钟左右的时间内。例如,我将使用Instant来衡量执行时间,或者存储当时发生的某事的内部字段。根据您的情况计算下一次运行?LocalDateTime似乎合适,但这是一个意见。如您所述,两者都可以使用。
Dariusz 2015年

您可以详细说明LocalDateTime stores date and time without timezone, but it's initial value is timezone dependent吗?什么是初始值?时区如何变化?谢谢。
最多

12

您错了LocalDateTime:它不存储任何时区信息,并且具有纳秒级精度。引用Javadoc(重点是我的):

ISO-8601日历系统中没有时区的日期时间,例如2007-12-03T10:15:30。

LocalDateTime是一个不变的日期时间对象,它代表一个日期时间,通常被视为年-月-日-小时-分钟-秒。也可以访问其他日期和时间字段,例如,一年中的某天,一周中的某天和一周中的某周。时间以纳秒精度表示。例如,值“ 2007年10月2日13:45.30.123456789”可以存储在LocalDateTime中。

两者之间的差异是Instant代表从纪元(01-01-1970)的偏移,因此代表了时间轴上的特定时刻。Instant在地球的两个不同位置同时创建的两个对象将具有完全相同的值。


考虑单个时区应用程序,在哪些用例中,您偏爱LocalDateTime,反之亦然?
曼努埃尔·奥尔达娜2015年

3
@manuelaldana这更多是一个品味问题。对于与用户相关的任何内容(生日...),对于任何与计算机相关的内容(执行时间...),我都希望使用LocalDateTime。
Tunaki 2015年

2
@manuelaldana如果不存在单个时区应用程序,则很少。您可能会忽略为您当地的巴洛克音乐俱乐部准备的一个小应用程序的时区。但是,一旦您需要向旅行(和跨越时区)的人们发布活动,他们就会希望该数据绑定到时区,以便他们的日历应用可以根据需要进行调整。我建议您学习在所有应用程序中正确使用时区。
罗勒·布尔克

@Tunaki您在上一段中对“偏移”一词的使用令人分心。该单词在日期时间工作中具有一定的含义,因此在此上下文中使用此单词可能无济于事。
罗勒·布尔克

0

Instant 对应于本初子午线(格林威治)上的时间。

LocalDateTime相对于操作系统的时区设置,

没有附加信息(例如偏移量或时区)就无法表示瞬间。


2
即时是基于UTC而非GMT。
Torsten Ojaperv
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.