TheCrownedPixel
TheCrownedPixel

Reputation: 369

Pulling all dates that fall on specific date of the week in a year - Java

Been wrestling with this problem for a while, would love some input.

The problem I want to solve collects all of the dates in one specific year which fall on a particular day of the week, for example, every Tuesday in 2014. The dates are stored in an ArrayList<Date>. This list is then returned.

Also have to validate to make sure the year is not 0 and the day of the week submitted must be a number between 1-7.

If there are any issues, I would love to know what I have screwed up.

public List<Date> getDatesforDayOfWeek(int year, int dayOfWeek) throws InvalidDateException, ParseException {

    List<Date> dateList = new ArrayList<>();

    if (year <= 0 || (1 > dayOfWeek && dayOfWeek > 7)) {

        throw new InvalidDateException("Year or day of week is invalid.");

    } else {

        Calendar newCal = Calendar.getInstance();
        newCal.set(YEAR, year);
        newCal.set(DAY_OF_YEAR, 1);



        while (newCal.get(YEAR) < year + 1) {

            int currentDayOfWeek = newCal.get(DAY_OF_WEEK);

            Date newDate = null;
            if (currentDayOfWeek >= dayOfWeek) {

                int dayOfMonth = newCal.get(DAY_OF_MONTH);
                String strDayOfMonth = String.valueOf(dayOfMonth);
                String strYear = String.valueOf(year);

                DateUtility d1 = new DateUtility();
                Date passDate = newCal.getTime();

                String weekDay = d1.getWeekDayNameAbbreviation(passDate);
                String monthAbbreviation = d1.getMonthAbbreviation(passDate);


                String finalString = new String();

                finalString.concat(weekDay).concat(" ").
                        concat(monthAbbreviation).concat(" ").
                        concat(strDayOfMonth).concat(" ").
                        concat(strYear);

                SimpleDateFormat format = new SimpleDateFormat("EEE MMM dd YYYY");
                Date theDate = format.parse(finalString);

                dateList.add(theDate);
            }
            newCal.add(Calendar.DATE, 1);
        }

    }
    return (dateList);
}

Upvotes: 4

Views: 435

Answers (3)

Basil Bourque
Basil Bourque

Reputation: 339332

tl;dr

startOfYear                                              // `Year.of( 2019 ).atDay( 1 )` gets the first day of the year.
.datesUntil( startOfYear.plusYears( 1 ) )                // Generate a stream of incrementing `LocalDate` objects.
.filter(                                                 // Pull out the dates that are a Tuesday.
    t -> t.getDayOfWeek().equals( DayOfWeek.TUESDAY ) 
)
.collect( Collectors.toList() )                          // Return results in a `List` of `LocalDate` objects.

ISO 8601

The ISO 8601 standard for date-time work defines Monday as the first day of week, identified by number 1. Sunday is 7.

Avoid j.u.Date & .Calendar

The java.util.Date and .Calendar classes bundled with java are notoriously troublesome. Avoid them. They have been supplanted in Java 8 by the new java.time package. That package was inspired by Joda-Time, an alternative that remains an active viable project with some advantages.

Both Joda-Time and java.time use ISO 8601 by default.

Date-Only

For this Question, we need only dates, not time-of-day or time zones. Both Joda-Time and java.time offer a LocalDate class for this purpose.

java.time

Use Year.of and LocalDate::plusYears to determine the bounds of a year, yielding a pair of LocalDate objects for each first-day-of-year.

LocalDate startOfYear = Year.of( 2019 ).atDay( 1 );  // Determine first day of the year.
LocalDate startOfFollowingYear = startOfYear.plusYears( 1 );

Loop, incrementing the date one day at a time. If that date happens to be a Tuesday, add it to our collection.

