1

I need help to return the first day (and last day) of a given week, passing only the week of the month and the corresponding month and year.

I've already looked for a way to return using LocalDate or Calendar and haven't found it.

My Locale is PT-BR - First day of week is Sunday

Thanks in advance.

Ex:

Year 2021 - Month 7 (July) - Week 1 - First day: 1 - Last Day: 3
Year 2021 - Month 7 (July) - Week 2 - First day: 4 - Last Day: 10
Year 2021 - Month 7 (July) - Week 3 - First day: 11 - Last Day: 17
Year 2021 - Month 7 (July) - Week 4 - First day: 18 - Last Day: 24
Year 2021 - Month 7 (July) - Week 5 - First day: 25 - Last Day: 31
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
sammubr
  • 535
  • 1
  • 4
  • 23

4 Answers4

3

java.time

The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.

Solution using java.time, the modern Date-Time API:

  1. Use WeekFields.of(locale).getFirstDayOfWeek() to find the first day of the week.
  2. Use TemporalAdjusters.nextOrSame(firstDayOfWeek) to find the last day of the week.
  3. Use TemporalAdjusters.lastDayOfMonth() to find the last date of the month.

Demo:

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.Month;
import java.time.format.TextStyle;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        // Test
        System.out.println(getWeeks(2021, 7, new Locale("pt", "BR")));

        System.out.println("---------------------------------------------------------");

        System.out.println(getWeeks(2021, 2, new Locale("pt", "BR")));

        System.out.println("---------------------------------------------------------");

        System.out.println(getWeeks(2021, 7, new Locale("en", "GB")));

        System.out.println("---------------------------------------------------------");

        System.out.println(getWeeks(2021, 2, new Locale("en", "GB")));
    }

    static String getWeeks(int year, int month, Locale locale) {
        DayOfWeek firstDayOfWeek = WeekFields.of(locale).getFirstDayOfWeek();
        LocalDate firstDateOfMonth = LocalDate.of(year, month, 1);
        String monthName = Month.of(month).getDisplayName(TextStyle.FULL, Locale.ENGLISH);
        int lastDayOfMonth = firstDateOfMonth.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth();

        int firstDay = 1;
        int lastDayOfFirstWeek = LocalDate.of(year, month, 1).with(TemporalAdjusters.nextOrSame(firstDayOfWeek))
                .getDayOfMonth();
        int lastDay = lastDayOfFirstWeek == 1 ? 7 : lastDayOfFirstWeek - 1;

        int i;
        StringBuilder sb = new StringBuilder();

        for (i = 1; i <= lastDayOfMonth / 7; i++) {
            sb.append(String.format("Year %d - Month %d (%s) - Week %d - First day: %d - Last Day: %d%n", year, month,
                    monthName, i, firstDay, lastDay));
            firstDay = lastDay + 1;
            lastDay += 7;
        }

        if (lastDayOfFirstWeek != 1 && lastDayOfMonth >= 28)
            sb.append(String.format("Year %d - Month %d (%s) - Week %d - First day: %d - Last Day: %d%n", year, month,
                    monthName, i, firstDay, lastDayOfMonth));

        return sb.toString();
    }
}

Output:

Year 2021 - Month 7 (July) - Week 1 - First day: 1 - Last Day: 3
Year 2021 - Month 7 (July) - Week 2 - First day: 4 - Last Day: 10
Year 2021 - Month 7 (July) - Week 3 - First day: 11 - Last Day: 17
Year 2021 - Month 7 (July) - Week 4 - First day: 18 - Last Day: 24
Year 2021 - Month 7 (July) - Week 5 - First day: 25 - Last Day: 31

---------------------------------------------------------
Year 2021 - Month 2 (February) - Week 1 - First day: 1 - Last Day: 6
Year 2021 - Month 2 (February) - Week 2 - First day: 7 - Last Day: 13
Year 2021 - Month 2 (February) - Week 3 - First day: 14 - Last Day: 20
Year 2021 - Month 2 (February) - Week 4 - First day: 21 - Last Day: 27
Year 2021 - Month 2 (February) - Week 5 - First day: 28 - Last Day: 28

---------------------------------------------------------
Year 2021 - Month 7 (July) - Week 1 - First day: 1 - Last Day: 4
Year 2021 - Month 7 (July) - Week 2 - First day: 5 - Last Day: 11
Year 2021 - Month 7 (July) - Week 3 - First day: 12 - Last Day: 18
Year 2021 - Month 7 (July) - Week 4 - First day: 19 - Last Day: 25
Year 2021 - Month 7 (July) - Week 5 - First day: 26 - Last Day: 31

