2

I have some problem with Calendar in Java. When I add weeks to calendar it dosen't work correctly in some cases. Eg. set week_of_yer to 3 and then add -3 weeks ;) works good in most cases but not in year 2010, 2016 etc. (This years are after year with 53 weeks) . It looks like error in JDK, I think they change year when week number is 52 :D Some simple code:

Calendar calendar = new GregorianCalendar();
calendar.set(Calendar.YEAR, 2016);
calendar.set(Calendar.WEEK_OF_YEAR, 3);
calendar.add(Calendar.WEEK_OF_YEAR, -3);
System.out.println("rolled week: " + calendar.get(Calendar.WEEK_OF_YEAR) + " - " + calendar.get(Calendar.YEAR));

Result of this code is: rolled week: 53 - 2016 :D

Should be 53 - 2015 in all other cases it works as it should.

Does anyone has similar problem?

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
Nico
  • 21
  • 2
  • 1
    Did you consider using jodatime? It's a date handling library which is considered far superior to Java's standard Date and Calendar classes. It also has a way to create a new DateTime a specific Period defined as "3 weeks" from an existing DateTime. http://joda-time.sourceforge.net/ – Philipp Nov 23 '12 at 22:48
  • I forgot abut jodatime... it is great library for time. – Nico Nov 24 '12 at 15:06

3 Answers3

4

The problem is that you're showing the year rather than the week year. You don't need to do arithmetic with the calendar to show this. Basically you're displaying January 1st 2016. The year of that is 2016, but the week year (i.e. the year within which the week-of-year is calculated) is 2015.

Unfortunately, as far as I can see, Java doesn't have a way of fetching the week year. You'd be best off using Joda Time, which lets you do all this easily:

import org.joda.time.*;

public class Test {
    public static void main(String[] args) throws Exception {
        LocalDate date = new LocalDate(2016, 1, 1);
        System.out.println(date);
        System.out.println(date.getWeekOfWeekyear());
        System.out.println(date.getWeekyear());
    }    
}

Output:

2016-01-01
53
2015
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
0

Jon you are right. Today I noticed where is problem it is exactly what you said. I'm interested only in week number and year, so before adding number to week I set day of week to monday. For me it works.

Calendar calendar = new GregorianCalendar();
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.WEEK_OF_YEAR, week);
calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
calendar.add(Calendar.WEEK_OF_YEAR, -51);
Nico
  • 21
  • 2
0

The Answer by Jon Skeet is correct and should be accepted. You are confusing week-of-year with a calendar-year versus a week-based-year.

Week-based Year

If we are talking about the standard ISO 8601 definition of a week, that means:

  • Week runs Monday-Sunday.
  • Week # 1 is the week containing the first Thursday of the calendar year.

Say that first Thursday is January 2, which means Wednesday the day before is the 1st, and the prior Monday & Tuesday are the last days of the prior calendar year but are in the following week-based year. So you cannot mix up calendar year numbers with week-based year numbers.

Avoid legacy date-time classes

Also, you are using troublesome old date-time classes such as Calendar which are now legacy, supplanted by the java.time classes.

LocalDate

First get the current date. The LocalDate class represents a date-only value without time-of-day and without time zone.

A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.

Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( z );

IsoFields

Get the ISO 8601 week number by using the IsoFields class. And get the year number of the week-based year calendar.

int weekNumber = today.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR );
int yearNumberOfWeekBasedYear = today.get( IsoFields.WEEK_BASED_YEAR );  // Around end/beginning of year, may be different than calendar-year number.

Let's take another specific example, the first day of 2017. Notice how its calendar-year is of course 2017 but because it lands on a Sunday it is in the last week (# 52) of the week-based year 2016 (not 2017!).

LocalDate firstOf2017 = LocalDate.of ( 2017 , Month.JANUARY , 1 );

DayOfWeek firstOf2017DayOfWeek = firstOf2017.getDayOfWeek ();
int weekNumber = firstOf2017.get ( IsoFields.WEEK_OF_WEEK_BASED_YEAR );
int yearNumberOfWeekBasedYear = firstOf2017.get ( IsoFields.WEEK_BASED_YEAR );  // Around end/beginning of year, may be different than calendar-year number.
int yearNumberOfCalendarYear = firstOf2017.getYear ();

Dump to console.

System.out.println ( "firstOf2017.toString(): " + firstOf2017 );
System.out.println ( "firstOf2017DayOfWeek.toString(): " + firstOf2017DayOfWeek );
System.out.println ( "weekNumber: " + weekNumber );
System.out.println ( "yearNumberOfWeekBasedYear: " + yearNumberOfWeekBasedYear );
System.out.println ( "yearNumberOfCalendarYear: " + yearNumberOfCalendarYear );

firstOf2017.toString(): 2017-01-01

firstOf2017DayOfWeek.toString(): SUNDAY

weekNumber: 52

yearNumberOfWeekBasedYear: 2016

yearNumberOfCalendarYear: 2017

Week math

You can subtract weeks with the LocalDate class.

LocalDate ld = firstOf2017.minusWeeks( 3 );

YearWeek

You will probably want to add the ThreeTen-Extra library to your project to access the YearWeek class.

YearWeek yearWeek = YearWeek.of( 2016 , 3 ); // Week # 3 of week-based year 2016. 

Get one of the days of that week by specifying a DayOfWeek enum object.

LocalDate ld = yearWeek.atDay( DayOfWeek.MONDAY );

2016-01-18

Add three weeks.

LocalDate threeWeeksLater = ld.plusWeeks( 3 );

2016-02-08

Get that new date’s week number.

YearWeek yearWeek2 = YearWeek.from( threeWeeksLater );
int weekNumber = yearWeek2.getWeek();
int yearNumberOfWeekBasedYear = yearWeek2.getYear();

Generate standard ISO 8601 string representation.

String output = yearWeek2.toString();

2016-W06


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.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Community
  • 1
  • 1
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154