As per https://en.wikipedia.org/wiki/ISO_8601#Week_dates, Weekdays start on Monday. But, from Java, if you try to extract week number in two different ways, two different outputs come if the date is a Sunday.
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class TestDate {
public static void main(String[] args) throws ParseException {
final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
final SimpleDateFormat weekFormatter = new SimpleDateFormat("ww");
String date = "2018-10-21";
System.out.println(weekFormatter.format(formatter.parse(date)));
Calendar calendar = Calendar.getInstance();
calendar.setFirstDayOfWeek(Calendar.MONDAY);
calendar.setTime(formatter.parse(date));
System.out.println(calendar.get(Calendar.WEEK_OF_YEAR));
}
}
Output:
43
42
Is this an inconsistency?
This is just a test program I wrote to reproduce the issue, I noticed the problem in Hive, like the following:
0: jdbc:hive2://zk0-something> select from_unixtime(t, 'ww'), weekofyear(from_unixtime(t, 'yyyy-MM-dd')) from (select 1540122033 as t) a;
+------+------+--+
| _c0 | _c1 |
+------+------+--+
| 43 | 42 |
+------+------+--+
1 row selected (0.388 seconds)
0: jdbc:hive2://zk0-something>
解决方案
java.time
String date = "2018-10-21";
LocalDate ld = LocalDate.parse(date);
int weekOfYear = ld.get(WeekFields.ISO.weekOfYear());
System.out.println(weekOfYear);
Output:
42
Since you are interested in the ISO 8601 rules for week numbers, use WeekFields.ISO for getting week related data from a LocalDate. You may also use a formatter if you like:
DateTimeFormatter weekFormatter = DateTimeFormatter.ofPattern("ww", Locale.FRANCE);
System.out.println(ld.format(weekFormatter));
Output is the same:
42
The locale passed to DateTimeFormatter.ofPattern determines the week scheme. If I pass Locale.US instead, I get 43.
I recommend you use java.time, the modern Java date and time API, and stay away from the old date-time classes like SimpleDateFormat and Calendar. The old ones were poorly designed and the modern ones are much nicer to work with.
What went wrong in your code?
Both the outdated SimpleDateFormat class and the modern DateTimeFormatter take their week numbering scheme from their locale. If no locale is specified for the formatter, it uses the default locale of the JVM. So if the JVM has American locale, for example, the formatter will print 43 in your first example because in the US Sunday October 21 this year was in week 43. If the locale is French, it will print 42 because that day was in week 42 in France. France follows the ISO 8601 standard, the USA does not.
In your example, setting the Calendar’s first day of week to Monday causes the week number to be 42 as you had expected. This will not always be the case, however. Week numbers are defined not only by the first day of the week but also by the definition of week 1. From your link:
The first ISO week of a year may have up to three days that are
actually in the Gregorian calendar year that is ending; if they are
Monday, Tuesday and Wednesday. Similarly, the last ISO week of a year
may have up to three days that are actually in the Gregorian calendar
year that is starting; if they are Friday, Saturday, and Sunday. The
Thursday of each ISO week is always in the Gregorian calendar year
denoted by the ISO week-numbering year.
The American definition of which week is week 1 is different: In the US January 1 is always in week 1. Therefore if your Calendar is created with American locale, setting its first day of week to Monday is not enough to make is follow ISO 8601 rules. Coincidentally, for 2018 the week numbers agree, though.