---------------------------------------------------------
Year 2021 - Month 2 (February) - Week 1 - First day: 1 - Last Day: 7
Year 2021 - Month 2 (February) - Week 2 - First day: 8 - Last Day: 14
Year 2021 - Month 2 (February) - Week 3 - First day: 15 - Last Day: 21
Year 2021 - Month 2 (February) - Week 4 - First day: 22 - Last Day: 28

ONLINE DEMO

Learn more about the modern Date-Time API* from Trail: Date Time.


* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
2

I found your week calendar approach quite amusing to play with, came up with a method that receives two parameters (month and year) and builds up the week calendar of that month:

private static Map<Integer, Integer[]> getWeekCalendar(int month, int year) {
    Map<Integer, Integer[]> weekCalendar = new HashMap<>();
    LocalDate date = LocalDate.of(year,month,1);
    int week = 1;
    LocalDate firstOfWeek = LocalDate.from(date);
    while (date.getMonth().equals(Month.of(month))) {
        //if it is a saturday, advance to the next week and register current week bounds
        if (date.getDayOfWeek().equals(DayOfWeek.SATURDAY)) {
            weekCalendar.put(week++, new Integer[]{firstOfWeek.getDayOfMonth(), date.getDayOfMonth()});
            firstOfWeek = LocalDate.from(date.plusDays(1L));
        }
        date = date.plusDays(1L);
    }
    //this one is necessary because the month may end on a Saturday
    if (firstOfWeek.getMonth().equals(Month.of(month))) {
        weekCalendar.put(week, new Integer[]{firstOfWeek.getDayOfMonth(), date.minusDays(1L).getDayOfMonth()});
    }
    return weekCalendar;
}

And a main class for testing purposes:

public static void main(String[] args) {
    int wantedMonth = 7;
    int wantedYear = 2021;
    Map<Integer, Integer[]> weekCalendar = getWeekCalendar(wantedMonth, wantedYear);
    weekCalendar.entrySet().stream()
            .map(e -> String.format("Year %d - Month %d (%s) - Week %d - First day: %d - Last day: %d", wantedYear, wantedMonth, Month.of(wantedMonth).name(), e.getKey(), e.getValue()[0], e.getValue()[1]))
            .forEach(System.out::println);
}

output:

Year 2021 - Month 7 (JULY) - Week 1 - First day: 1 - Last day: 3
Year 2021 - Month 7 (JULY) - Week 2 - First day: 4 - Last day: 10
Year 2021 - Month 7 (JULY) - Week 3 - First day: 11 - Last day: 17
Year 2021 - Month 7 (JULY) - Week 4 - First day: 18 - Last day: 24
Year 2021 - Month 7 (JULY) - Week 5 - First day: 25 - Last day: 31
Luiz Damy
  • 216
  • 2
  • 9
1

Annual calendar of weeks

To expand on the correct Answer by Arvind Kumar Avinash, let’s get the weekly calendar for a year. And for our week, let’s use a third-party class designed to represent a span-of-time of days. We collect a map of month as key and list of weeks as value. From that map we can retrieve any particular week.

ThreeTen-Extra library, LocalDateRange class

Add the ThreeTen-Extra library to your project. This gives us the LocalDateRange class to represent a span of time as a pair of LocalDate objects. This class provides many convenient methods such as abuts, contains, and so on, likely to be helpful when you are working with weeks.

Specify a Year and a Locale. From the locale we determine the first day-of-week as shown in Answer by Avinash.

Loop each month, then each week

We loop every month of the year, using an array of Month objects returned by the Month.values method on this enum. We combine each month with the year to get a YearMonth object.

For each YearMonth, we get the first day of the month. Then we move back in time to get the first day-of-week (DayOfWeek), or use the same date if it is indeed our desired day-of-week. A TemporalAdjuster moves us along the timeline. You can write your own TemporalAdjuster implementation, but we can find one already written for our needs here: TemporalAdjusters.previousOrSame( DayOfWeek ).

Then we loop to calculate each week of the month, making a LocalDateRange from the pair of LocalDate objects for start and end of each week. We collect those LocalDateRange objects into a NavigableSet, a TreeSet.

