使用 with* 函数、TemporalAdjusters 或通过设置 TemporalFields 调整 ZonedDateTimes 之间有什么区别吗?

Is there ever any difference between adjusting ZonedDateTimes using the with* functions, TemporalAdjusters or by setting TemporalFields?

假设我有以下代码:

java.time.ZonedDateTime inputZonedDateTime = inputDate.toInstant().atZone(zoneId);
ZonedDateTime flattenedDateTime = inputZonedDateTime.withDayOfYear(1);

奇怪的是这个实用方法已经好几个星期不存在了,所以好几个星期我都是这样实现的:

flattenedDateTime = inputZonedDateTime.with(java.time.temporal.ChronoField.DAY_OF_WEEK, 1);

然后你也可以这样做:

flattenedDateTime = inputZonedDateTime.with(TemporalAdjusters.firstDayOfMonth());

(也有几周不可用)

这3种调整日期的方式有什么区别吗?我想确保它们 always 在功能上是等价的,这样我就可以确保在将 ChronoUnit 调整为 1 的这种特定情况下,我可以始终互换使用它们。特别是因为没有实用方法对于 "week" 案例。

如果您质疑这是否是正确的查找方法:

  1. 给我划日期时间,将星期几调整为星期一。

    示例:05-03-2019 .... -> 04-03-2019 ....

  2. 给我划日期时间,日期适合一个月的第一天!

    示例:05-03-2019 .... -> 01-03-2019 ....

那么答案是肯定的

        ZoneId zoneID = ZoneId.of( "Europe/Belgrade" ) ;
        ZonedDateTime zoneDateTime = ZonedDateTime.now(zoneID) ;

        System.out.println(zoneDateTime.withDayOfMonth(1));
        System.out.println(zoneDateTime.withMonth(1));
        System.out.println(zoneDateTime.with(ChronoField.DAY_OF_WEEK,1));
        System.out.println(zoneDateTime.with(TemporalAdjusters.firstDayOfMonth()));

我理解你的困惑。区别不在于 withXx 方法与 TemporalAdjusters 的工作方式不同。区别是:

  • 年的第一天和月份的第一天被明确定义,因此很容易构建到 withXx 方法或时间调整器中。
  • 一周的第一天因文化而异。在某些地方,一周从星期日或其他某天开始。将 ChronoField.DAY_OF_WEEK 设置为 1 会将星期几设置为星期一。因此,无论是 withFirstDayOfWeek 方法还是 firstDayOfWeek 时间调整器都是危险的,并且会给一些用户带来意想不到的结果。

如果您确定只想考虑 ISO 周,其中星期一是第一天,inputZonedDateTime.with(ChronoField.DAY_OF_WEEK, 1) 可以满足您的目的。否则正确的解决方案是使用 WeekFields 对象及其 dayOfWeek​() 时间场。将此字段设置为 1 将根据 WeekFields 对象表示的周定义设置为一周的第一天。

根据维基百科:

  • 在中东大部分地区,星期六开始。
  • 在加拿大、美国、印度、日本、台湾、香港、澳门、以色列、埃及、南非、菲律宾和拉丁美洲的大部分地区,它从周日开始。
  • 欧盟和大多数其他欧洲国家、大部分亚洲和大洋洲使用星期一(与 ISO 达成一致)。

I was trying to take into account that Sunday can be the first day of the week in for example the US.

示例代码

    // Don’t set default locale from production code, it’s for demonstration only
    Locale.setDefault(Locale.forLanguageTag("ar-SD"));

    WeekFields wf = WeekFields.of(Locale.getDefault());
    DateTimeFormatter formatter = DateTimeFormatter
            .ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.MEDIUM)
            .withLocale(Locale.ENGLISH);

    ZonedDateTime inputZonedDateTime
            = ZonedDateTime.of(2019, 3, 3, 12, 0, 0, 0, ZoneId.of("Asia/Amman"));
    ZonedDateTime flattenedDateTime = inputZonedDateTime.with(wf.dayOfWeek(), 1);
    System.out.println("First day of week in "
            + Locale.getDefault().getDisplayCountry(Locale.ENGLISH)
            + " is " + flattenedDateTime.format(formatter));

First day of week in Sudan is Saturday, March 2, 2019, 12:00:00 PM

当然,您可以通过其他方式获得 WeekFields.of 的正确语言环境。其他几个语言环境的输出包括:

  • es-PY:

    First day of week in Paraguay is Sunday, March 3, 2019, 12:00:00 PM

  • 毫克-毫克:

    First day of week in Madagascar is Monday, February 25, 2019, 12:00:00 PM

Link: Wikipedia article: week