这些之间有什么区别:
zonedDateTime.truncatedTo(ChronoUnit.DAYS);
zonedDateTime.toLocalDate().atStartOfDay(zonedDateTime.getZone());
有理由偏爱另一个吗?
由于
答案 0 :(得分:43)
为了纠正而更新:
在大多数情况下是相同的,请参阅以下巴西从冬季到夏季转换时的示例:
ZonedDateTime zdt =
ZonedDateTime.of(2015, 10, 18, 0, 30, 0, 0,
ZoneId.of("America/Sao_Paulo")); // switch to summer time
ZonedDateTime zdt1 = zdt.truncatedTo(ChronoUnit.DAYS);
ZonedDateTime zdt2 = zdt.toLocalDate().atStartOfDay(zdt.getZone());
System.out.println(zdt); // 2015-10-18T01:30-02:00[America/Sao_Paulo]
System.out.println(zdt1); // 2015-10-18T01:00-02:00[America/Sao_Paulo]
System.out.println(zdt2); // 2015-10-18T01:00-02:00[America/Sao_Paulo]
截断发生在本地时间轴上。如果您选择DAYS,那么您选择午夜。根据{{3}} truncate()
- 方法最终转换回新的ZonedDateTime
并将时间向前移动间隔的大小(1小时)。
首先将zdt转换为LocalDate
(切断时间部分),然后在给定时区中查找其ZonedDateTime
- 部分对于这种情况实际上是相同的。
然而,对于从夏季时间切换回冬季时间的相反情况,有一个例外(非常感谢@Austin提供了反例)。问题是在重叠期间何时决定使用哪个偏移量。通常设计/指定类ZonedDateTime
以使用前一个偏移量,另请参阅javadoc的摘录:
对于Overlaps,一般策略是如果是本地日期时间 落在重叠的中间,然后是前一个偏移 保留。如果没有先前的偏移量,或者前一个偏移量是 无效,则使用较早的偏移量,通常为" summer"时间。
如果类ZonedDateTime
因此遵循其自己的规范,那么这两个程序仍然是等同的意思:
zdt.truncatedTo(ChronoUnit.DAYS);
应该相当于
zdt.toLocalDate().atStartOfDay().atZone(zdt.getZone()).withEarlierOffsetAtOverlap();
但根据@Austin的例子,我在自己的测试中证实的真实行为是:
zdt.toLocalDate().atStartOfDay().atZone(zdt.getZone()).withLaterOffsetAtOverlap();
看起来像ZonedDateTime
课程中隐藏的不一致,温和地说。如果你问我哪种方法更受欢迎,那么我宁愿提倡第二种方法,尽管它更长,需要更多的击键。但它的最大优点是更加透明。偏好第二种方法的另一个原因是:
它确实获得当地时间等于一天开始的第一瞬间。否则,在使用第一种方法时,您必须写:
zdt.truncatedTo(ChronoUnit.DAYS).withEarlierOffsetAtOverlap();
答案 1 :(得分:29)
他们略有不同。根据javadocs,truncatedTo()
将尝试在重叠的情况下保留时区,但atStartOfDay()
将找到第一次出现的午夜。
例如,古巴在凌晨1点恢复夏令时,回到上午12点。如果您在转换后的某个时间开始,atStartOfDay()
将返回第一次出现的上午12点,而truncatedTo()
将返回第二次出现。
ZonedDateTime zdt = ZonedDateTime.of(2016, 11, 6, 2, 0, 0, 0, ZoneId.of("America/Havana"));
ZonedDateTime zdt1 = zdt.truncatedTo(ChronoUnit.DAYS);
ZonedDateTime zdt2 = zdt.toLocalDate().atStartOfDay(zdt.getZone());
System.out.println(zdt); // 2016-11-06T02:00-05:00[America/Havana]
System.out.println(zdt1); // 2016-11-06T00:00-05:00[America/Havana]
System.out.println(zdt2); // 2016-11-06T00:00-04:00[America/Havana]
答案 2 :(得分:6)
请注意,还有另一种方法:
zonedDateTime.with(LocalTime.MIN);
产生与truncatedTo
相同的结果答案 3 :(得分:0)
zonedDateTime.truncatedTo(ChronoUnit.DAYS)
是更好的选择。
正如 Austin 指出的那样,toLocalDate()
会丢失区域信息。来自 LocalDate 文档:
ISO-8601 日历系统中没有时区的日期,例如 2007-12-03。 LocalDate 是一个不可变的日期时间对象,它表示 一个日期,通常被视为年-月-日。其他日期字段,例如 也可以访问年份、星期和星期。为了 例如,值“2nd October 2007”可以存储在 LocalDate 中。
除了 Austin 给出的示例之外,在开发和部署在不同机器上的常见情况下,这可能会导致问题。
举一个具体的例子,考虑处理纽约一家餐厅从美国东部时间上午 11 点到晚上 11 点收到的订单。如果代码是在纽约时间在机器上开发的,toLocalDate()
将不会显示任何错误。但是,当代码部署在 UTC 时区的服务器上时,它会在晚上 8 点之后关闭(当 EDT 比 UTC 晚 4 小时时)。