We report our results by adding each YearMonth as the key to a NavigableMap (TreeMap), with the value being our NavigableSet of LocalDateRange.

Example code

package work.basil.datetime;

import org.threeten.extra.LocalDateRange;

import java.time.*;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
import java.util.*;

public final class WeeklyCalendar {
    public static final NavigableMap < YearMonth, List < LocalDateRange > > calculate ( final Year year , final Locale locale ) {
        Objects.requireNonNull( year );
        DayOfWeek startOfWeek = WeekFields.of( Objects.requireNonNull( locale ) ).getFirstDayOfWeek();

        NavigableMap < YearMonth, List < LocalDateRange > > navMap = new TreeMap <>();

        TemporalAdjuster adjuster = TemporalAdjusters.previousOrSame( startOfWeek );
        for ( Month month : Month.values() ) {
            List < LocalDateRange > weeks = new ArrayList <>();
            YearMonth yearMonth = year.atMonth( month );
            LocalDate start = yearMonth.atDay( 1 ).with( adjuster );
            LocalDate end = start.plusWeeks( 1 );
            do {
                LocalDateRange week = LocalDateRange.of( start , end );
                weeks.add( week );
                // Set up next loop.
                start = start.plusWeeks( 1 );
                end = start.plusWeeks( 1 );
            } while ( ! YearMonth.from( end ).isAfter( yearMonth ) );
            navMap.put( yearMonth , weeks );
        }

        return Collections.unmodifiableNavigableMap( navMap );  // Generally best to return immutable values.
    }
}

Usage:

Year year = Year.of( 2021 );
Locale locale = new Locale( "pt" , "BR" );
NavigableMap < YearMonth, List < LocalDateRange > > map = WeeklyCalendar.calculate( year , locale );

LocalDateRange weekOneOfMarch2021 = map.get( YearMonth.of( 2021 , Month.MARCH ) ).get( 0 );  // Lists use annoying zero-based counting, index number. 

When run.

map = {2021-01=[2020-12-27/2021-01-03, 2021-01-03/2021-01-10, 2021-01-10/2021-01-17, 2021-01-17/2021-01-24, 2021-01-24/2021-01-31], 2021-02=[2021-01-31/2021-02-07, 2021-02-07/2021-02-14, 2021-02-14/2021-02-21, 2021-02-21/2021-02-28], 2021-03=[2021-02-28/2021-03-07, 2021-03-07/2021-03-14, 2021-03-14/2021-03-21, 2021-03-21/2021-03-28], 2021-04=[2021-03-28/2021-04-04, 2021-04-04/2021-04-11, 2021-04-11/2021-04-18, 2021-04-18/2021-04-25], 2021-05=[2021-04-25/2021-05-02, 2021-05-02/2021-05-09, 2021-05-09/2021-05-16, 2021-05-16/2021-05-23, 2021-05-23/2021-05-30], 2021-06=[2021-05-30/2021-06-06, 2021-06-06/2021-06-13, 2021-06-13/2021-06-20, 2021-06-20/2021-06-27], 2021-07=[2021-06-27/2021-07-04, 2021-07-04/2021-07-11, 2021-07-11/2021-07-18, 2021-07-18/2021-07-25], 2021-08=[2021-08-01/2021-08-08, 2021-08-08/2021-08-15, 2021-08-15/2021-08-22, 2021-08-22/2021-08-29], 2021-09=[2021-08-29/2021-09-05, 2021-09-05/2021-09-12, 2021-09-12/2021-09-19, 2021-09-19/2021-09-26], 2021-10=[2021-09-26/2021-10-03, 2021-10-03/2021-10-10, 2021-10-10/2021-10-17, 2021-10-17/2021-10-24, 2021-10-24/2021-10-31], 2021-11=[2021-10-31/2021-11-07, 2021-11-07/2021-11-14, 2021-11-14/2021-11-21, 2021-11-21/2021-11-28], 2021-12=[2021-11-28/2021-12-05, 2021-12-05/2021-12-12, 2021-12-12/2021-12-19, 2021-12-19/2021-12-26]}

weekOneOfMarch2021 = 2021-02-28/2021-03-07

Notice how LocalDateRange#toString uses the standard format of start date, then SOLIDUS /, then end date.

Annual overlap

Notice that we start with days in the previous year. And we do not include some of the last days of the calendar-year.

Half-Open

Notice that the weeks here are defined using Half-Open approach where the beginning is inclusive while the ending is exclusive. This is usually the best approach in defining a span-of-time. I recommend using this approach consistently throughout your codebase to keep the logic consistent and thereby avoid errors and confusion.

