This started as a simple error: I had YYYY
instead of yyyy
in my format string for a SimpleDateFormat
object. But I\'m totally baffled
A week can be defined in different ways. For example, in the United States, the first day of the week is considered to be Sunday most often while in Europe and many other places the first day is Monday.
Likewise, the week of a week-based year can also be defined in different ways.
The legacy classes you are using implicitly use the definitions specified by a Locale
. The locale being applied is also implicit, using the JVM’s current default Locale
if you do not otherwise specify.
For even more interesting details about the difficulty in defining a week, see this Question, Different behavior of WeekFields on JVM 8 and JVM 10
There is a practical international standard for date-time handling, ISO 8601.
The ISO 8601 definition of a week is:
I suggest using the ISO 8601 standard definition whenever possible. This standard definition is simple and logical, with increasing adoption across industries.
The java.time classes offer some support for week of week-based year in the WeekFields class.
LocalDate ld = LocalDate.of( 2019 , Month.JANUARY , 1 ) ;
long week = ld.get( WeekFields.ISO.weekOfWeekBasedYear() ) ;
See this code run live at IdeOne.com.
ld.toString(): 2019-01-01
week: 1
org.threeten.extra.YearWeek
But if doing much of this work, I suggest adding the ThreeTen-Extra library to your project. You will find the YearWeek class to be helpful. This class offers several handy methods such as generating a LocalDate
for any day within that week.
LocalDate ld = LocalDate.of ( 2019 , Month.JANUARY , 1 );
YearWeek yw = YearWeek.from ( ld );
LocalDate startOfWeek = yw.atDay ( DayOfWeek.MONDAY );
ld.toString(): 2019-01-01
yw.toString(): 2019-W01
startOfWeek.toString(): 2018-12-31
Notice how the first day of the year in the week-based year of 2019 is a date from the previous calendar year, 2018 rather than 2019.
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.*
classes.
Where to obtain the java.time classes?
It's simple: December 27 2015 is day 1 of week 1 of week-year 2016 (and December 27 2026 is day 1 of week 1 of week-year 2027). This can be verified by adding these lines:
SimpleDateFormat odf = new SimpleDateFormat("YYYY-ww-u");
System.out.println(odf.format(d1));
System.out.println(odf.format(d2));
System.out.println(odf.format(d3));
If a SimpleDateFormat
outputs a date it can use all fields: year, month, day, day of week, week of month, week in year, week-year etc.
On parsing, SimpleDateFormat
expects a matching set of values: either day, month, year or day of week, week in year, week-year. Since you supplied a week-year but did not supply day of week and week in year, those to values have been assumed as 1.
The actual values depend on your locale:
(see https://docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html#week_and_year)
On my system (using de-ch locale, with "EEE MMM dd HH:mm:ss zzz yyyy - YYYY-ww-u" as format) I get
Mo Jan 04 00:00:00 MEZ 2016 - 2016-01-1 Mo Jan 04 00:00:00 MEZ 2016 - 2016-01-1 Mo Jan 04 00:00:00 MEZ 2027 - 2027-01-1