Jose
Jose

Reputation: 1829

Breakdown year in weeks - Java 8

I want to create a "select", that when selecting a year in another "select", I load all the weeks of that year, from Monday to Friday:

Example, if I select 2018 my "select" would be something like this.

1 - 01/01/18 - 07/01/18

2 - 01/08/18 - 01/14/18

3 - 15/01/18 - 01/21/10

......

52 - 12/24/18 - 12/30/18

The data was prepared in the back-end with java. To then send it as an array of string to the front-end

Upvotes: 0

Views: 87

Answers (2)

Basil Bourque
Basil Bourque

Reputation: 340108

Avoid legacy date-time classes

The modern solution uses java.time classes. These supplanted the terribly flawed legacy classes such as Date & Calendar.

Looping

Determine the first day of year. And the first day of the following year, as our limit.

Year year = Year.of( 2018 );
LocalDate firstOfYear = year.atDay( 1 );
LocalDate firstOfFollowingYear = year.plusYears( 1 ).atDay( 1 );

Use a TemporalAdjuster to move through time. The TemporalAdjusters class (note the plural) happens to offer an adjuster implementation for our needs, previousOrSame. Use that to move back in time to get a Monday, if the 1st of year is not already Monday.

TemporalAdjuster adjuster = TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY );
LocalDate firstMondayOfYear = firstOfYear.with( adjuster );

Define a list to hold our weekly report.

List < String > weeks = new ArrayList <>( 53 );

Define a formatter to generate the text for our weekly report. The DateTimeFormatter class can automatically localize, so we need not hard-code a specific format.

Locale locale = Locale.forLanguageTag( "es-ES" ); // Spain locale.
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate( FormatStyle.SHORT ).withLocale( locale );

Loop through the weeks of the year. We increment one week at a time, until hitting the following year.

int nthWeekOfYear = 0;
LocalDate localDate = firstMondayOfYear;
while ( localDate.isBefore( firstOfFollowingYear ) ) {
    nthWeekOfYear++;
    String output = nthWeekOfYear + " - " + localDate.format( formatter ) + " - " + localDate.plusDays( 6 ); // Using Fully-Closed weeks, where both beginning and end are inclusive. In contrast to Half-Open.
    weeks.add( output );
    // Set up next loop.
    localDate = localDate.plusWeeks( 1 );
}

Convert from a List to an array, per your directions in the Question.

String[] results = weeks.toArray( new String[ weeks.size() ] );

Dump to console.

System.out.println( "results = " + Arrays.toString( results ) );

Streams

We could rewrite this code by using streams and lambdas. This requires Java 9+.

The LocalDate#datesUntil method produces a Stream of LocalDate objects.

While I do not necessarily recommend this code, I found it interesting that we can write the entire routine as a one-liner.

int inputYear = 2018;
List < String > weeks =
        Year
                .of( inputYear )
                .atDay( 1 )
                .with( TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) )
                .datesUntil( Year.of( inputYear ).plusYears( 1 ).atDay( 1 ) , Period.ofWeeks( 1 ) )
                .map( localDate -> localDate.format( DateTimeFormatter.ofLocalizedDate( FormatStyle.SHORT ).withLocale( Locale.forLanguageTag( "es-ES" ) ) ) + " - " + localDate.plusDays( 6 ).format( DateTimeFormatter.ofLocalizedDate( FormatStyle.SHORT ).withLocale( Locale.forLanguageTag( "es-ES" ) ) ) )
                .toList();  // Before Java 16, change to .collect( Collectors.toList()); or .collect( Collectors.toUnmodifiableList());

When run.

weeks = [1/1/18 - 7/1/18, 8/1/18 - 14/1/18, 15/1/18 - 21/1/18, 22/1/18 - 28/1/18, 29/1/18 - 4/2/18, 5/2/18 - 11/2/18, 12/2/18 - 18/2/18, 19/2/18 - 25/2/18, 26/2/18 - 4/3/18, 5/3/18 - 11/3/18, 12/3/18 - 18/3/18, 19/3/18 - 25/3/18, 26/3/18 - 1/4/18, 2/4/18 - 8/4/18, 9/4/18 - 15/4/18, 16/4/18 - 22/4/18, 23/4/18 - 29/4/18, 30/4/18 - 6/5/18, 7/5/18 - 13/5/18, 14/5/18 - 20/5/18, 21/5/18 - 27/5/18, 28/5/18 - 3/6/18, 4/6/18 - 10/6/18, 11/6/18 - 17/6/18, 18/6/18 - 24/6/18, 25/6/18 - 1/7/18, 2/7/18 - 8/7/18, 9/7/18 - 15/7/18, 16/7/18 - 22/7/18, 23/7/18 - 29/7/18, 30/7/18 - 5/8/18, 6/8/18 - 12/8/18, 13/8/18 - 19/8/18, 20/8/18 - 26/8/18, 27/8/18 - 2/9/18, 3/9/18 - 9/9/18, 10/9/18 - 16/9/18, 17/9/18 - 23/9/18, 24/9/18 - 30/9/18, 1/10/18 - 7/10/18, 8/10/18 - 14/10/18, 15/10/18 - 21/10/18, 22/10/18 - 28/10/18, 29/10/18 - 4/11/18, 5/11/18 - 11/11/18, 12/11/18 - 18/11/18, 19/11/18 - 25/11/18, 26/11/18 - 2/12/18, 3/12/18 - 9/12/18, 10/12/18 - 16/12/18, 17/12/18 - 23/12/18, 24/12/18 - 30/12/18, 31/12/18 - 6/1/19]

A little more reasonable would be extracting the formatter and the Year.

Locale locale = Locale.forLanguageTag( "es-ES" ); // Spain locale.
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate( FormatStyle.SHORT ).withLocale( locale );

Year year = Year.of( 2018 );
List < String > weeks =
        year
                .atDay( 1 )
                .with( TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) )
                .datesUntil( year.plusYears( 1 ).atDay( 1 ) , Period.ofWeeks( 1 ) )
                .map( localDate -> localDate.format( formatter ) + " - " + localDate.plusDays( 6 ).format( formatter ) )
                .toList();  // Before Java 16, change to .collect( Collectors.toList()); or .collect( Collectors.toUnmodifiableList());

ISO 8601

If your definition of week complies with the ISO 8601 definition of week:

  • First day of week is Monday
  • Week # 1 of year contains the first Thursday of the calendar year

… then I suggest adding the ThreeTen-Extra library to your project to utilize the YearWeek class.

Upvotes: 1

Jose
Jose

Reputation: 1829

public class Weeks {

public static void main(String[] args) {
    int year = 2018;
    int weeks = getNumWeeksForYear(year);

    for (int i = 1; i < weeks; i++) {

        Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.set(Calendar.WEEK_OF_YEAR, i);
        calendar.set(Calendar.YEAR, year);

        Date monday = calendar.getTime();
        calendar.add(Calendar.DATE, 6);
        Date sunday = calendar.getTime();
        System.out.println("week" + i);
        System.out.println(monday);
        System.out.println(sunday);
    }

}

public static int getNumWeeksForYear(int year) {
    Calendar c = Calendar.getInstance();
    c.set(year, 0, 1);
    return c.getMaximum(Calendar.WEEK_OF_YEAR);
}

}

Upvotes: 0

Related Questions