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

发布于 2024-11-24 09:41:43 字数 711 浏览 1 评论 0 原文

我在 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,导致测试失败。

但是,如果我按如下方式更改代码,并添加一个额外的调用来获取:

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。

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

I have the following code in a JUnit test, which seemed to work last week is failing this week:

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

As you could probably infer from my comment, since the 12th is a Tuesday, I expect Date to be 15 after setting the DAY_OF_WEEK to Friday. However, the value that is printed is 22, and causes the test to fail.

If I, however change the code as follows, and add an additional call to 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));

I get the output that I expect, 12 and 15.

Can someone explain what is going on, and why this test was working last week?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

赠意 2024-12-01 09:41:43

首先要明白的是,Month + Day + DayOfWeek 对于日历来说没有任何意义。日历将根据以下内容计算日期的真实值

年 + 月 + 日期

年 + 月 + WEEK_OF_MONTH + DAY_OF_WEEK

(或其他一些组合,如年 + 一年中的某天等)因此 Date + DayOfWeek 本质上对其意义不大。

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

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

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

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

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

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

The first thing to understand is that Month + Day + DayOfWeek does not mean anything to the Calendar. The Calendar will calculate the true value of the date based on

YEAR + MONTH + DATE

or

YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK

(Or some other combos like year + day of year etc.) So Date + DayOfWeek doesn't inherently mean much to it.

The second thing to understand is when you set on a Java Calendar it doesn't actually recompute the absolute time or update related fields until an operation that forces computation occurs.

After your first set, the calendar is in a conflicted state. The month and day say that it's July 12th, but the 'week of month' and 'day of week' still say that it's today, whatever today is. You then call set day of week to friday. So now year month and day say July 12th, but the 'week of month' and 'day of week' fields say it's Friday of 'this' week.

The rules of the calendar say that the most recently set field "wins" when there's a conflict, so the week of month and day of week combining to say Friday of this week are what's used to calculate the other fields.

Inserting a get in the middle 'fixes' it because it forces the entire internal state of the calendar to get recomputed to Tuesday July 12th before setting to Friday, so there are no internal conflicts. The 'week of month' got set to the week that contains July 12th by the recalculation prior to you setting day of week to Friday.

Edit: Sorry to make changes after two days, noticed this open in an old browser tab and thought I would expand for the hopeful help of future googlers:

The reason it worked for Jon in the comments is he lives in London. His computer thinks weeks start on Mondays. So when asked for Friday of 'this' week, it still responded July 15th when asked on Sunday July 17th. I bring this up because differing first days of the week in different Locales are just yet another way that trying to use the WEEK_OF fields in a calendar goes haywire.

雨轻弹 2024-12-01 09:41:43

Bug 4655637 (看起来与您的问题类似)。我在最新的 JDK6 (Windows) 下检查了该代码,在这两种情况下我都有 15 。顺便说一句:我建议始终使用 GregorianCalendar 明确类,除非您想要其他东西(取决于您的语言环境)。

There is Bug 4655637 (looks similar to your issue). I checked that code under latest JDK6 (Windows) and I have 15 in both cases. BTW: I am suggest to always use GregorianCalendar class explicitly unless you want something else (depending on your locale).

赠意 2024-12-01 09:41:43

java.time

2014 年 3 月,Java 8 引入了现代的 java.time 日期时间 API,它取代了 容易出错的遗留java.util 日期时间 API。任何新代码都应使用 java.time API。如果您收到 java.util.Date 的实例,请使用 java.time.Instant。 com/en/java/javase/11/docs/api/java.base/java/util/Date.html#toInstant()" rel="nofollow noreferrer">Date#toInstant 并导出其他根据您的要求,其中 java.time 的日期时间类。

使用现代日期时间 API 的解决方案

您可以使用 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

在线演示

特定日期-同周的星期几

如果您希望同一周的星期五,您应该首先指定您的星期是如何度过的。正如接受的答案中提到的,例如,美国的周与英国的周不同。为了演示差异,这里介绍如何在美国获得同一周的星期日,在美国,星期日是一周的第一天,而在英国,一周从星期一开始。

演示:

