我试图获得与给定日期同一周的星期日。 在此期间我遇到了这个问题:
Calendar calendar = Calendar.getInstance(Locale.GERMANY);
calendar.set(2017, 11, 11);
calendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
System.out.println(calendar.getTime().toString());
结果" Sun Jan 07 11:18:42 CET 2018"
但
Calendar calendar2 = Calendar.getInstance(Locale.GERMANY);
calendar2.set(2017, 11, 11);
calendar2.getTime();
calendar2.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
System.out.println(calendar2.getTime().toString());
给了我正确的日期"太阳12月17日11:18:42 CET 2017"
有人可以解释为什么第一个例子就是这样吗?这是真的有意吗?
由于
答案 0 :(得分:10)
基本上,Calendar
API很糟糕,应该避免。它没有明确记录,但我认为我知道它在哪里,它在这种情况下表现得像预期的那样。我的意思是它遵循API作者的意图,而不是您或任何人阅读您的代码的意图......
可以通过调用set方法来设置日历字段值。在需要计算其时间值(Epoch的毫秒数)或日历字段的值之前,不会解释日历中设置的任何字段值。调用get,getTimeInMillis,getTime,add和roll涉及这样的计算。
然后:
从日历字段计算日期和时间时,可能没有足够的信息用于计算(例如,只有年月和月份没有日期),或者可能存在不一致的信息(例如7月15日星期二, 1996年(格里高利) - 1996年7月15日实际上是星期一)。日历将解析日历字段值,以便按以下方式确定日期和时间。
如果日历字段值中存在任何冲突,则日历会优先考虑最近设置的日历字段。以下是日历字段的默认组合。将使用由最近设置的单个字段确定的最新组合。
对于日期字段:
- 年+月+ DAY_OF_MONTH
- 年+月+周W_K_MONTH + DAY_OF_WEEK
- 年+月+ DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
- 年+ DAY_OF_YEAR
- 年+ DAY_OF_WEEK + WEEK_OF_YEAR
在第一个例子中,最后一个字段集是"星期几"意味着它将使用YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
计算(我认为)。年和月已设定为2017年12月,但是星期是当前的周,即2018年1月的第5周......所以,当您说要设置星期几时星期天,它在第5周和第34周找到了星期天。 2017年12月。12月只有4个星期,所以它有效地推进了它......我想。这一切都很混乱,基本上你不应该考虑这个问题。
在第二个示例中,调用getTime()
"锁定"您指定的年/月/日,并计算其他字段。当您设置星期几时,然后在现有的计算字段中对其进行调整。
基本上,尽可能避免使用此API。使用java.time
,这是远更干净的日期/时间API。
答案 1 :(得分:1)
正如Jon Skeet所说,避免Calendar
。对于你的情况来说它真的很可怕,而且它的设计很糟糕。而是做
WeekFields weekFieldsForLocale = WeekFields.of(Locale.GERMANY);
// To find out which number Sunday has in the locale,
// grab any Sunday and get its weekFieldsForLocale.dayOfWeek()
int dayNumberOfSundayInLocale = LocalDate.now()
.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY))
.get(weekFieldsForLocale.dayOfWeek());
LocalDate date = LocalDate.of(2017, Month.DECEMBER, 11);
LocalDate sunday
= date.with(weekFieldsForLocale.dayOfWeek(), dayNumberOfSundayInLocale);
System.out.println(sunday);
这将打印预期日期
2017-12-17
正如其他人已经提到的,解决方案是使用现代Java日期和时间API java.time
。通常它也可以更好地使用。一个很好的功能是我正在使用的LocalDate
类。这是一个没有时间的日期,这似乎更准确地符合您的要求Calendar
。
如果上述内容看起来很复杂,那是因为我认为你知道,“同一周的星期日”在不同的语言环境中意味着不同的东西。在德国遵循的国际标准中,星期一开始,因此星期日是一周的最后一天。例如,在美国标准中,星期日是本周的第一天。 WeekFields.dayOfWeek()
将一周中的日期从1到7编号,所以当我们想要将日期设置为星期日时,我们首先需要找出星期日在这个编号中得到的数字(德国7,美国1) )。因此,对于任何星期日,获取其weekFieldsForLocale.dayOfWeek()
值,然后使用此值将星期几设置为星期日。这是必要的原因是with()
方法是如此通用,因此被设计为仅接受数值;我们不能只传递一个DayOfWeek
对象。
如果我将Locale.US
替换为代码,我会得到2017-12-10
,这是星期日是一周中第一天的日历的正确星期日。如果您确定只希望您的代码适用于德国,那么您当然可以对7
进行硬编码(请使用一个非常具有说明性的名称使其成为常量)。
链接: Oracle Tutorial Date Time解释如何使用java.time
。网上还有其他资源(只是避免过时的建议java.util.Calendar
: - )