Fully-Closed

However, if you insist on fully-closed approach, the LocalDateRange class can support that. See the LocalDateRange.ofClosed method.

Use at your own risk

I quickly whipped up this code just now. So no guarantees. Verify the logic, and perform your own tests.

ISO 8601 weeks

Be aware that there are several ways to define a week. Some industries commonly use a particular definition.

There is an international standard that is growing in popularity: ISO 8601. That standard defines weeks as Monday being the first day of the week, and week # 1 containing the first Thursday of the calendar year. Therefore a year is made of either 52 or 53 complete weeks of 7 days each.

The ThreeTen-Extra library offers a class to represent ISO 8601 weeks: YearWeek.

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

There is no single function to get the week of the month.

I put together a method to calculate the week of the month. Here are the test results from one of my many test runs.

Week 1 of July 2021 is 1 - 3
Week 2 of July 2021 is 4 - 10
Week 3 of July 2021 is 11 - 17
Week 4 of July 2021 is 18 - 24
Week 5 of July 2021 is 25 - 31
Week 6 of July 2021 is 0 - 0
Week 1 of August 2021 is 1 - 7
Week 2 of August 2021 is 8 - 14

I created a LocalDate based on the year and month. I did some date arithmetic to get the Sunday day of the month and Saturday day of the month for a week of the month.

Here's the code. I tested it for July and August 2021. It may work for other months.

import java.time.LocalDate;

public class DatesFinder {

    public static void main(String[] args) {
        DatesFinder df = new DatesFinder();
        int[] days = df.findDays(2021, 7, 1);
        System.out.println("Week 1 of July 2021 is " + days[0] + " - " + days[1]);
        days = df.findDays(2021, 7, 2);
        System.out.println("Week 2 of July 2021 is " + days[0] + " - " + days[1]);
        days = df.findDays(2021, 7, 3);
        System.out.println("Week 3 of July 2021 is " + days[0] + " - " + days[1]);
        days = df.findDays(2021, 7, 4);
        System.out.println("Week 4 of July 2021 is " + days[0] + " - " + days[1]);
        days = df.findDays(2021, 7, 5);
        System.out.println("Week 5 of July 2021 is " + days[0] + " - " + days[1]);
        days = df.findDays(2021, 7, 6);
        System.out.println("Week 6 of July 2021 is " + days[0] + " - " + days[1]);
        days = df.findDays(2021, 8, 1);
        System.out.println("Week 1 of August 2021 is " + days[0] + " - " + days[1]);
        days = df.findDays(2021, 8, 2);
        System.out.println("Week 2 of August 2021 is " + days[0] + " - " + days[1]);
    }
    
    public int[] findDays(int year, int month, int weekNumber) {
        LocalDate date =  LocalDate.of(year, month, 1);
        int weekday = date.getDayOfWeek().getValue();
        // weekday is 1 - Monday to 7 - Sunday.
        // convert to 0 - Sunday.
        weekday = weekday % 7;
        
        LocalDate sundayDate = date.minusDays(weekday);
        int dayIncrement = (weekNumber - 1) * 7;
        if (dayIncrement > 0) {
            sundayDate = sundayDate.plusDays(dayIncrement);
        }
        
        int lowDay = sundayDate.getDayOfMonth();
        LocalDate saturdayDate = sundayDate.plusDays(6L);
        int highDay = saturdayDate.getDayOfMonth();
//      System.out.println(date + " " + sundayDate + " " + lowDay + " " + highDay);
        
        // Test for beginning of month
        if (sundayDate.getMonth().compareTo(date.getMonth()) < 0) {
            lowDay = 1;
        }
        
        // Test for end of month
        if (saturdayDate.getMonth().compareTo(date.getMonth()) > 0) {
            LocalDate nextMonth = date.plusMonths(1L);
            nextMonth = nextMonth.minusDays(1L);
            highDay = nextMonth.getDayOfMonth();
        }
        
        // Test for outside of month (weekNumber too high)
        if (sundayDate.getMonth().compareTo(date.getMonth()) > 0) {
            lowDay = 0;
            highDay = 0;
        }

        int[] output = new int[2];
        output[0] = lowDay;
        output[1] = highDay;
        
        return output;
    }

}
Gilbert Le Blanc
  • 50,182
  • 6
  • 67
  • 111