Java 日历 DAY_OF_WEEK 设置为零

Java Calendar DAY_OF_WEEK SET to zero

我有这个来自 PROD(> 7 年)的非常旧的代码块需要调试。有一点我无法理解。 代码中的一个部分会计算下一次任务将 运行 的时间,对于需要 运行 的任务,特别是在星期天、星期一,它使用 Calendar.SUNDAY。但是有一种说法即使在多次阅读文档后我也无法解释其行为

日历cal = Calendar.getInstance(); cal.set(Calendar.DAY_OF_WEEK, 0);

由于天数是从 1-7(Calendar.SUNDAYCalendar.SATURDAY)可以解释的,但是这里的零是如何工作的,为什么没有例外?

why there is no exception?

因为你没有设置宽松模式false,默认是true

演示:

import java.util.Calendar;

public class Main {
    public static void main(String[] args) {
        Calendar cal = Calendar.getInstance();
        cal.setLenient(false);
        cal.set(Calendar.DAY_OF_WEEK, 0);
        System.out.println(cal.getTime());
    }
}

输出:

Exception in thread "main" java.lang.IllegalArgumentException: DAY_OF_WEEK

文档说:

Any out of range values are either normalized in lenient mode or detected as an invalid value in non-lenient mode

作为规范化的一部分,值会翻转,例如以下代码将值设置为 cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY - 1):

cal.set(Calendar.DAY_OF_WEEK, 0);

同样,下面的代码设置值相当于cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY - 2)

cal.set(Calendar.DAY_OF_WEEK, -1);

通过在“测试台”中试用,我发现:

当数字 1-7 被“溢出”时,“日历设置”看起来会调整 value/integer。 我可以看到这个模式:

       Day Of Week:  1  2  3  4  5  6  7  | 1 2 3 4 5 6 7 | 1  2  3   4   5   6   7  | ... 
 Value of calendar: -6 -5 -4 -3 -2 -1  0  | 1 2 3 4 5 6 7 | 8  9  10  11  12  13  14 | ...

测试台:

public static void main(String[] args) {
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.DAY_OF_WEEK, -6);
    System.out.println("Calendar value -6 returns: " + cal.get(Calendar.DAY_OF_WEEK));

    cal.set(Calendar.DAY_OF_WEEK, 0);
    System.out.println("Calendar value 0 returns: " + cal.get(Calendar.DAY_OF_WEEK));

    cal.set(Calendar.DAY_OF_WEEK, 1);
    System.out.println("Calendar value 1 returns: " + cal.get(Calendar.DAY_OF_WEEK));

    cal.set(Calendar.DAY_OF_WEEK, 7);
    System.out.println("Calendar value 7 returns: " + cal.get(Calendar.DAY_OF_WEEK));

    cal.set(Calendar.DAY_OF_WEEK, 8);
    System.out.println("Calendar value 8 returns: " + cal.get(Calendar.DAY_OF_WEEK));

    cal.set(Calendar.DAY_OF_WEEK, 14);
    System.out.println("Calendar value 14 returns: " + cal.get(Calendar.DAY_OF_WEEK));
}

输出:

Calendar value -6 returns: 1
Calendar value 0 returns: 7
Calendar value 1 returns: 1
Calendar value 7 returns: 7
Calendar value 8 returns: 1
Calendar value 14 returns: 7

输出是根据“模式”。

java.time

我建议您使用 java.time,现代 Java 日期和时间 API,作为您的日期和时间工作。例如:

    LocalDate ld = LocalDate.now(ZoneId.systemDefault())
                            .with(DayOfWeek.TUESDAY);
    System.out.println(ld);

今天,6 月 5 日星期六,当我 运行 这段代码时,输​​出是:

2021-06-01

是的,6 月 1 日是星期二。由于我们将枚举常量传递给 with(),因此实际上不可能传递超出 运行ge 的值。 DayOfWeek 是一个枚举,包含一周中 7 天的 7 个值。我们唯一可能遇到的麻烦是传递 null,这将抛出一个 NullPointerException,我想你想要的。

如果我们坚持将星期几作为数字传递,那是可能的。 java.time 从星期一 = 1 到星期日 = 7 对一周中的几天进行编号。

    LocalDate ld = LocalDate.now(ZoneId.systemDefault())
                            .with(ChronoField.DAY_OF_WEEK, 2);

到目前为止,输出和以前一样是 2021-06-01。但是如果我们传递 0 呢?

                            .with(ChronoField.DAY_OF_WEEK, 0);

Exception in thread "main" java.time.DateTimeException: Invalid value for DayOfWeek (valid values 1 - 7): 0

恕我直言,我们不仅得到了您要求的例外情况,而且还收到了一条清晰且有用的例外消息。

星期几在这里如何工作?

对于 Calendar 星期几 0 与 7 = 星期六相同。似乎至少一个宽松的老式 GregorianCalendar 在星期几执行一种模 7 运算以将其置于 1 到 7 的区间内。我没有找到这个记录。 GregorianCalendar 可能是您正在处理的 Calendar 的具体子类。我尝试了不同的数字,它们都等同于 7 模 7:

    int[] dows = { 0, 7, -7, 14, -14, -98 };
    for (int dow : dows) {
        Calendar cal = new GregorianCalendar(2021, Calendar.JUNE, 2);               
        Date dateBefore = cal.getTime();
        cal.set(Calendar.DAY_OF_WEEK, dow);
        System.out.format("%s and day of week %3d yields %s%n", dateBefore, dow, cal.getTime());
    }

输出:

Wed Jun 02 00:00:00 CEST 2021 and day of week   0 yields Sat Jun 05 00:00:00 CEST 2021
Wed Jun 02 00:00:00 CEST 2021 and day of week   7 yields Sat Jun 05 00:00:00 CEST 2021
Wed Jun 02 00:00:00 CEST 2021 and day of week  -7 yields Sat Jun 05 00:00:00 CEST 2021
Wed Jun 02 00:00:00 CEST 2021 and day of week  14 yields Sat Jun 05 00:00:00 CEST 2021
Wed Jun 02 00:00:00 CEST 2021 and day of week -14 yields Sat Jun 05 00:00:00 CEST 2021
Wed Jun 02 00:00:00 CEST 2021 and day of week -98 yields Sat Jun 05 00:00:00 CEST 2021

教程link

Oracle tutorial: Date Time 解释如何使用 java.time.