【问题标题】:java.time.LocalDate vs Instant for a 'business date'java.time.LocalDate 与“营业日期”的即时对比
【发布时间】:2019-10-18 20:14:54
【问题描述】:

我想表示“营业日期”,例如“2019 年 6 月 3 日”发生的交易。为此,我们积极忽略时区,因为完全知道日本的“2019 年 6 月 3 日”可能是美国的“2019 年 6 月 2 日”——在“当天”内订购同样无关紧要。所有日期都将是今天或之前的日期。

我的明显答案是这是LocalDate。然而,其他人建议这将更好地表示为2019-06-03T00:00:00.000ZInstant

除了在 UI 中将其转换/格式化为人类可读的日期时我们必须进行的不同调用之外,这两种方法之间实际上有什么区别吗?

这是与What's the difference between Instant and LocalDateTime? 不同的问题,因为时间与此问题无关,它仅与过去(或当前)日期有关。

【问题讨论】:

  • 我曾经在一个产品上工作过,我们错误地将出生日期表示为瞬间。这是我们多年后仍在处理的那些史诗般的错误之一。使用LocalDate
  • @AndyTurner 这几乎听起来像是答案的基础——具体出了什么问题?
  • 鉴于 LocalDate 是日期的自然类型,“其他人”是否有特殊原因建议使用 Instant?这就像说“我想表示一个整数”并且有人建议使用 double... 需要一个非常好的理由。
  • @Oli 查看 jonskeet 的文章Storing UTC is not a silver bullet
  • @JonSkeet 谢谢,int/double 是一个很好的类比。

标签: java java.time.instant java-time


【解决方案1】:

我曾经开发过一个产品,我们错误地将出生日期表示为瞬间。

这是我们多年后仍在处理的那些“微小”设计错误之一。

问题是当用户更改时区时,您无法在 UI 中可靠地显示它;在后端进行转换很容易出错,因为开发人员在与服务器运行的不同时区工作。它有点在一个时区工作,但随着产品扩展到其他时区,它变得非常头疼。

瞬间是时间线上的一个;本地日期是时间的范围(而不是明确定义的时间范围,因为它可以表示不同时区的不同瞬间范围)。它们代表不同的事物。

如果你想表示一个日期,存储一个日期。我的意思不是java.util.Date,这真的是一个瞬间。

使用LocalDate

【讨论】:

  • 正如我所说,我的直觉是 LocalDate,但如果我不能自己争论这个案子,那可能意味着我不完全理解它!因此,也许要说明的一点是“使用 Instant 时很容易出错,而 LocalDate 可以保护您免受这种情况的影响”? (事实上​​,我们在世界各地的多个地方都在努力和运行它。)
  • 有了本地日期,您就有了年、月、日。而已。你不能不小心把它当作别的东西。瞬间可能会被意外解释为“某个时区的时间”,如果时区错误,则表示转换错误。
  • 我只想提一下,存储出生日期只是使用LocalDate 可能有意义的少数边缘情况之一。如果我于 2020 年 1 月 1 日凌晨 1:00 出生在德国,那么对于美国某个时区的某人来说,将我的生日显示为 2019 年 12 月 31 日是没有意义的。话虽如此,在几乎所有其他情况下,我认为同时记录 TZ 或使用 Instant 之类的东西是完全有意义的,其中 TZ 是默认定义的。
【解决方案2】:

LocalDate 可能不明确

Answer by Andy Turner 是正确且有价值的。另外,我想指出LocalDate 固有的模糊性。

LocalDate 类表示仅日期值,没有时间,没有时区。对于任何给定的时刻,日期在全球范围内因时区而异。在某个特定时刻,在日本东京可能是“明天”,而在美国俄亥俄州托莱多可能是“昨天”。两个不同的日期在同一时刻生效。

因此,虽然您可能认为日期包含 24 小时,定义了特定的时刻范围,但事实并非如此。您必须将日期放在时区的上下文中才能确定时刻。 (顺便说一句,日子并不总是 24 小时。)

LocalDate localDate = LocalDate.of( 2021 , Month.JANUARY , 24 ) ;
ZoneId zoneTokyo = ZoneId.of( "Asia/Tokyo" ) ;
ZonedDateTime startOfDayTokyo = localDate.atStartOfDay( zoneTokyo ) ;  // Determine a specific moment. 

startOfDayTokyo.toString(): 2021-01-24T00:00+09:00[亚洲/东京]

要在 UTC 中查看同一时刻,请提取 Instant 对象。

Instant instant = startOfDayTokyo.toInstant() ;

请注意日期是 23 日而不是 24 日。

instant.toString(): 2021-01-23T15:00:00Z

通过第三挂钟时间查看同一时刻,即美国俄亥俄州托莱多时区 America/New_York 的时刻。

ZonedDateTime zdtToledo = instant.atZone( ZoneId.of( "America/New_York" ) ) ;

请注意日期是 23 日而不是 24 日。

zdtToledo.toString(): 2021-01-23T10:00-05:00[美国/纽约]

查看此代码run live at IdeOne.com

因此,在存储LocalDate 时,您可能还需要存储时区名称。在数据库表中,这意味着两列。

例如,想想生日。如果知道某人当天的年龄至关重要,那么仅日期值是不够的。只有一个约会,一个在日本东京看起来年满 18 岁的人在美国俄亥俄州托莱多仍然是 17 岁。作为另一个例子,考虑合同中的到期日。如果仅表示日期,日本的利益相关者将认为任务过期,而托莱多的另一利益相关者将任务视为准时。

相反,当您指的是时间轴上的某个特定点时,请使用Instant(或OffsetDateTimeZonedDateTime)。


【讨论】:

    猜你喜欢
    • 2020-08-11
    • 2020-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-21
    • 1970-01-01
    相关资源
    最近更新 更多