Java 日历 DAY_OF_WEEK 设置为零

问题描述

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

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

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

解决方法

为什么没有例外?

这是因为您没有将宽松模式设置为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

文档说:

任何超出范围的值要么在宽松模式下标准化,要么 在非宽松模式下检测为无效值

作为归一化的一部分,该值被翻转,例如以下代码将值设置为等效于 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“溢出”时,它看起来是“日历集”调整值/整数。 我可以看到这种模式:

       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(),因此实际上不可能传递超出范围的值。 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);

线程“main”中的异常 java.time.DateTimeException: Invalid value 对于 DayOfWeek(有效值 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

教程链接

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