LocalDate localDate = startOfYear;
List < LocalDate > tuesdays = new ArrayList <>( 55 ); // Set initialCapacity to maximum number of tuesdays in a year. Probably 53, but I'll go with 55 for good measure.
while ( localDate.isBefore( startOfFollowingYear ) )
{
    if ( localDate.getDayOfWeek().equals( DayOfWeek.TUESDAY ) )
    {
        tuesdays.add( localDate );
    }
    // Set up the next loop.
    localDate = localDate.plusDays( 1 );
}
System.out.println( tuesdays );

See this code run live at IdeOne.com.

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

Or get fancy with functional lambda syntax. The LocalDate::datesUntil method generates a stream, in Java 9 and later. Then filter the stream by a match on DayOfWeek.TUESDAY.

LocalDate startOfYear = Year.of( 2019 ).atDay( 1 );
Stream < LocalDate > stream = startOfYear.datesUntil( startOfYear.plusYears( 1 ) );
List < LocalDate > tuesdays = stream.filter( t -> t.getDayOfWeek().equals( DayOfWeek.TUESDAY ) ).collect( Collectors.toList() );

Joda-Time

Here is some example code in Joda-Time 2.4 for collecting all the Tuesdays in a year.

int year = 2014; 
String input = year + "-01-01";
LocalDate localDateInput = LocalDate.parse( input );
LocalDate firstTuesday = localDateInput.withDayOfWeek ( DateTimeConstants.TUESDAY );
LocalDate tuesday = firstTuesday;  // for incrementing by week.
List<LocalDate> list = new ArrayList<>();
while ( tuesday.getYear() == year ) {
  list.add( tuesday );
  tuesday.plusWeeks( 1 );
}

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Upvotes: 1

Jonathan Drapeau
Jonathan Drapeau

Reputation: 2610

I think your main problem lies in this condition

if (currentDayOfWeek >= dayOfWeek) {

since that will count any day that is "higher" than the day you want. If you pass 3, it will also count any day that is higher than 3, which isn't what you want.

the condition should be

if (currentDayOfWeek == dayOfWeek) {

I also recommend you use Calendar getTime method instead of parsing a String to get your Date.

Upvotes: 0

Elliott Frisch
Elliott Frisch

Reputation: 201467

Your question fails to specify which is first day of the week, but things are further complicated by your method for testing the current day of the week. Let's start with validating days of the week by using the Calendar standard,

private static boolean isValidDayOfWeek(int dayOfWeek) {
    switch (dayOfWeek) {
    // Seven days of the week.
    case Calendar.SUNDAY: case Calendar.MONDAY: case Calendar.TUESDAY: 
    case Calendar.WEDNESDAY: case Calendar.THURSDAY: case Calendar.FRIDAY:
    case Calendar.SATURDAY:
        return true;
    }
    return false;
}

It then follows that we can do something like,

public static List<Date> getDatesforDayOfWeek(int year, int dayOfWeek) {
    List<Date> dateList = new ArrayList<>();
    if (year <= 0 || !isValidDayOfWeek(dayOfWeek)) {
        return null;
    } else {
        Calendar newCal = Calendar.getInstance();
        newCal.set(Calendar.YEAR, year);
        newCal.set(Calendar.DAY_OF_YEAR, 1);
        // First, let's loop until we're at the correct day of the week.
        while (newCal.get(Calendar.DAY_OF_WEEK) != dayOfWeek) {
            newCal.add(Calendar.DAY_OF_MONTH, 1);
        }
        // Now, add the Date to the List. Then add a week and loop (stop
        // when the year changes).
        do {
            dateList.add(newCal.getTime());
            newCal.add(Calendar.DAY_OF_MONTH, 7);
        } while (newCal.get(Calendar.YEAR) == year);
    }
    return dateList;
}

Leaving us with main(). So, to get every Tuesday in 2014 you could then use -

public static void main(String[] args) {
    List<Date> tuesdays = getDatesforDayOfWeek(2014, Calendar.TUESDAY);
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
    for (Date d : tuesdays) {
        System.out.println(df.format(d));
    }
}

Upvotes: 1

Related Questions