hiway
hiway

Reputation: 3976

SimpleDateFormat week year

java8 GregorianCalendar DOC

A week year is in sync with a WEEK_OF_YEAR cycle. All weeks between the first and last weeks (inclusive) have the same week year value. Therefore, the first and last days of a week year may have different calendar year values.

For example, January 1, 1998 is a Thursday. If getFirstDayOfWeek() is MONDAY and getMinimalDaysInFirstWeek() is 4 (ISO 8601 standard compatible setting), then week 1 of 1998 starts on December 29, 1997, and ends on January 4, 1998. The week year is 1998 for the last three days of calendar year 1997. If, however, getFirstDayOfWeek() is SUNDAY, then week 1 of 1998 starts on January 4, 1998, and ends on January 10, 1998; the first three days of 1998 then are part of week 53 of 1997 and their week year is 1997.

As the DOC said, if I set FirstDayOfWeek to SUNDAY and MinimalDaysInFirstWeek to 4, so week year of 1998-01-01 should be 1997, but junit result is 1998.

Calendar calendar = Calendar.getInstance();
    calendar.set(Calendar.YEAR, 1998);
    calendar.set(Calendar.MONTH, 0);
    calendar.set(Calendar.DAY_OF_MONTH, 1);
    calendar.setMinimalDaysInFirstWeek(4);
    calendar.setFirstDayOfWeek(Calendar.SUNDAY);

    System.out.println(calendar.getTimeZone());
    System.out.println(calendar.getMinimalDaysInFirstWeek());
    System.out.println(calendar.getFirstDayOfWeek());

    SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd");
    System.out.println(sdf.format(calendar.getTime()));

junit result is: sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=29,lastRule=null]

4

1

1998-01-01

Upvotes: 3

Views: 799

Answers (1)

Anonymous
Anonymous

Reputation: 86203

TL;DR: The formatter uses its own week numbering

The date formatter is using its own week numbering scheme, not that of the date (in your case that of the Calendar object). I am demonstrating using java.time the modern Java date and time API. Specifically I am using a LocalDate. A LocalDate is always a date in the ISO calendar system.

    LocalDate date = LocalDate.of(1998, Month.JANUARY, 1);
    DateTimeFormatter baseFormatter = DateTimeFormatter.ofPattern("YYYY-MM-dd");

    DateTimeFormatter chinaFormatter = baseFormatter.withLocale(Locale.CHINA);
    System.out.println("China:   " + date.format(chinaFormatter));

    DateTimeFormatter franceFormatter = baseFormatter.withLocale(Locale.FRANCE);
    System.out.println("France:  " + date.format(franceFormatter));

    DateTimeFormatter irelandFormatter = baseFormatter.withLocale(Locale.forLanguageTag("en-IE"));
    System.out.println("Ireland: " + date.format(irelandFormatter));

Output from this snippet is:

China:   1998-01-01
France:  1998-01-01
Ireland: 1997-01-01
  • If your default locale is China, your formatter was using China weeks. They begin on Sunday, and week 1 is the week that contains January 1 (like in the US, as another example). So January 1 of 1998 must belong to week year 1998.
  • France uses ISO numbering: The week begins on Monday, and as you said, in this case week 1 of 1998 begins on December 29, 1997. So we get 1998 again.
  • Ireland is one of the few countries using the mixed scheme that you were trying to obtain in your question: Minimal days in first week is 4 (as in ISO), and Sunday is the first day of the week (as in China and the US). So using a formatter with Ireland as locale country gives you the result you were trying to get, 1997.

A different way of obtaining a week year of 1997 from your date is to put your own WeekFields object together. Then you don’t need to worry about locale.

    WeekFields myWeekFields = WeekFields.of(DayOfWeek.SUNDAY, 4);
    int weekYear = date.get(myWeekFields.weekBasedYear());
    System.out.println("Week year: " + weekYear);

Week year: 1997

java.time??

The date and time classes that you were using, Calendar and SimpleDateFormat, are poorly designed and long outdated.

It seems from your document link that you are using Java 8, and if so, there’s absolutely no reason why you should struggle with the old classes. Use java.time as I do above. A backport for Java 6 and 7 exists too.

How come the code in the question didn’t work?

You were taking the time out of your Calendar using calendar.getTime(). This returns a Date (another poorly designed and long outdated class). A Date is a point in time, nothing more. It hasn’t got any notion of week number or week year. You passed this Date to your SimpleDateFormat, which had no way of detecting that the Date had originally come from a Calendar object with a certain setting of days in first week and first day of week. Instead the SimpleDateFormat uses its own definition of weeks. It always does that. Since you had not explicitly set a locale or calendar for the SimpleDateFormat, it used the default locale of your JVM. This produced a week year of 1998, as it would in the vast majority of locales.

Upvotes: 4

Related Questions