What is the motivation for two different week-base

2019-01-12 06:17发布

问题:

These are the two fields in the package java.time.temporal:

IsoFields.WEEK_BASED_YEAR

WeekFields.ISO.weekBasedYear()

ISO-8601 defines a so-called week date besides the other two kinds of date, namely the usual calendar date (consisting of year, month and day-of-month) and the ordinal date (consisting of year and day-of-year). A weekdate is defined in the format YYYY-'W'ww-e. w stands for the week-of-year, e for the numerical ISO-day-of-week. Y stands for the week-based-year and is identical to calendar year with the exception at the begin or end of calendar year, because a week-based-year is bound to the week cycle which can eventually start in the previous year. Two rules are important to understand how a week date is formed:

  1. Weeks always start at Monday.
  2. The first week of a calendar year is the week which contains at least four days.

At first glance both JSR-310-fields appear to be identical because ISO-8601 only mentions one kind of week-based-year. But wait, surprise. Let's consider following code example:

LocalDate date1 = 
  LocalDate.of(2000, 2, 29).with(IsoFields.WEEK_BASED_YEAR, 2014);
System.out.println("IsoFields-Test: " + date1); // output: 2014-03-01

LocalDate date2 = 
  LocalDate.of(2000, 2, 29).with(WeekFields.ISO.weekBasedYear(), 2014);
System.out.println("WeekFields-Test: " + date2); // output: 2014-02-25

While I very well understand the second variation I am really astonished to see the different result for first date which is using the "official" ISO-8601-reference in its classname. To explain the calculated results:

The date 2000-02-29 corresponds to 2000-W09-2 in ISO-weekdate-notation while 2014-02-25 corresponds to 2014-W09-2 preserving the week-of-year and the day-of-week. So far so fine. This preserving characteristics for smaller fields is similar to the rule how to change the calendar year (which should in most cases keep the month and day-of-month in a calendar date unchanged).

But what is about the result 2014-03-01? Here the algorithm has simply added four days to the corresponding week-date in order to take in account the difference in the field "day-of-month" (29 versus 25). I have not found any source or official documentation for this behaviour. Does anybody know where we can find the justification for the difference between these two fields? Any documentation available about the algorithmic behaviour?

UPDATE:

Now I tried to test the self-consistency of the new API with this expression in order to find out which of the two fields is better supported:

System.out.println(
  "14 week-based-years later = "
  + LocalDate.of(2000, 2, 29).plus(14, IsoFields.WEEK_BASED_YEARS));

The output is 2014-03-01 similar to the described case of IsoFields.WEEK_BASED_YEAR, although I still find the result 2014-02-25 (=2014-W09-2) much more logical. Since this temporal unit is also found in the class IsoFields so far the behaviour is self-consistent within the class IsoFields. Looks like an undocumented and non-intuitive "feature".

I am using the version: java.runtime.version=1.8.0-b132

More testing:

LocalDate d = LocalDate.of(2014, 3, 1); // 2014-W09-6
System.out.println(
  "week-of-year in 2014-03-01: " 
  + d.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR));
System.out.println(
  "day-of-week in 2014-03-01: " 
  + d.get(ChronoField.DAY_OF_WEEK));

LocalDate later = d.plus(14, IsoFields.WEEK_BASED_YEARS); // 2028-03-02 = 2028-W09-4
System.out.println(
  "14 week-based-years later = " 
  + later);
System.out.println(
  "week-of-year in " + later + ": " 
  + later.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR));
System.out.println(
  "day-of-week in " + later + ": " 
  + later.get(ChronoField.DAY_OF_WEEK));

Output:

week-of-year in 2014-03-01: 9
day-of-week in 2014-03-01: 6
14 week-based-years later = 2028-03-02
week-of-year in 2028-03-02: 9
day-of-week in 2028-03-02: 4

I do not recognize any clear rule. Neither the day-of-week is preserved nor the day-of-month is preserved while adding 14 week-based-years. So this is an extra question: What is the rule behind IsoFields.WEEK_BASED_YEARS? Maybe JSR-310-team can enlighten us?

回答1:

This is a bug in IsoFields which managed to slip through because this method was untested (sorry about that). There should be very little observable difference between IsoFields and WeekFields.ISO when implemented correctly.

See the bug report and patch which will eventually work through the system and be fixed.

Note, testing revealed that getting the field is fine, the bug only affected the with/adjustInto method of WEEK_BASED_YEAR. The unit WEEK_BASED_YEARS is affected because the addition is implemented internally by reusing the broken WEEK_BASED_YEAR.

Update 2014-08-28: This was one of 14 java.time bugs fixed in 8u20.