Java 日历 - 设置 day_of_week 后日期不可预测

问题描述 投票:0回答:4

我在 JUnit 测试中有以下代码,上周似乎可以工作,但本周却失败了:

Calendar cal = Calendar.getInstance();
cal.set(2011, Calendar.JULY, 12);
cal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY); // push the date to 15
System.out.println(cal.get(Calendar.DATE));

您可能从我的评论中推断出,由于 12 日是星期二,因此在将 DAY_OF_WEEK 设置为星期五后,我预计日期为 15。但是,打印的值是 22,导致测试失败。

但是,如果我将代码更改如下,并添加一个额外的 get 调用:

Calendar cal = Calendar.getInstance();
cal.set(2011, Calendar.JULY, 12);
System.out.println(cal.get(Calendar.DATE));
cal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY); // push the date to 15
System.out.println(cal.get(Calendar.DATE));

我得到了我期望的输出,12 和 15。

有人可以解释一下发生了什么以及为什么上周这个测试有效吗?

java
4个回答
19
投票

首先要明白的是,Month + Day + DayOfWeek 对日历来说没有任何意义。 日历将根据

计算日期的真实值

年+月+日

年 + 月 + WEEK_OF_MONTH + DAY_OF_WEEK

(或者其他一些组合,例如年份+一年中的某一天等)所以日期+星期几本身并没有多大意义。

要理解的第二件事是,当您在 Java 日历上设置时,它实际上不会重新计算绝对时间或更新相关字段,直到发生强制计算的操作。

第一组比赛结束后,日历就处于冲突状态。 月份和日期表示今天是 7 月 12 日,但“月份中的星期”和“星期中的日期”仍然表示今天是今天,无论今天是什么。 然后,您将一周中的设定日期称为星期五。 所以现在年月日表示 7 月 12 日,但“月份中的星期”和“星期中的日期”字段表示这是“本周”的星期五。

日历的规则规定,当存在冲突时,最近设置的字段“获胜”,因此月份中的星期和星期几结合起来表示本周的星期五用于计算其他字段。

在中间插入 get 可以“修复”它,因为它会强制日历的整个内部状态在设置为星期五之前重新计算到 7 月 12 日星期二,因此不存在内部冲突。 在将星期几设置为星期五之前,通过重新计算将“月份中的星期”设置为包含 7 月 12 日的那一周。

编辑:抱歉两天后进行更改,注意到它在旧的浏览器选项卡中打开,并认为我会扩展以期为未来的谷歌用户提供希望的帮助:

评论中对乔恩有效的原因是他住在伦敦。 他的计算机认为一周是从星期一开始的。 因此,当被问及“本周”的星期五时,当在 7 月 17 日星期日被问及时,它仍然回答 7 月 15 日。 我提出这个问题是因为不同区域设置中一周的第一天不同,这只是尝试在日历中使用 WEEK_OF 字段变得混乱的另一种方式。


3
投票

Bug 4655637(看起来与您的问题类似)。我在最新的 JDK6 (Windows) 下检查了该代码,在这两种情况下我都有

15
。顺便说一句:我建议始终明确使用
GregorianCalendar
类,除非您想要其他东西(取决于您的语言环境)。


1
投票

编辑:官方文档

以下是日历字段的默认组合。这 最近的组合,由最近设置的单个确定 字段,将被使用。

对于日期字段:

 YEAR + MONTH + DAY_OF_MONTH
 YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
 YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
 YEAR + DAY_OF_YEAR
 YEAR + DAY_OF_WEEK + WEEK_OF_YEAR

对于一天中的时间字段:

 HOUR_OF_DAY
 AM_PM + HOUR

除了@Affe的明确答案之外,以下组合似乎有效(截至@GrzegorzSzpetkowski的错误报告链接)

日历需要以下字段组合来确定 约会。

 MONTH + DAY_OF_MONTH
 MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
 MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
 DAY_OF_YEAR
 DAY_OF_WEEK + WEEK_OF_YEAR

当您设置 DAY_OF_WEEK 时,日历需要一个星期字段 (WEEK_OF_MONTH、DAY_OF_WEEK_IN_MONTH 或 WEEK_OF_YEAR)也已 放。因此,避免在未设置一周之一的情况下设置 DAY_OF_WEEK 领域。


0
投票

java.time

2014 年 3 月,Java 8 引入了现代的

java.time
日期时间 API,取代了容易出错的遗留
java.util
日期时间 API。任何新代码都应使用
java.time
API。如果您收到
java.util.Date
的实例,请使用
java.time.Instant
 将其转换为 
Date#toInstant
,并根据您的要求从中派生
java.time
的其他日期时间类。

您可以使用

LocalDate#with
,它根据作为参数传递给它的指定调整器策略对象返回
LocalDate

演示:

public class Main {
    public static void main(String[] args) {
        LocalDate date = LocalDate.of(2011, Month.JULY, 12);
        System.out.println("Date: " + date + ", Day of week: " + date.getDayOfWeek());

        // Set date to the current or next Friday
        LocalDate dateOnFri = date.with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY));
        System.out.println(dateOnFri);
    }
}

输出:

Date: 2011-07-12, Day of week: TUESDAY
2011-07-15

在线演示

Trail:日期时间了解有关现代日期时间 API 的更多信息。

© www.soinside.com 2019 - 2024. All rights reserved.