Monirul Islam
Monirul Islam

Reputation: 13

How can i fix the problem printing all the fridays and saturdays of any given year?

i'm trying to print all the saturdays and sundays of any given year. but for some reason the program won't print only saturdays but not fridays for the year 2022. i've tried different values for year but still didn't find any, other than 2022. what did i do wrong here??? bellow is the code.

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Scanner;

public class JavaDayFinder extends Thread {

    int day;
    int year;

    JavaDayFinder(int day, int year) {
        this.day = day;
        this.year = year;
    }

    @Override
    public void run() {
        Calendar calendar = new GregorianCalendar();
        calendar.set(year, Calendar.JANUARY, 1);
        calendar.getTime();
        calendar.set(Calendar.DAY_OF_WEEK, day);
        calendar.getTime();

        while (calendar.get(Calendar.YEAR) == year) {
            System.out.println(calendar.getTime());
            calendar.add(Calendar.DAY_OF_MONTH, 7);
            try {
                Thread.sleep(250);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) {
        System.out.println("This program prints all the common holidays (Fridays & Saturdays) of any given year.");
        System.out.print("Please input the year of which you want to know the holidays: ");
        Scanner scan = new Scanner(System.in);
        int year = scan.nextInt();
        System.out.println("Given year : " + year);
        System.out.println();

        JavaDayFinder friday = new JavaDayFinder(6, year);
        JavaDayFinder saturday = new JavaDayFinder(7, year);
        friday.start();
        saturday.start();
    }
}

bellow is the output

This program prints all the common holidays (Fridays & Saturdays) of any given year.
Please input the year of which you want to know the holidays: 2022
Given year : 2022

Sat Jan 01 17:05:56 BDT 2022
Sat Jan 08 17:05:56 BDT 2022
Sat Jan 15 17:05:56 BDT 2022
Sat Jan 22 17:05:56 BDT 2022
Sat Jan 29 17:05:56 BDT 2022
Sat Feb 05 17:05:56 BDT 2022
Sat Feb 12 17:05:56 BDT 2022
Sat Feb 19 17:05:56 BDT 2022
Sat Feb 26 17:05:56 BDT 2022
Sat Mar 05 17:05:56 BDT 2022
Sat Mar 12 17:05:56 BDT 2022
Sat Mar 19 17:05:56 BDT 2022
Sat Mar 26 17:05:56 BDT 2022
Sat Apr 02 17:05:56 BDT 2022
Sat Apr 09 17:05:56 BDT 2022
Sat Apr 16 17:05:56 BDT 2022
Sat Apr 23 17:05:56 BDT 2022
Sat Apr 30 17:05:56 BDT 2022
Sat May 07 17:05:56 BDT 2022
Sat May 14 17:05:56 BDT 2022
Sat May 21 17:05:56 BDT 2022
Sat May 28 17:05:56 BDT 2022
Sat Jun 04 17:05:56 BDT 2022
Sat Jun 11 17:05:56 BDT 2022
Sat Jun 18 17:05:56 BDT 2022
Sat Jun 25 17:05:56 BDT 2022
Sat Jul 02 17:05:56 BDT 2022
Sat Jul 09 17:05:56 BDT 2022
Sat Jul 16 17:05:56 BDT 2022
Sat Jul 23 17:05:56 BDT 2022
Sat Jul 30 17:05:56 BDT 2022
Sat Aug 06 17:05:56 BDT 2022
Sat Aug 13 17:05:56 BDT 2022
Sat Aug 20 17:05:56 BDT 2022
Sat Aug 27 17:05:56 BDT 2022
Sat Sep 03 17:05:56 BDT 2022
Sat Sep 10 17:05:56 BDT 2022
Sat Sep 17 17:05:56 BDT 2022
Sat Sep 24 17:05:56 BDT 2022
Sat Oct 01 17:05:56 BDT 2022
Sat Oct 08 17:05:56 BDT 2022
Sat Oct 15 17:05:56 BDT 2022
Sat Oct 22 17:05:56 BDT 2022
Sat Oct 29 17:05:56 BDT 2022
Sat Nov 05 17:05:56 BDT 2022
Sat Nov 12 17:05:56 BDT 2022
Sat Nov 19 17:05:56 BDT 2022
Sat Nov 26 17:05:56 BDT 2022
Sat Dec 03 17:05:56 BDT 2022
Sat Dec 10 17:05:56 BDT 2022
Sat Dec 17 17:05:56 BDT 2022
Sat Dec 24 17:05:56 BDT 2022
Sat Dec 31 17:05:56 BDT 2022

Process finished with exit code 0

Upvotes: 0

Views: 143

Answers (3)

MC Emperor
MC Emperor

Reputation: 23017

Well, that is because for Friday, the code line calendar.set(Calendar.DAY_OF_WEEK, day) causes the year to be set to 2021, so the line while (calendar.get(Calendar.YEAR) == year) is immediately skipped because of the condition being false.

As Ole V.V. already said in the comments, you should not use Calendar anymore. Use LocalDate and DayOfWeek. Here is the modified code:

@Override
public void run() {
    var dayOfWeek = DayOfWeek.of(day);
    LocalDate currDate = LocalDate.of(year, 1, 1)
        .with(TemporalAdjusters.nextOrSame(dayOfWeek));

    while (currDate.getYear() == year) {
        System.out.println(currDate);
        currDate = currDate.plusWeeks(1);
        try {
            Thread.sleep(250);
        }
        catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

Here's what happens:

  • LocalDate.of(year, 1, 1) yields January 1st of the given year.
  • TemporalAdjusters.nextOrSame(DayOfWeek) takes a given date and moves to the next specified day-of-week, or stays on January 1st if it is the specified day-of-week. In the example of the year 2022, 1st of January is a Saturday, so the yielded date will be January 7th, 2022, because that's the next Friday.
  • Within the loop, currDate.plusWeeks(1) obviously adds a week to the current date.(1)

Note: DayOfWeek.MONDAY has value 1, DayOfWeek.SUNDAY has value 7.(2) But I agree with Ole V.V. here: you are better off using DayOfWeek constants instead of integers, as this is better readable.


(1) LocalDate itself is immutable, so nothing is actually added. Instead, a new instance is returned with one week added.

(2) Well, at least DayOfWeek.of(int dayOfWeek) maps those values to the respective days-of-week. The enum itself does not hold a particular value, other than the enum's ordinal value.

Upvotes: 4

Basil Bourque
Basil Bourque

Reputation: 339332

tl;dr

Excerpt of code using java.time classes, streams, lambdas, and predicate.

this.datesOfYearforDaysOfWeek(
        Year.of( 2023 ) ,
        Set.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY )
)
year.atDay( 1 )
        .datesUntil(
                year
                        .plusYears( 1 )
                        .atDay( 1 )
        )
        .filter(
                date -> dows.contains( date.getDayOfWeek() )
        )
        .toList()

Details

The Answer by MC Emperor is correct and wise. For fun, I will show a different approach. May not be as efficient, though I have not tested performance. Here we use streams and lambdas.

We use LocalDate#datesUntil to get a stream of LocalDate objects. Notice that the datesUntil method uses Half-Open logic, where the beginning is inclusive while the ending is exclusive. So a year starts on the first and runs up to, but does not include, the first of the following year.

The Year class represents a year in way that makes the code more clear and self-documenting.

Main logic:

    LocalDate firstOfYear = year.atDay( 1 );
    List < LocalDate > dates =
            firstOfYear
                    .datesUntil( firstOfYear.plusYears( 1 ) )
                    .filter(
                            date -> dows.contains( date.getDayOfWeek() )
                    )
                    .toList();

Complete method.

List < LocalDate > datesOfYearforDaysOfWeek ( final Year year , final Set < DayOfWeek > dows )
{
    // Validate inputs.
    Objects.requireNonNull( year );
    Objects.requireNonNull( dows );
    if ( dows.isEmpty() ) { throw new IllegalArgumentException( "Days of week is empty." ); }

    // Logic
    LocalDate firstOfYear = year.atDay( 1 );
    List < LocalDate > dates =
            firstOfYear
                    .datesUntil( firstOfYear.plusYears( 1 ) )
                    .filter(
                            date -> dows.contains( date.getDayOfWeek() )
                    )
                    .toList();

    // Result.
    return List.copyOf( dates );  // Effectively a no-op if already an immutable list.
}

Usage.

List < LocalDate > dates =
        this.datesOfYearforDaysOfWeek(
                Year.of( 2023 ) ,
                Set.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY )
        );

When run.

dates.toString() = [2023-01-01, 2023-01-07, 2023-01-08, 2023-01-14, 2023-01-15, 2023-01-21, 2023-01-22, 2023-01-28, 2023-01-29, 2023-02-04, 2023-02-05, 2023-02-11, 2023-02-12, 2023-02-18, 2023-02-19, 2023-02-25, 2023-02-26, 2023-03-04, 2023-03-05, 2023-03-11, 2023-03-12, 2023-03-18, 2023-03-19, 2023-03-25, 2023-03-26, 2023-04-01, 2023-04-02, 2023-04-08, 2023-04-09, 2023-04-15, 2023-04-16, 2023-04-22, 2023-04-23, 2023-04-29, 2023-04-30, 2023-05-06, 2023-05-07, 2023-05-13, 2023-05-14, 2023-05-20, 2023-05-21, 2023-05-27, 2023-05-28, 2023-06-03, 2023-06-04, 2023-06-10, 2023-06-11, 2023-06-17, 2023-06-18, 2023-06-24, 2023-06-25, 2023-07-01, 2023-07-02, 2023-07-08, 2023-07-09, 2023-07-15, 2023-07-16, 2023-07-22, 2023-07-23, 2023-07-29, 2023-07-30, 2023-08-05, 2023-08-06, 2023-08-12, 2023-08-13, 2023-08-19, 2023-08-20, 2023-08-26, 2023-08-27, 2023-09-02, 2023-09-03, 2023-09-09, 2023-09-10, 2023-09-16, 2023-09-17, 2023-09-23, 2023-09-24, 2023-09-30, 2023-10-01, 2023-10-07, 2023-10-08, 2023-10-14, 2023-10-15, 2023-10-21, 2023-10-22, 2023-10-28, 2023-10-29, 2023-11-04, 2023-11-05, 2023-11-11, 2023-11-12, 2023-11-18, 2023-11-19, 2023-11-25, 2023-11-26, 2023-12-02, 2023-12-03, 2023-12-09, 2023-12-10, 2023-12-16, 2023-12-17, 2023-12-23, 2023-12-24, 2023-12-30, 2023-12-31]

Made more compact, though not necessarily better. Same results.

    // Logic
    List < LocalDate > dates =
            year.atDay( 1 )
                    .datesUntil(
                            year
                                    .plusYears( 1 )
                                    .atDay( 1 )
                    )
                    .filter(
                            date -> dows.contains( date.getDayOfWeek() )
                    )
                    .toList();

Executor Service

You may want to use an executor service rather than manage threads yourself.

Define your task as a Callable that returns your desired list of dates. Construct the object with the desired year and day-of-week.

class DatesForDayOfWeekTask implements Callable < List < LocalDate > >
{
    Year year;
    DayOfWeek dow;

    public DatesForDayOfWeekTask ( final Year year , final DayOfWeek dow )
    {
        this.year = year;
        this.dow = dow;
    }

    @Override
    public List < LocalDate > call ( ) throws Exception
    {
        return datesOfYearforDaysOfWeek( this.year , Set.of( this.dow ) );
    }
}

Example usage.

System.out.println( "INFO - Demo started. " + Instant.now() );

ExecutorService executorService = Executors.newCachedThreadPool();

DatesForDayOfWeekTask saturdayTask = new DatesForDayOfWeekTask( Year.of( 2023 ) , DayOfWeek.SATURDAY );
DatesForDayOfWeekTask sundayTask = new DatesForDayOfWeekTask( Year.of( 2023 ) , DayOfWeek.SUNDAY );

Future < List < LocalDate > > saturdayFuture = executorService.submit( saturdayTask );
Future < List < LocalDate > > sundayFuture = executorService.submit( sundayTask );

System.out.println( "INFO Tasks submitted. Please wait. " + Instant.now() );
try { Thread.sleep( Duration.ofSeconds( 10 ).toMillis() ); } catch ( InterruptedException e ) { throw new RuntimeException( e ); }
this.shutdownAndAwaitTermination( executorService );

try
{
    System.out.println( "saturdayFuture.get() = " + saturdayFuture.get() );
    System.out.println( "sundayFuture.get() = " + sundayFuture.get() );
}
catch ( InterruptedException e ) { throw new RuntimeException( e ); }
catch ( ExecutionException e ) { throw new RuntimeException( e ); }
System.out.println( "INFO - Demo ended. " + Instant.now() );

When run.

INFO - Demo started. 2022-09-28T00:38:58.460930Z
INFO Tasks submitted. Please wait. 2022-09-28T00:38:58.469034Z
saturdayFuture.get() = [2023-01-07, 2023-01-14, 2023-01-21, 2023-01-28, 2023-02-04, 2023-02-11, 2023-02-18, 2023-02-25, 2023-03-04, 2023-03-11, 2023-03-18, 2023-03-25, 2023-04-01, 2023-04-08, 2023-04-15, 2023-04-22, 2023-04-29, 2023-05-06, 2023-05-13, 2023-05-20, 2023-05-27, 2023-06-03, 2023-06-10, 2023-06-17, 2023-06-24, 2023-07-01, 2023-07-08, 2023-07-15, 2023-07-22, 2023-07-29, 2023-08-05, 2023-08-12, 2023-08-19, 2023-08-26, 2023-09-02, 2023-09-09, 2023-09-16, 2023-09-23, 2023-09-30, 2023-10-07, 2023-10-14, 2023-10-21, 2023-10-28, 2023-11-04, 2023-11-11, 2023-11-18, 2023-11-25, 2023-12-02, 2023-12-09, 2023-12-16, 2023-12-23, 2023-12-30]
sundayFuture.get() = [2023-01-01, 2023-01-08, 2023-01-15, 2023-01-22, 2023-01-29, 2023-02-05, 2023-02-12, 2023-02-19, 2023-02-26, 2023-03-05, 2023-03-12, 2023-03-19, 2023-03-26, 2023-04-02, 2023-04-09, 2023-04-16, 2023-04-23, 2023-04-30, 2023-05-07, 2023-05-14, 2023-05-21, 2023-05-28, 2023-06-04, 2023-06-11, 2023-06-18, 2023-06-25, 2023-07-02, 2023-07-09, 2023-07-16, 2023-07-23, 2023-07-30, 2023-08-06, 2023-08-13, 2023-08-20, 2023-08-27, 2023-09-03, 2023-09-10, 2023-09-17, 2023-09-24, 2023-10-01, 2023-10-08, 2023-10-15, 2023-10-22, 2023-10-29, 2023-11-05, 2023-11-12, 2023-11-19, 2023-11-26, 2023-12-03, 2023-12-10, 2023-12-17, 2023-12-24, 2023-12-31]
INFO - Demo ended. 2022-09-28T00:39:08.480522Z

Full example code.

package work.basil.example.days;

import java.time.*;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.*;

public class App
{
    public static void main ( String[] args )
    {
        App app = new App();
//        app.demo();
        app.demoThreaded();
    }

    private void demo ( )
    {
        List < LocalDate > dates =
                this.datesOfYearforDaysOfWeek(
                        Year.of( 2023 ) ,
                        Set.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY )
                );
        System.out.println( "dates.toString() = " + dates );
    }

    List < LocalDate > datesOfYearforDaysOfWeek ( final Year year , final Set < DayOfWeek > dows )
    {
        // Validate inputs.
        Objects.requireNonNull( year );
        Objects.requireNonNull( dows );
        if ( dows.isEmpty() ) { throw new IllegalArgumentException( "Days of week is empty." ); }

        // Logic
        List < LocalDate > dates =
                year.atDay( 1 )
                        .datesUntil(
                                year
                                        .plusYears( 1 )
                                        .atDay( 1 )
                        )
                        .filter(
                                date -> dows.contains( date.getDayOfWeek() )
                        )
                        .toList();

        // Result.
        return List.copyOf( dates );  // Effectively a no-op if already an immutable list.
    }

    private void demoThreaded ( )
    {
        System.out.println( "INFO - Demo started. " + Instant.now() );

        ExecutorService executorService = Executors.newCachedThreadPool();

        DatesForDayOfWeekTask saturdayTask = new DatesForDayOfWeekTask( Year.of( 2023 ) , DayOfWeek.SATURDAY );
        DatesForDayOfWeekTask sundayTask = new DatesForDayOfWeekTask( Year.of( 2023 ) , DayOfWeek.SUNDAY );

        Future < List < LocalDate > > saturdayFuture = executorService.submit( saturdayTask );
        Future < List < LocalDate > > sundayFuture = executorService.submit( sundayTask );

        System.out.println( "INFO Tasks submitted. Please wait. " + Instant.now() );
        try { Thread.sleep( Duration.ofSeconds( 10 ).toMillis() ); } catch ( InterruptedException e ) { throw new RuntimeException( e ); }
        this.shutdownAndAwaitTermination( executorService );

        try
        {
            System.out.println( "saturdayFuture.get() = " + saturdayFuture.get() );
            System.out.println( "sundayFuture.get() = " + sundayFuture.get() );
        }
        catch ( InterruptedException e ) { throw new RuntimeException( e ); }
        catch ( ExecutionException e ) { throw new RuntimeException( e ); }
        System.out.println( "INFO - Demo ended. " + Instant.now() );
    }


    class DatesForDayOfWeekTask implements Callable < List < LocalDate > >
    {
        Year year;
        DayOfWeek dow;

        public DatesForDayOfWeekTask ( final Year year , final DayOfWeek dow )
        {
            this.year = year;
            this.dow = dow;
        }

        @Override
        public List < LocalDate > call ( ) throws Exception
        {
            return datesOfYearforDaysOfWeek( this.year , Set.of( this.dow ) );
        }
    }


    // Boilerplate code taken from `ExecutorService` interface Javadoc, and slightly modified.
    private void shutdownAndAwaitTermination ( ExecutorService executorService )
    {
        executorService.shutdown(); // Disable new tasks from being submitted
        try
        {
            // Wait a while for existing tasks to terminate
            if ( ! executorService.awaitTermination( 60 , TimeUnit.SECONDS ) )
            {
                executorService.shutdownNow(); // Cancel currently executing tasks
                // Wait a while for tasks to respond to being cancelled
                if ( ! executorService.awaitTermination( 60 , TimeUnit.SECONDS ) )
                { System.err.println( "Pool did not terminate" ); }
            }
        }
        catch ( InterruptedException ex )
        {
            // (Re-)Cancel if current thread also interrupted
            executorService.shutdownNow();
            // Preserve interrupt status
            Thread.currentThread().interrupt();
        }
    }
}

Upvotes: 2

aayoustic
aayoustic

Reputation: 109

Explanation:

If the 1st Jan was a Saturday, which means that week's Friday will be 1 day before the 1st Jan i.e. 31st Dec. When you set the below field for Friday, it's setting the value to 31 Dec, and because of your while condition, it doesn't execute the loop.

calendar.set(Calendar.DAY_OF_WEEK, day);

This code won't work for an edge case i.e. any year starting with a Saturday won't print Fridays. E.g. 2011, 2028, etc.

The below method sets to the 1st Friday/Sat/Any-Weekday of the month:

    private void setNextAvailableDay(Calendar calendar, int day) {
        Date currTime = calendar.getTime();
        calendar.set(Calendar.DAY_OF_WEEK, day);
        if(currTime.after(calendar.getTime()))
            calendar.add(Calendar.DAY_OF_MONTH, 7);
    }
calendar.set(Calendar.DAY_OF_WEEK, day);
// can be replaced by
setNextAvailableDay(calendar, day)

Upvotes: 0

Related Questions