Pulling all dates that refer to a specific date of the week in a year - Java
Struggling with this problem for a while, would love some kind of input.
The problem I want to solve is collecting all dates in one specific year that fall on a specific day of the week, for example, every Tuesday in 2014. Dates are stored in ArrayList<Date>
. This list is then returned.
It also needs to be confirmed so that the year is not 0 and the day of the week represented must be a number between 1 and 7.
If there is any problem I would like to know what I messed 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);
}
source to share
Your question doesn't specify what is the first day of the week, but your method makes it even more complicated to test the current day of the week. Start by checking the days of the week using the standard Calendar
,
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 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 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, so that every Tuesday in 2014 you can 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));
}
}
source to share
I think your main problem is this condition
if (currentDayOfWeek >= dayOfWeek) {
since this 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
that which is not what you want.
the condition should be
if (currentDayOfWeek == dayOfWeek) {
I also recommend using the getTime calendar method instead of parsing String
to get Date
.
source to share
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 working with date and time defines Monday as the first day of the week , indicated by the number 1. Sunday is 7.
Avoid juDate & .Calendar
The java.util.Date and .Calendar classes bundled with java are notoriously troublesome. Avoid them. They were superseded in Java 8 by the new java.time package. This package was inspired by Joda-Time, an alternative that remains an active viable project with some benefits.
Joda-Time and java.time use ISO 8601 by default .
Date-Only
For this question, we only need 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 define the boundaries of the year by getting a pair of objects LocalDate
for each first day of the year.
LocalDate startOfYear = Year.of( 2019 ).atDay( 1 ); // Determine first day of the year.
LocalDate startOfFollowingYear = startOfYear.plusYears( 1 );
Cycle increasing the date one day at a time. If this date turns out to be 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 );
Watch this code 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 carried away with functional lambda syntax . The method generates a stream in Java 9 and later. Then filter the stream by match in . LocalDate::datesUntil
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 sample code in Joda-Time 2.4 to collect all 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
Java.time infrastructure is built in Java 8 and later versions . These classes displace nasty old outdated date and time classes such as , , and . java.util.Date
Calendar
SimpleDateFormat
For more information, see the Oracle Tutorial . And search for many examples and explanations. Specification JSR 310 .
The Joda-Time project , now in maintenance mode , recommends moving to the java.time classes .
You can exchange java.time objects directly with your database. Use the JDBC driver, the appropriate JDBC 4.2 or later version . No need for strings, no need for java.sql.*
.
Where can I get the java.time classes?
- Java SE 8 , Java SE 9 , Java SE 10 , Java SE 11 and later are part of the Java standard API with an embedded implementation.
- Java 9 adds some minor features and fixes.
- Java SE 6 and Java SE 7
- Most of java.time functionality has been ported to Java 6 and 7 in ThreeTen-Backport .
- Android
- Later versions of Android bundle implementations of the java.time classes.
- For earlier Android versions (<26), the ThreeTenABP project adapts the ThreeTen-Backport (mentioned above). See How to use ThreeTenABPโฆ .
The ThreeTen-Extra project extends java.time with additional classes. This project is a testing ground for possible future additions to java.time. Here you can find some useful classes such as , , and others . Interval
YearWeek
YearQuarter
source to share