Nate
Nate

Reputation: 99

How to determine the date passing the Week Number and Day Number (Week) in java

I need to get the date by passing these parameters

  1. year
  2. week number (in a month) i.e. 1,2,3,4,5
  3. day number (in a week) 0 (Sunday) to 6 (Saturday)
  4. Month

I looked for a constructor in Calendar class but does not contain these parameters.

Upvotes: 0

Views: 2818

Answers (3)

Anonymous
Anonymous

Reputation: 86276

In spite of your tags I agree with Joe C’s comment, you should prefer the modern Java date and time API (AKA known as java.time or JSR-310) over the long outdated Calendar and friends. The modern classes are more programmer friendly and nicer to work with, they come with fewer surprises, lead to clearer and more natural code (at least when you get used to the fluent style) and are easier to debug should you make a bug.

I don’t know how you count your weeks of the month. I have assumed that the first week starts on the first of the month and lasts 7 days (so the second week starts on the 8th and the 5th week on the 29th day of the month). If you count differently, please see Hugo’s answer.

I suggest this method:

public static LocalDate weekNoAndDayToDate(Year year, Month month, int weekOfMonth, int dayOfWeek) {
    // find day of week: convert from 0 (Sunday) to 6 (Saturday)
    // to DayOfWeek, from 1 (Monday) to 7 (Sunday)
    DayOfWeek dow;
    if (dayOfWeek == 0) { // Sunday
        dow = DayOfWeek.SUNDAY;
    } else {
        dow = DayOfWeek.of(dayOfWeek);
    }
    return year.atMonth(month)
            .atDay(1)
            .with(TemporalAdjusters.nextOrSame(dow))
            .with(ChronoField.ALIGNED_WEEK_OF_MONTH, weekOfMonth);
}

With this we may do for example

    LocalDate today = weekNoAndDayToDate(Year.of(2017), Month.JULY, 1, 1);

This yields

2017-07-03

If you need either a Date or a GregorianCalendar, for example for an old API that you cannot change, do one of the following:

    Date oldfashiondDateObject
            = Date.from(today.atStartOfDay(ZoneId.systemDefault()).toInstant());
    GregorianCalendar oldfashionedCalendarObject
            = GregorianCalendar.from(today.atStartOfDay(ZoneId.systemDefault()));

In both cases the result will be different in different time zones (one of the inherent troubles of the old classes). On my computer the first yields a Date of

Mon Jul 03 00:00:00 CEST 2017

The second yields a GregorianCalendar equal to the same point in time.

Upvotes: 2

user7605325
user7605325

Reputation:

As @Ole V.V. explained, you need to define in what day the week starts, and how many days it must have to be considered the first week.

For example, the ISO-8601 standard considers:

  • Monday to be the first day-of-week.
  • The minimal number of days in the first week: the standard counts the first week as needing at least 4 days

The month is divided into periods where each period starts on the defined first day-of-week. The earliest period in the same month is referred to as week 0 if it has less than the minimal number of days and week 1 if it has at least the minimal number of days.

Depending on how you define those, you can have different results. Consider the calendar for July 2017:

     July 2017
Su Mo Tu We Th Fr Sa
                   1
 2  3  4  5  6  7  8
 9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31

If we consider ISO's definition, we have:

  • week zero - 2017-07-01 to 2017-07-02
  • week 1: from 2017-07-03 to 2017-07-09
  • week 2: from 2017-07-10 to 2017-07-16
  • week 3: from 2017-07-17 to 2017-07-22
  • week 4: from 2017-07-23 to 2017-07-30
  • week 5: 2017-07-31

If we consider first day of week as Sunday and minimal number of days in the first week as 1, we have:

  • week 1: 2017-07-01
  • week 2: from 2017-07-02 to 2017-07-08
  • week 3: from 2017-07-09 to 2017-07-15
  • week 4: from 2017-07-16 to 2017-07-21
  • week 5: from 2017-07-22 to 2017-07-29
  • week 6: from 2017-07-30 to 2017-07-31

As a solution with Calendar was already posted, I'm gonna write one using the new API. If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.

If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).

The code below works for both. The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.

I'm using a DateTimeFormatter because it takes all the fields (month, year, day of week and week of month) and resolves the day accordingly, creating a LocalDate (which has the day/month/year fields). I'm also using the WeekFields class, which can be configured to use different week definitions (first day and minimal number of days in first week)

There's also a little adjustment to consider Sunday as zero:

public LocalDate getDate(int weekOfMonth, int dayOfWeek, int month, int year) {
    // you can customize your week definition (first day of week and mininum days in first week)
    WeekFields wf = WeekFields.of(DayOfWeek.SUNDAY, 2);
    DateTimeFormatter fmt = new DateTimeFormatterBuilder()
        // week of month, using week definition from WeekFields
        .appendValue(wf.weekOfMonth()).appendPattern(" ")
        // day of week
        .appendValue(ChronoField.DAY_OF_WEEK)
        // month and year
        .appendPattern(" M/yyyy")
        // create formatter
        .toFormatter();

    return LocalDate.parse(weekOfMonth + " " +
           // Sunday is 0, adjusting value
           (dayOfWeek == 0 ? 7 : dayOfWeek) + " " + month + "/" + year, fmt);
}

Using this code (week starts on Sunday, and 2 days are required to be considered the first week - otherwise week will be zero as in the first example above):

LocalDate d = getDate(1, 6, 7, 2017);

d will be 2017-07-08 (Saturday in the week 1 of July 2017).

If you want to use ISO 8601 definition, use the constant WeekFields.ISO instead of using WeekFields.of() method.


As @Ole V.V. suggested in the comments, it can also be done without creating a formatter: get the first dayOfWeek of the month and adjust it to the desired weekOfMonth:

public LocalDate getDate(int weekOfMonth, int dayOfWeek, int month, int year) {
    // you can customize your week definition (first day of week and mininum days in first week)
    WeekFields wf = WeekFields.of(DayOfWeek.SUNDAY, 2);

    // Sunday is 0, adjusting value
    DayOfWeek dow = DayOfWeek.of(dayOfWeek == 0 ? 7 : dayOfWeek);

    // get the first weekday of the month
    LocalDate first = LocalDate.of(year, month, 1).with(TemporalAdjusters.nextOrSame(dow));

    // check in which week this date is
    int week = first.get(wf.weekOfMonth());

    // adjust to weekOfMonth
    return first.plusWeeks(weekOfMonth - week);
}

This works exactly the same way, but without the need of a formatter - I've tested with dates from year 1600 to 2100 and it gets the same results.


PS: Calendar also has a similar configuration via the methods setFirstDayOfWeek() and setMinimalDaysInFirstWeek(). You can check the default configuration calling the respective get methods.

Upvotes: 1

Jay Smith
Jay Smith

Reputation: 2480

To create date from year, week number and week's day use java.util.Calendar instance:

Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, 2017);
        cal.set(Calendar.WEEK_OF_YEAR, 26);
        cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);

To convert from Calendar to java.util.Date :

Date date = cal.getTime();

To convert Date into java.time.LocalDateTime use :

LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());

Upvotes: 1

Related Questions