Abhinay Pandey
Abhinay Pandey

Reputation: 83

getting Monday date from given week and year using java.time package

I want to get Monday date from given week and year using Java 8 package java.time. But at some point I am facing issue as it's not returning proper date.

private LocalDate getDateFromWeekAndYear(final String week,final String year){
    LocalDate date = LocalDate.now();
    date = date.with(WeekFields.ISO.dayOfWeek(), 1);
    date = date.with(WeekFields.ISO.weekOfWeekBasedYear(), Long.parseLong(week));
    date = date.with(WeekFields.ISO.weekBasedYear(), Long.parseLong(year));

    return date;
}

For example:

Is there any logical mistake I am making or some other issue ?

Upvotes: 3

Views: 1840

Answers (4)

Angelina
Angelina

Reputation: 1

I wanted Monday of a week about 6 month ago - to be specific 26 weeks ago.. below code gave me the required date:

LocalDate.now().minusWeeks(26).with(WeekFields.ISO.dayOfWeek(), DayOfWeek.MONDAY.getValue())

Upvotes: 0

Basil Bourque
Basil Bourque

Reputation: 340098

tl;dr

YearWeek.of ( 2013 , 1 ).atDay ( DayOfWeek.MONDAY )

Incorrect expectations

Your expectations are not correct. The Monday of 2015-W53 is 2015-12-28, not 2014-12-28, year 2015 rather than 2014. There is no reason to expect 2014. Please edit your Question to explain your thoughts, if you need more explanation.

You may be confused about a calendar-year versus a week-based year. In the ISO 8601 definition of a week based year, week number one contains the first Thursday of the calendar-year. This means we have some overlap between the years. The last few days of a calendar-year may reside in the following week-based-year. And vice-versa, the first few days of a calendar-year may reside in the previous week-based-year.

As an example, you can see in the screenshot below, the last day of calendar 2012 (December 31) falls in week one of the following week-based-year of 2013 at week number 1. And in the other screen shot, we have the opposite, where the first three days of calendar 2016 (January 1, 2, & 3) land in the week-based-year of 2015 at week number 53.

  • The Monday of 2013-W01 is 2012-12-31.
  • The Monday of 2015-W53 is 2015-12-28.

2013-W01 calendar screen shot

2015-W53 calendar screen shot

YearWeek

I suggest adding the ThreeTen-Extra library to your project to make use of of the YearWeek class. Rather than pass around mere integer numbers for year and week, pass around objects of this class. Doing so makes your code more self-documenting, provides type-safety, and ensures valid values.

// Pass ( week-based-year-number, week-number ). *Not* calendar year! See the ISO 8601 standard.
YearWeek yw = YearWeek.of( 2013 , 1 ); 

You can pull any day from that week.

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

Let's try this kind of code.

YearWeek yw1 = YearWeek.of ( 2013 , 1 );
LocalDate ld1 = yw1.atDay ( DayOfWeek.MONDAY );

YearWeek yw2 = YearWeek.of ( 2015 , 53 );
LocalDate ld2 = yw2.atDay ( DayOfWeek.MONDAY );

System.out.println ( "yw1: " + yw1 + " Monday: " + ld1 );
System.out.println ( "yw2: " + yw2 + " Monday: " + ld2 );

yw1: 2013-W01 Monday: 2012-12-31

yw2: 2015-W53 Monday: 2015-12-28


Tip: To see those ISO 8601 standard week numbers on a Mac in Calendar.app, set System Preferences > Language & Region > Calendar > ISO 8601. Then in Calendar.app, set Preferences > Advanced > Show week numbers.

Upvotes: 2

Meno Hochschild
Meno Hochschild

Reputation: 44071

This is astonishingly more difficult than the partially invalid expectations of OP and most answers show.

First to say: It is very important to define the correct order of week-based-manipulations. The OP has first applied day-manipulation, then year-based manipulation. The correct approach is in reverse! I will show the right helper method implementation:

public static void main(String... args) {
    System.out.println(
      getDateFromWeekAndYear("53", "2015")); // 2015-12-28, NOT 2014-12-28
    System.out.println(
      getDateFromWeekAndYear("53", "2015").get(WeekFields.ISO.weekOfWeekBasedYear())); // 53
    System.out.println(
      getDateFromWeekAndYear("53", "2014")); // 2014-12-29
    System.out.println(
      getDateFromWeekAndYear("53", "2014").get(WeekFields.ISO.weekOfWeekBasedYear())); // 1
}

private static LocalDate getDateFromWeekAndYear(final String week,final String year) {
    int y = Integer.parseInt(year);
    LocalDate date = LocalDate.of(y, 7, 1); // safer than choosing current date
    // date = date.with(WeekFields.ISO.weekBasedYear(), y); // no longer necessary
    date = date.with(WeekFields.ISO.weekOfWeekBasedYear(), Long.parseLong(week));
    date = date.with(WeekFields.ISO.dayOfWeek(), 1);

    return date;
}

If you don't respect this specific order then you will indeed get sometimes a 2014-date for the input 2015-W53 (depending on the current date).

Second problem: I have also avoided to start with current date in order to be not near start or end of calendar year (calendar year != week-based-year) and instead chosen midth of year as starting point.

The third problem is lenient handling of week 53 in (week-based)-year 2014. It does not exist because 2014 had only 52 weeks!!! A strict algorithm should recognize and reject such an input. Therefore I advise against using YearWeek.of(2014, 53) (in the external library Threeten-Extra) resulting in the first week of 2015, see also its javadoc. Better than such lenient handling would have been

YearWeek yw = YearWeek.of(2014, 52);
if (yw.is53WeekYear()) {
  yw = YearWeek.of(2014, 53);
}

or using this code from my own time library Time4J (whose class CalendarWeek has extra i18n-features and extra week arithmetic in comparison with YearWeek):

CalendarWeek.of(2014, 53); // throws an exception
System.out.println(CalendarWeek.of(2014, 1).withLastWeekOfYear()); // 2014-W52

Only using java.time-package:

Using such external libraries would at least have helped to solve the first problem in a transparent way. If you are not willing to add an extra dependency then you can do this to handle week 53 if invalid:

If the expression WeekFields.ISO.weekOfWeekBasedYear() applied on the result of your helper method yields the value 1 then you know that week 53 was invalid. And then you can decide if you want to accept lenient handling or to throw an exception. But silent adjusting such an invalid input is IMHO bad design.

Upvotes: 5

LHA
LHA

Reputation: 9655

First, you need to calculate the first Monday of the first week of the year, then simply plus multi of 7 to the date.

public static LocalDate firstMonday(int week, int year) {

    LocalDate firstMonOfFirstWeek = LocalDate.now()
            .with(IsoFields.WEEK_BASED_YEAR, year) // year
            .with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 1) // First week of the year
            .with(ChronoField.DAY_OF_WEEK, 1); // Monday

    // Plus multi of 7
    return firstMonOfFirstWeek.plusDays( (week - 1) * 7);
}

public static void main(String[] args) {
    System.out.println(firstMonday(1, 2013)); // 2012-12-31
    System.out.println(firstMonday(53 ,2015 )); // 2015-12-28
}

Upvotes: 3

Related Questions