11

I am working on a project. There I should find the total weeks of a year. I tried with the following code, but I get the wrong answer: 2020 has 53 weeks, but this code gives 52 weeks.

Where have I gone wrong in this code?

package com.hib.mapping;

import java.time.LocalDate;
import java.time.temporal.WeekFields;
import java.util.Calendar;
import java.util.GregorianCalendar;

import org.joda.time.DateTime;

public class TestWeek {

    public static void main(String args[]) {
        System.out.println(getWeeks());
    }

    public static int getWeeks() {

        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, 2020);
        cal.set(Calendar.MONTH, Calendar.JANUARY);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        GregorianCalendar gregorianCalendar = new GregorianCalendar();

        int weekDay = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (gregorianCalendar.isLeapYear(2020)) {
            if (weekDay == Calendar.THURSDAY || weekDay == Calendar.WEDNESDAY)
                return 53;
            else
                return 52;
        } else {
            if (weekDay == Calendar.THURSDAY)
                return 53;
            else
                return 52;
        }

    }

}

Output:

52

halfer
  • 19,824
  • 17
  • 99
  • 186
Kumaresan Perumal
  • 1,926
  • 2
  • 29
  • 35
  • 1
    Not exactly a duplicate, but suggested reading: https://stackoverflow.com/questions/44197872/java-time-get-max-number-of-weeks-for-particular-year?noredirect=1 – Federico klez Culloca Jan 27 '20 at 17:17
  • 2
    Suggest providing your definition of a week. –  Jan 27 '20 at 17:17
  • 4
    It is 2020. Please avoid using the troublesome old legacy date api around `Date` and `Calendar`. Use `java.time` instead, it is much better and simpler. – Zabuzard Jan 27 '20 at 17:21
  • If (year is leap) and (Jan 1 is Sunday) then 54 else 53. – Akina Jan 27 '20 at 17:24
  • can you give me some code? – Kumaresan Perumal Jan 27 '20 at 17:24
  • [nebekerdev](https://stackoverflow.com/users/12787664/nebekerdev) would like to comment: he reason why it always returns 52 is that you are subtracting one from the int weekday before checking that it is equal to the calendar day, which goes from 1 - 7. – Ole V.V. Jan 27 '20 at 19:27
  • What is your definition of a week? In your code sample, the definition of a week varies by `Locale`. – Basil Bourque Jan 28 '20 at 04:58

6 Answers6

7

tl;dr

For a standard ISO 8601 week, use the YearWeek class from ThreeTen-Extra library with a ternary statement.

YearWeek          // Represents an entire week of a week-based-year.
.of( 2020 , 1 )   // Pass the number of the week-based-year (*not* calendar year), and a week number ranging from 1 to 52 or 1 to 53.
.is53WeekYear()   // Every standard week-based-year has either 52 or 52 complete weeks.
? 53              // Ternary statement returns 53 if the predicate returns True, …
: 52              // … otherwise returns 52. 

That is, YearWeek.of( 2020 , 1 ).is53WeekYear() ? 53 : 52

Define “week”

You need to define a week. In your code sample, the definition of week varies by the JVM’s current default Locale. So your results may vary at runtime.

Your code also uses terrible date-time classes that were supplanted years ago by the modern java.time classes. Stop using GregorianCalendar & Calendar; they were replaced for good reasons.

ISO 8601 week

The ISO 8601 standard defines a week as:

  • Weeks start on Monday, end on Sunday.
  • Week # 1 has the first Thursday of the calendar-year.

That definition means:

  • The first and last few days of a week-based-year may be the trailing/leading days of the previous/following calendar-year.
  • The week-based-year has either 52 or 53 complete weeks.

If your definition differs, see the Answer by Ole V.V..

YearWeek:is53WeekYear

If this matches your definition, then add the ThreeTen-Extra library to your project to extend the java.time functionality built into Java 8 and later. You then have access to the YearWeek class.

ZoneId z = ZoneId.of( "America/Montreal" ) ;
YearWeek yearWeekNow = YearWeek.now( z ) ;
boolean is53WeekYear = yearWeekNow.is53WeekYear() ;

int weeksLong = yearWeekNow.is53WeekYear() ? 53 : 52 ;

To ask about a particular week-based-year, just arbitrarily pick any week of the year. For example, for the week-based year 2020 we ask for week # 1.

int weeksLong = YearWeek.of( 2020 , 1 ).is53WeekYear() ? 53 : 52 ;
LocalDate weekStart = YearWeek.of( 2020 , 1 ).atDay( DayOfWeek.MONDAY ) ;

weeksLong = 53

weekStart = 2019-12-30

Notice how the first day of the week-based-year of 2020 is from the calendar-year 2019.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • I am in India. Should I specify the country name? – Kumaresan Perumal Jan 28 '20 at 06:41
  • @KumaresanPerumal No, the timezone is there for `now`. Since you want to get the week count for a specific year, instead of the current year, just use `YearWeek.of(yourYear, 1)`. – Sweeper Jan 28 '20 at 06:44
  • @KumaresanPerumal Wikipedia keeps [this list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) of time zone names. May be slightly out-of-date. As far as I know, all of India uses the time zone is `Asia/Kolkata`, currently with an offset of +05:30. In Java code: `ZoneId.of( "Asia/Kolkata" )`. For more info and history, see Wikipedia, [*Time in India*](https://en.wikipedia.org/wiki/Time_in_India). – Basil Bourque Jan 28 '20 at 07:26
6

Using the Wikipedia definition here. A year has 53 weeks if 1st Jan is a Thursday, or 31st Dec is a Thursday, otherwise it has 52 weeks. This definition is equivalent to the one you used. I think this is a way easier condition to check for, as you don't need to check for leap years.

Using the Java 8 java.time APIs:

int year = 2020;
boolean is53weekYear = LocalDate.of(year, 1, 1).getDayOfWeek() == DayOfWeek.THURSDAY ||
        LocalDate.of(year, 12, 31).getDayOfWeek() == DayOfWeek.THURSDAY;
int weekCount = is53weekYear ? 53 : 52;
Sweeper
  • 213,210
  • 22
  • 193
  • 313
4

The flexible solution

This should work for any week numbering scheme that can be represented in a WeekFields object.

public static int noOfWeeks(WeekFields wf, int year) {
    LocalDate lastDayOfYear = YearMonth.of(year, Month.DECEMBER).atEndOfMonth();
    if (lastDayOfYear.get(wf.weekBasedYear()) > year) { // belongs to following week year
        return lastDayOfYear.minusWeeks(1).get(wf.weekOfWeekBasedYear());
    }
    else {
        return lastDayOfYear.get(wf.weekOfWeekBasedYear());
    }
}

The idea is to find the week number of the last week of the week based year. I try first with 31 December, but that may be in the first week of the following year. If so, I go one week back.

I have tested pretty thoroughly with WeekFields.ISO, not so much with other WeekFields objects, but as I said, I believe it works.

If you know for a fact that you will always need ISO 8601 weeks, I think you should go with one of the good answers by Sweeper and by Basil Bourque. I posted this in case you needed a more flexible solution that would work with other week numbering schemes too.

Use java.time

The code in your question is funny in that it imports classes both from Joda-Time and from java.time, yet uses the old Calendar and GregorianCalendar from Java 1.1. These classes were poorly designed and are now long outdated, you should not use them. Joda-Time is in maintenance mode, java.time has taken over after it. Which is what I use and recommend that you use.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
1

I think this should work just fine as well:

int year = 2020;
long numOfWeeks = LocalDate.of(year, 1, 1).datesUntil(LocalDate.of(year, 12, 31), Period.ofDays(7)).count();
System.out.println("Weeks: " + numOfWeeks);
Tim Hunter
  • 826
  • 5
  • 10
  • Nice thought. It gives the correct result for 2020 (53 weeks) as well as 2019 and 2021 (52 weeks each). It may be a little hard to convince oneself, though. – Ole V.V. Jan 27 '20 at 19:31
  • It seems to give 53 weeks for all leap years, though, which is not correct. – Ole V.V. Jan 27 '20 at 19:53
  • @OleV.V. Hmm, fair enough. I'm curious how the calculations of LocalDate work under the hood then. Stupid Earth and it's slightly imperfect time cycle, making things more complicated than they have to be. – Tim Hunter Jan 27 '20 at 20:11
  • [LocalDate.datesUntil](https://docs.oracle.com/javase/9/docs/api/java/time/LocalDate.html#datesUntil-java.time.LocalDate-) is Java 9+ – ZhekaKozlov Jan 28 '20 at 08:48
0

Below code works for me.

public static int getTotalWeeksInYear(int year){
        int totalWeeks=0;
        Calendar calendar=Calendar.getInstance();
        for(int month=0;month<12;mmonth++){
            int day=1;
            do{
                calendar.set(year, month, day);
                if(calendar.get(Calendar.DAY_OF_WEEK)==5)
                    totalWeeks++;
                day++;
            }while (day <=calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        }
        return totalWeeks;
}

Using Java.time

public static long getTotalWeekByLocalDate(LocalDate ldate) {

        long weeksInYear = IsoFields.WEEK_OF_WEEK_BASED_YEAR.rangeRefinedBy(ldate).getMaximum();

        return weeksInYear;
    }
JAVA_CAT
  • 737
  • 3
  • 13
  • 33
  • 2
    I certainly recommend the java.time code. Not so much that it’s shorter, it’s first and foremost vastly easier to understand. Why didn’t you put it first? – Ole V.V. Jan 05 '21 at 17:48
  • 1
    I too support java.time. But in the question user trying to solve issue using Calendar class. – JAVA_CAT Jan 06 '21 at 04:43
  • What the question really meant to ask about is unclear. The code imports `java.time.LocalDate` and `java.time.temporal.WeekFields`. My interpretation is that either java.time is really what the OP wanted, or she or he had not decided at all. – Ole V.V. Jan 06 '21 at 05:21
-1

After trying a lot in java 8. I could not find a solution. then I prepared Joda date and time dependency. It gave me a good answer as I expected

code:

for (int i = 2020; i < 2100; i++) {
  int weeks = new DateTime().withYear(i).weekOfWeekyear().getMaximumValue();
  System.out.println(i + " years : " + weeks); 
}

Maven Dependency:

<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.10.5</version>
</dependency>
Kumaresan Perumal
  • 1,926
  • 2
  • 29
  • 35