tl;dr
You must understand the difference between a “normal” calendar year and a standard week-based year. The first/last few days of a calendar year may appear in the previous/next week-based year.

The calendar year of 2014 ends on December 31, 2014 of course. But that last day of 2014 lands in the next week-based-year, 2015.
The last day of week-based year 2014 ends on December 28, 2014 (calendar year). The first day of 2015 week-based-year is December 29, 2014 (calendar year). The calendar graphic above should make these facts clear.
2014-12-28 = 2014-W52-7
2014-12-29 = 2015-W01-1
2015-01-01 = 2015-W01-4
Ask for week-based week and year number:
myZonedDateTime.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR )
…and…
myZonedDateTime.get( IsoFields.WEEK_BASED_YEAR )
Simply put: For the last/first few days of the year, calling myZonedDateTime.getYear()
and myZonedDateTime.get( IsoFields.WEEK_BASED_YEAR )
may differ in results.
java.time
The modern approach uses the java.time classes that supplanted the troublesome old legacy date-time classes.
To manage dates I'm using the class Calendar. I'm saving timestamps in a DB, using Calendar timestamp = Calendar.getInstance();
Instead, use ZoneDateTime
to capture the current moment with a wall-clock time used by people of a certain region (time zone).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
I'm able to save and retrieve the dates from the DB using long values to store dates.
No, use an approriate data type, both in defining your database column and your Java code.
To store a moment, a specific moment on the timeline, in a SQL standard compliant database, define your column as TIMESTAMP WITH TIME ZONE
.
With JDBC 4.2 and later, you can directly exchange java.time objects with your database.
myPreparedStatement.setObject( … , zdt ) ;
Retrieve as an Instant
, a moment in UTC, may be the best route in the other direction. Then adjust into your desired time zone.
Instant instant = myResultSet.getObject( … , Instant.class ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
This is my problem: when I have a timestamp to store, I need to know if in the DB there is already a timestamp belonging to the same week. To do this I thought I could use the couple of methods: timestamp.get(Calendar.YEAR)
and timestamp.get(Calendar.WEEK_OF_YEAR)
Unfortunately, the meaning of a week in the confusing Calendar
class varies by locale.
Perhaps your own definition agrees with the standard ISO 8601 definition of a week.
- The first day is Monday, running through Sunday.
- Week number one of a week-based year contains the first Thursday of the calendar year.
- A week-based year has either 52 or 53 weeks.
- The first/last few days of a calendar year may appear in the previous/next week-based year.
Note that determining a week requires determining a date. And determining a date requires a time zone. For any given moment, the date varies around the globe by zone. So be clear on your use of time zone (ZoneId
) as seen above. In other words, a week number is contextual; it depends on a date in a particular zone.
Access a TemporalField
in ZonedDateTime::get
. The IsoFields
class defines the constants you need.
int week = zdt.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ) ;
int weekBasedYear = zdt.get( IsoFields.WEEK_BASED_YEAR ) ;
Even better, add the ThreeTen-Extra library to your project to use YearWeek
class.
org.threeten.extra.YearWeek yw = YearWeek.from( zdt ) ;
to uniquely identify a week, but this isn't true for weeks having days belonging to two consecutive years.
This is an example of what I get for the last week of the year 2014.
Saturday, December 27, 2014 year: 2014 week: 52
In a standard week, 2014-12-27 is in week 52.
YearWeek.from( LocalDate.parse( "2014-12-27" ) ).toString()
2014-W52
The end of 2014 happens to land in the following week-based year (2015).
YearWeek.from( LocalDate.parse( "2014-12-31" ) ).toString()
2015-W01
You can compare YearWeek
. Call equals
, isBefore
, or isAfter
methods.
boolean sameYearWeek = YearWeek.from( someZdt ).equals( YearWeek.from( otherZdt ) )
Terminology
So, be sure that in your documentation, code comments, and discussions, you always distinguish between a calendar year and week-based year. Never say "the year" as that is terribly ambiguous.
Add in financial years for even more confusion. And some industries have their own definitions of years and seasons and so on. So be careful with your terms.
Beware of calendaring software settings
Never assume the definition of a week number. Be sure the source of such a number has the same definition of week as you, such as ISO 8601 definition.
For example, the Calendar app supplied by Apple with macOS defaults to a "Gregorian" calendar definition of week. As for what that means, I do not know as I could not find any documentation as to their intent/definition. For ISO 8601 weeks, you must change a setting away from default.
About java.time
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
.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
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?