How to calculate Date from ISO8601 week number in Java

Solution 1:

UPDATE: The concepts presented here still apply, but the code is outmoded. The Joda-Time project, now in maintenance mode, advises migration to the java.time classes. See the java.time code in the Answer by Szulc.

Short Answer

DateTime dateTimeStart = new DateTime( "2003-W01-1", DateTimeZone.UTC ); // Joda-Time 2.4.
DateTime dateTimeStop = dateTimeStart.plusWeeks( 1 );

For details, read on.

Avoid j.u.Date

The old java.util.Date and java.util.Calendar classes bundled with Java are notoriously troublesome and should be avoided. Sun and its partners put a lot of neat stuff in the Java libraries, but not all of it is good. The date-time classes are perhaps the worst of it.

Furthermore those classes have weak support for ISO 8601 weeks. See this answer for details.

ISO Week Rules

You could write your own code using those classes, but I don't recommend this. The rules for calculation of ISO weeks are simple enough:

  • Week 1 has the first Thursday of the calendar year.
  • Monday is first day of week.

Joda-Time

In their place, the common replacement is a library called Joda-Time. This library includes excellent support for ISO weeks.

Simple to add to your project, just add a single .jar file.

Other Example Code

See this other answer of mine or this one for example code for getting a date-time from an ISO week number.

java.time

Java 8 has a new date-time framework, inspired by Joda-Time, found in the java.time package.

Adding Libraries

Java is built to mix libraries together. Doing so is one of the main purposes to object-oriented programming and late-binding. The comments on your question refer to the all-too-common situation where bosses irrationally or ignorantly forbid adding libraries. While there are valid reasons for such a ban, they are rare.

Forbidding adding libraries and jars in Java is like forbidding the hooking up of trailers on a fleet of vehicles equipped with a hitch.

The old date-time classes really are bad enough that many of us add Joda-Time to most any new project as a habit.

Half-Open

In date-time work, a common way to define a span of time is the "Half-Open" approach. This means the beginning is inclusive while the ending is exclusive. So a standard week begins on the first moment of a Monday, and ends on the first moment of the following Monday. Search StackOverflow.com for more discussion and examples.

Diagram of Half-Open approach to defining a week, going from start of Day 1 to start of Day 8

Textual Representation of ISO Week

The ISO 8601 standard defines ways to represent a standard week and even a day within that week.

Take the year, a hyphen, a W delimiter, and the week number represents the whole week: YYYY-Www. Add a hyphen and day-of-week number to pinpoint a day within that week: YYYY-Www-D.

Joda-Time understands this format as seen in the code example below.

Example Code

Here is some Joda-Time 2.4 code. Search StackOverflow.com for discussion and examples of these concepts. This Question and this Answer pretty much duplicate many others.

int year = 2003;
int week = 1; // Domain: 1 to 53.

// Build a String in ISO 8601 Week format: YYYY-Www-D
// Hard-coding a `1` for Monday, the standard first-day-of-week.
String input = ( String.format( "%04d", year ) + "-W" + String.format( "%02d", week ) + "-1" );

// Specify the time zone by which to define the beginning of a day.
DateTimeZone timeZone = DateTimeZone.UTC; // Or: DateTimeZone.forID( "America/Montreal" );

// Calculate beginning and ending, using Half-Open (inclusive, exclusive) approach.
DateTime dateTimeStart = new DateTime( input, timeZone );
DateTime dateTimeStop = dateTimeStart.plusWeeks( 1 );

// Use Joda-Time's tidy Interval class to represent the entire week. Use getters to access start and stop.
Interval weekInterval = new Interval( dateTimeStart, dateTimeStop );

// Is today in that week? Joda-Time has handy methods: contains, isBefore, isAfter, overlap.
boolean isTodayInThatWeek = weekInterval.contains( DateTime.now() );

Dump to console.

System.out.println( "input: " + input );
System.out.println( "dateTimeStart: " + dateTimeStart );
System.out.println( "dateTimeStop: " + dateTimeStop );
System.out.println( "interval: " + interval );
System.out.println( "isTodayInThatWeek: " + isTodayInThatWeek );

When run.

input: 2003-W01-1
dateTimeStart: 2002-12-30T00:00:00.000Z
dateTimeStop: 2003-01-06T00:00:00.000Z
interval: 2002-12-30T00:00:00.000Z/2003-01-06T00:00:00.000Z
isTodayInThatWeek: false

Solution 2:

The Java 8 / java.time way

In Java 8, you can use TemporalField combined with LocalDate::with(TemporalField, long) method for getting the correct week (of the week based year) and TemporalAdjuster combined with LocalDate::with(TemporalAdjuster) method to jump to the required day of the week, like this:

final int year = 2020;
final int weekNumber = 34;

LocalDate mondayOfWeek = LocalDate.of(year, Month.JUNE, 1)
                         .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
                         .with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, weekNumber)
                         

LocalDate sundayOfWeek = mondayOfWeek.plusDays(6);

Solution 3:

LocalDate date = 
  LocalDate.parse("2015 53", 
     new DateTimeFormatterBuilder().appendPattern("YYYY w")
    .parseDefaulting(WeekFields.ISO.dayOfWeek(), 1)
    .toFormatter()));