public class Main {

    public static void main(String[] args) {
        LocalDate date = LocalDate.of(2011, Month.JULY, 12);

        WeekFields usWeeks = WeekFields.of(Locale.US);
        LocalDate sundayInSameWeekUs = date.with(
                usWeeks.dayOfWeek(), DayOfWeek.SUNDAY.get(usWeeks.dayOfWeek()));
        System.out.println("Sunday in USA: " + sundayInSameWeekUs);

        WeekFields ukWeeks = WeekFields.of(Locale.UK);
        LocalDate sundayInSameWeekUk = date.with(
                ukWeeks.dayOfWeek(), DayOfWeek.SUNDAY.get(ukWeeks.dayOfWeek()));
        System.out.println("Sunday in UK:  " + sundayInSameWeekUk);
    }

}

输出:

Sunday in USA: 2011-07-10
Sunday in UK:  2011-07-17

在线演示

了解有关来自 Trail: Date Time< 的现代日期时间 API /强>。

java.time

In March 2014, Java 8 introduced the modern, java.time date-time API which supplanted the error-prone legacy, java.util date-time API. Any new code should use the java.time API. If you are receiving an instance of java.util.Date, convert it tojava.time.Instant, using Date#toInstant and derive other date-time classes of java.time from it as per your requirement.

Solution using modern date-time API

You can use LocalDate#with which returns a LocalDate based on the specified adjuster strategy object passed to it as an argument.

Demo:

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

Output:

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

Online Demo

A specific day-of-week in the same week

If you want Friday in the same week, you should first specify how your weeks go. As mentioned in the accepted answer, US weeks are different from UK weeks, for example. To demonstrate the difference here‘s how to get Sunday in the same week in the US, where Sunday is the first day of the week, and in the UK, where the week starts on Monday.

Demo:

public class Main {

    public static void main(String[] args) {
        LocalDate date = LocalDate.of(2011, Month.JULY, 12);

        WeekFields usWeeks = WeekFields.of(Locale.US);
        LocalDate sundayInSameWeekUs = date.with(
                usWeeks.dayOfWeek(), DayOfWeek.SUNDAY.get(usWeeks.dayOfWeek()));
        System.out.println("Sunday in USA: " + sundayInSameWeekUs);

        WeekFields ukWeeks = WeekFields.of(Locale.UK);
        LocalDate sundayInSameWeekUk = date.with(
                ukWeeks.dayOfWeek(), DayOfWeek.SUNDAY.get(ukWeeks.dayOfWeek()));
        System.out.println("Sunday in UK:  " + sundayInSameWeekUk);
    }

}

Output:

Sunday in USA: 2011-07-10
Sunday in UK:  2011-07-17

Online Demo

Learn more about the modern Date-Time API from Trail: Date Time.

滥情空心 2024-12-01 09:41:43

编辑:官方文档

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

对于日期字段:

 年 + 月 + DAY_OF_MONTH
 年 + 月 + WEEK_OF_MONTH + DAY_OF_WEEK
 年 + 月 + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
 年 + DAY_OF_YEAR
 年 + DAY_OF_WEEK + WEEK_OF_YEAR

对于一天中的时间字段:

<前><代码> HOUR_OF_DAY
AM_PM + HOUR


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

日历需要以下字段组合来确定
一个日期。

<前><代码> 月 + DAY_OF_MONTH
月 + 周_OF_月 + 日_OF_周
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
字段。

EDIT: official docs:

The following are the default combinations of the calendar fields. The
most recent combination, as determined by the most recently set single
field, will be used.

For the date fields:

 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

For the time of day fields:

 HOUR_OF_DAY
 AM_PM + HOUR

In addition to @Affe's clear answer, the following combinations seem to work (as of @GrzegorzSzpetkowski's bug report link)

Calendar expects the following combinations of the fields to determine
a date.

 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

When you set DAY_OF_WEEK, the calendar expects a week field
(WEEK_OF_MONTH, DAY_OF_WEEK_IN_MONTH or WEEK_OF_YEAR) has also been
set. So, avoid setting DAY_OF_WEEK without setting one of the week
fields.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文