【问题标题】:Java Calendar DAY_OF_WEEK SET to zeroJava 日历 DAY_OF_WEEK 设置为零
【发布时间】:2023-03-30 11:57:02
【问题描述】:

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

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

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

【问题讨论】:

  • 文档说“无论宽大模式如何,此方法都不会解释该值。”并且还说如果值低于零,则会引发异常。我想知道这是否意味着没有进行数据验证,并且没有为零值引发异常。这些遗留的日期时间类真是一团糟。请注意,您可以使用 java.time 包中的替换项编写代码,同时通过调用添加到旧类的新 to/from 方法轻松地来回转换。跨度>
  • 我建议你不要使用Calendar。该课程设计不良且早已过时。而是使用来自java.time, the modern Java date and time API 的类,例如LocalDateZonedDateTime

标签: java calendar dayofweek


【解决方案1】:

为什么没有例外?

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

【讨论】:

  • 很想知道这一点。奇怪的是,即使在调用 getTime() 之前,即使 Lenient 模式设置为 false,它也允许将值设置为 0,没有任何异常。
  • @dora - 在设置值时没有执行验证。只有在获取值时,如果在非宽松模式下检测到无效值,则会出现异常。
【解决方案2】:

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

当数字 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

按照“模式”输出。

【讨论】:

    【解决方案3】:

    java.time

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

        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);
    

    线程“主”java.time.DateTimeException 中的异常:无效值 对于 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。

    【讨论】:

      猜你喜欢
      • 2020-05-18
      • 1970-01-01
      • 2011-06-06
      • 2019-08-22
      • 2014-02-18
      • 1970-01-01
      • 2015-03-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多