2

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

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
hiway
  • 3,906
  • 10
  • 34
  • 57
  • So what is your question? – mentallurg Jan 06 '20 at 03:38
  • @mentallurg, according to DOC, week year for 1998-01-01 shoule be 1997, but junit result shows it is 1998, not 1997. – hiway Jan 06 '20 at 03:42
  • hmm maybe you are confusing the docu, or maybe you are confusing what `calendar.getFirstDayOfWeek()` is actually returning. I would guess that in case the first day has been set to SUNDAY, you would get back that your first week day of the year 1998 should be 4-1-1998 ?? Maybe you are looking for this one ? `calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek());` and remove the manually setting on day. – AntJavaDev Jan 06 '20 at 03:53
  • 1
    @hiway: I have just checked it: *getWeekYear()* return 1997 for yoiur code. Show the code how you get the year of week. – mentallurg Jan 06 '20 at 04:08
  • @mentallurg , `calendar.getWeekYear()` return 1997, like `https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html` said `Y` means `week year`, so I use `YYYY-MM-dd` to format `1998-01-01` expecting to get `1997-01-01`. There are some other websites like `https://www.juandebravo.com/2015/04/10/java-yyyy-date-format/` said not use `YYYY` to format date. so I test the java DOC said, it get a weird result. – hiway Jan 06 '20 at 05:47
  • 1
    I recommend you don’t use `Calendar` and `SimpleDateFormat`. Those classes are poorly designed and long outdated, the latter in particular notoriously troublesome. Instead use `LocalDate` and `DateTimeFormatter`, both from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Jan 06 '20 at 18:15

1 Answers1

4

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.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • good explanation, our project migrates from java7 to java8, CTO asks us not to use `YYYY-MM-dd` to format date, after reading Java DOC example I got confused. Now it is clear to me and I will use new classes. Thanks very much. – hiway Jan 07 '20 at 09:16