Java 日历 - 设置 day_of_week 后日期不可预测
我在 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。
有人可以解释一下发生了什么以及为什么这个测试上周有效吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
首先要明白的是,Month + Day + DayOfWeek 对于日历来说没有任何意义。日历将根据以下内容计算日期的真实值
或
(或其他一些组合,如年 + 一年中的某天等)因此 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
or
(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.
有 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 useGregorianCalendar
class explicitly unless you want something else (depending on your locale).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
。演示:
输出:
在线演示
特定日期-同周的星期几
如果您希望同一周的星期五,您应该首先指定您的星期是如何度过的。正如接受的答案中提到的,例如,美国的周与英国的周不同。为了演示差异,这里介绍如何在美国获得同一周的星期日,在美国,星期日是一周的第一天,而在英国,一周从星期一开始。
演示:
输出:
在线演示
了解有关来自 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 thejava.time
API. If you are receiving an instance ofjava.util.Date
, convert it tojava.time.Instant
, usingDate#toInstant
and derive other date-time classes ofjava.time
from it as per your requirement.Solution using modern date-time API
You can use
LocalDate#with
which returns aLocalDate
based on the specified adjuster strategy object passed to it as an argument.Demo:
Output:
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:
Output:
Online Demo
Learn more about the modern Date-Time API from Trail: Date Time.
编辑:官方文档:
除了 @Affe 的明确答案之外,以下组合似乎有效(截至 @GrzegorzSzpetkowski 的错误报告链接)
EDIT: official docs:
In addition to @Affe's clear answer, the following combinations seem to work (as of @GrzegorzSzpetkowski's bug report link)