【问题标题】:Unexpected date calculation result意外日期计算结果
【发布时间】:2018-04-25 19:59:54
【问题描述】:

我有一种在 Java 中查看日历的方法,它按年份、星期几和星期数计算日期。

现在,当我计算 2017 年的日期时,一切正常。但是当我从 2018 年 1 月开始计算日期时,它需要 2017 年的日期。

我的代码看起来像

import java.time.temporal.IsoFields;
import java.time.temporal.ChronoField;
import java.time.LocalDate;

// .....

LocalDate desiredDate = LocalDate.now()
                    .with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 1)
                    .with(ChronoField.DAY_OF_WEEK, 1)
                    .withYear(2018);

结果为 2018-01-02,应该是 2018-01-01。这怎么可能?

【问题讨论】:

  • 可能dayOfWeek索引从0开始
  • 在 ChronoFIeld 的 Javadoc 上写着:星期一 (1) 到星期日 (7)
  • @SayanSil a) 它没有,b) 不能解释结果2018-01-02
  • 是的。检查。它从 1 开始。您是否检查了除一年的开始周以外的其他周?
  • 如果你打印出链中的增量步骤(2017-11-13 -> 2017-01-02 -> 2017-01-02 -> 2018-01-02),问题就变得更清楚了。

标签: java time localdate


【解决方案1】:

调用方法的顺序似乎很重要。
如果你通过递减的时间粒度(年、星期几和星期几)调用它们,你会得到正确的结果:

long weekNumber = 1;
long dayOfWeek = 1;
int year = 2018;

LocalDate desiredDate = LocalDate.now()
    .withYear(year)
    .with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, weekNumber)
    .with(ChronoField.DAY_OF_WEEK, dayOfWeek );

System.out.println(desiredDate);

2018-01-01

注意问题的根源来自:

.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, weekNumber)

根据当前年份设置周数 (1 to 53)。
如果您使用 .withYear(year) 作为周数信息更改年份,Java LocalDate API 无法调整此值没有保存在 LocalDate 实例中。

您确实可以在LocalDate 实现中看到LocalDate 实例仅由3 个字段定义:yearmonthday

public final class LocalDate
        implements Temporal, TemporalAdjuster, ChronoLocalDate, Serializable {
    ...
    private final int year;
    /**
     * The month-of-year.
     */
    private final short month;
    /**
     * The day-of-month.
     */
    private final short day;
    ...
}

所以准确地说,重要的是:

.withYear(year) 在之前被调用

.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, weekNumber);

【讨论】:

  • Tnx 完成了这项工作。
  • 有意义 - 方法会调整先前的结果,它们不会同时被评估。但我可以理解有人因此而措手不及。
  • @davidxxx 原始版本调整为 2017 年的第一个星期一(1 月 2 日)然后更改年份。
  • 如果您尝试通过获取现有日期并一次修改一个字段来构建日期,则即使您按粒度排序设置也无法正确设置日期。这是 API 中的一个主要漏洞。在所有情况下一致地设置日期的唯一可靠方法是使用同时设置所有字段的构造函数。
  • @davidxxx each with 返回一个全新的不可变 LocalDate(即从纪元开始的天数),它对以前的操作一无所知。
【解决方案2】:

我想提一下,LocalDate 还有另一个问题(?)。

此代码也会产生错误的结果:

    int jahr = Integer.parseInt(str[0]);
    int woche = Integer.parseInt(str[1]);

    LocalDate year = LocalDate.of(jahr, 1, 1);
    LocalDate week = year.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, woche);
    LocalDate day = week.with(wochentag);
    return day;

如果将year变量的创建改为

 LocalDate year = LocalDate.now().withYear(jahr);

代码返回预期结果。您构建 LocalDate 的方式似乎很重要。我猜“.of()”版本中省略了时区。

【讨论】: