Java 8 日期时间:从 ZonedDateTime 开始一天

2022-08-31 12:44:54

这些之间有什么区别吗:

zonedDateTime.truncatedTo(ChronoUnit.DAYS);

zonedDateTime.toLocalDate().atStartOfDay(zonedDateTime.getZone());

有什么理由偏爱一个而不是另一个?

谢谢


答案 1

为更正而更新:

在大多数情况下,是的,从冬季切换到夏季时,请参阅以下巴西示例:

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,那么您选择午夜。根据javadoc,-方法最终转换回新的,并将时间向前移动间隔的大小(1小时)。truncate()ZonedDateTime

对于这种情况,首先将zdt转换为(切断时间部分),然后在给定时区中查找其-部分实际上是相同的。LocalDateZonedDateTime

但是,对于从夏时切换回冬令时的相反情况,有一个例外(非常感谢@Austin给出了一个反例)。问题是在重叠期间何时决定使用哪个偏移量。通常,该类被设计/指定为使用前面的偏移量,另请参阅Javadoc的摘录:ZonedDateTime

对于重叠,一般策略是,如果本地日期时间位于重叠的中间,则将保留以前的偏移量。如果没有以前的偏移量,或者以前的偏移量无效,则使用较早的偏移量,通常是“夏季”时间。

如果该类随后遵循其自己的规范,则这两个过程仍然是等效的含义: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();

答案 2

它们略有不同。根据javadocs的说法,truncatedTo()将尝试在重叠的情况下保留时区,但是在StartOfDay()会找到第一个出现的午夜。

例如,古巴在凌晨1点恢复夏令时,回落到凌晨12点。如果从该转换之后的时间开始,将返回第一次出现的 12am,而将返回第二次匹配项。atStartOfDay()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());
ZonedDateTime zdt3 = zdt.with(LocalTime.MIN);

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]
System.out.println(zdt3); // 2016-11-06T00:00-05:00[America/Havana]