13

I recently came across a task where i have to get all Fridays in a date range. I wrote a small piece of code and was surprised see some strange behaviour.

Below is my code:

public class Friday {
    public static void main(String[]args){
        String start = "01/01/2009";
        String end = "12/09/2013";
        String[] startTokens = start.split("/");
        String[] endTokens = end.split("/");
        Calendar  startCal = new GregorianCalendar(Integer.parseInt(startTokens[2]),Integer.parseInt(startTokens[1])-1,Integer.parseInt(startTokens[0]));
        Calendar endCal = new GregorianCalendar(Integer.parseInt(endTokens[2]),Integer.parseInt(endTokens[1])-1, Integer.parseInt(endTokens[0]));

        int startYear = Integer.parseInt(startTokens[2]);
        int endYear = Integer.parseInt(endTokens[2]); 


        int startWeek = startCal.get(Calendar.WEEK_OF_YEAR);
        int endWeek = endCal.get(Calendar.WEEK_OF_YEAR);

        Calendar cal = new GregorianCalendar();
        cal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
    //  cal.setMinimalDaysInFirstWeek(7);
        ArrayList<String> main = new ArrayList<String>();
        while(startYear <= endYear ){
               cal.set(Calendar.YEAR, startYear);
               System.out.println(cal.getMinimalDaysInFirstWeek());
                if(startYear == endYear){
                    main.addAll(getFridays(startWeek, endWeek, cal));
                }
                else{
                    main.addAll(getFridays(startWeek, 52, cal));
                    startWeek = 1;
                }
                startYear =startYear +1;
        }

        for(String s: main){
            System.err.println(s);
        }
    }
    public static ArrayList<String> getFridays(int startWeek, int endWeek, Calendar cal){
        ArrayList<String> fridays = new ArrayList<String>();
        while(startWeek <= endWeek){
            cal.set(Calendar.WEEK_OF_YEAR, startWeek);
            fridays.add(cal.getTime().toString());
            startWeek = startWeek+1;
        }
        return fridays;
    }
}

Now when I ran the code i noticed that Fridays of 2011 are missing. After some debugging and online browsing i figured that Calendar.WEEK_OF_YEAR is locale specific and I have to use setMinimalDaysInFirstWeek(7) to fix it.

So uncommented the related line in the above code.

From what I understood now first week of year should start from full week of year.

For example Jan 1 2010 is friday. But it should not show up in results as i configured it to treat that week begins from Jan 3rd. But Now i still see the Jan 1 as friday

I am very much confused. Can some one explain why it is happening?

These Stackoverflow articles helped me a bit:

Why dec 31 2010 returns 1 as week of year?

Understanding java.util.Calendar WEEK_OF_YEAR

Community
  • 1
  • 1
javaMan
  • 6,352
  • 20
  • 67
  • 94

8 Answers8

6

Here is an easier method, using the wonderful http://www.joda.org/joda-time/ library:

String start = "01/01/2009";
String end = "12/09/2013";
DateTimeFormatter pattern = DateTimeFormat.forPattern("dd/MM/yyyy");
DateTime startDate = pattern.parseDateTime(start);
DateTime endDate = pattern.parseDateTime(end);

List<DateTime> fridays = new ArrayList<>();

while (startDate.isBefore(endDate)){
    if ( startDate.getDayOfWeek() == DateTimeConstants.FRIDAY ){
        fridays.add(startDate);
    }
    startDate = startDate.plusDays(1);
}

at the end of this, you'd have the fridays in the fridays array. Simple?

Or if you want to speed things up, once you have gotten a friday, you can switch from using days, to using weeks:

String start = "01/01/2009";
String end = "12/09/2013";
DateTimeFormatter pattern = DateTimeFormat.forPattern("dd/MM/yyyy");
DateTime startDate = pattern.parseDateTime(start);
DateTime endDate = pattern.parseDateTime(end);

List<DateTime> fridays = new ArrayList<>();
boolean reachedAFriday = false;
while (startDate.isBefore(endDate)){
    if ( startDate.getDayOfWeek() == DateTimeConstants.FRIDAY ){
        fridays.add(startDate);
        reachedAFriday = true;
    }
    if ( reachedAFriday ){
        startDate = startDate.plusWeeks(1);
    } else {
        startDate = startDate.plusDays(1);
    }
}
dat3450
  • 954
  • 3
  • 13
  • 27
Nikola Yovchev
  • 9,498
  • 4
  • 46
  • 72
  • Thanks for the solution. Is there any advantage in using Joda. I mean Rob's and immsbora's suggestions also looks good. So, curious to know if there is any advantage by adding dependency. Interestingly no one answered why above code is acting weird. :P every one gave new solution :D – javaMan Dec 11 '13 at 19:57
  • 2
    Joda is generally more bug free than code you'd create with Calendar and Date. I am not saying the other answers are not correct, but many developers feel that joda provides a cleaner api than Calendar/Date and that is why it was suggested as a jsr-310 (improvement of Java Date, to be done somewhere in JDK8). Generally calendar and date are classes that are too much locale related, too much subtleties, too much possibilities something can go wrong. That is why many developers immediately start thinking in joda when working with dates. – Nikola Yovchev Dec 11 '13 at 20:07
  • 1
    I wouldn't even consider code that uses Calendar or Date production ready, that is why I didn't pay attention what your code does and where the errors might be in. It can be in the many subtleties I told you about in my previous comment. My advice is to ditch Calendar and Date completely and just use joda for every date related operation. The api is cleaner and has so much nicer features like chaining, fluent methods, etc. etc. – Nikola Yovchev Dec 11 '13 at 20:15
  • Thanks baba. I will wait some time to see if anybody explains what is wrong in above code. If i dont get answer i will accepts yours. – javaMan Dec 11 '13 at 20:24
  • 2
    I am wondering that nobody has noticed the wrong pattern after almost 3 years. The pattern should be "dd/MM/yyyy" (months instead of minutes). – Meno Hochschild Sep 16 '16 at 14:21
4

tl;dr

someLocalDate.with(                                      // Date-only value without time-of-day and without time zone, represented by `LocalDate` class.
    TemporalAdjusters.nextOrSame ( DayOfWeek.FRIDAY ) )  // Moving from one `LocalDate` object to another, to find the next Friday unless the starting date is already a Friday.
)                                                        // Return a `LocalDate` object.

java.time

The other Answers are outdated. The old java.util.Date/.Calendar classes have been supplanted in Java 8 and later by the new java.time framework. Joda-Time library is excellent, continues to be maintained, and even inspired java.time. But the Joda-Time team recommends moving on to java.time as soon as is convenient.

LocalDate

The java.time classes include LocalDate for a date-only value without time-of-day nor time zone. See Tutorial.

First parse your input strings to get LocalDate objects.

String inputStart = "01/01/2009";
String inputStop = "12/09/2013";  // 258 Fridays.
// String inputStop = "01/01/2009";  // 0 Friday.
// String inputStop = "01/02/2009";  // 1 Friday.

DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "MM/dd/yyyy" );

LocalDate start = LocalDate.parse ( inputStart , formatter );
LocalDate stop = LocalDate.parse ( inputStop , formatter );

In your own code, try-catch for exception in case of bad inputs. And verify that stop is indeed the same or later than start.

TemporalAdjusters

The java.time framework includes the TemporalAdjuster interface as a way of shifting date-time values. For example, getting the next or same Friday for any particular date. On your starting date, call with(TemporalAdjuster adjuster) and pass a pre-defined implementation of a TemporalAdjuster from the class TemporalAdjusters (note the plural s). See Tutorial.

List<LocalDate> fridays = new ArrayList<> ();  // Collect each Friday found.
LocalDate nextOrSameFriday = start.with ( TemporalAdjusters.nextOrSame ( DayOfWeek.FRIDAY ) );
// Loop while we have a friday in hand (non-null) AND that friday is not after our stop date (isBefore or isEqual the stop date).
while ( ( null != nextOrSameFriday ) & (  ! nextOrSameFriday.isAfter ( stop ) ) ) {
    fridays.add ( nextOrSameFriday );  //  Remember this friday.
    nextOrSameFriday = nextOrSameFriday.plusWeeks ( 1 );  // Move to the next Friday, setting up for next iteration of this loop.
}

Dump to console.

System.out.println ( "From: " + start + " to: " + stop + " are " + fridays.size () + " Fridays: " + fridays );

From: 2009-01-01 to: 2013-12-09 are 258 Fridays: [2009-01-02, 2009-01-09, 2009-01-16, 2009-01-23, 2009-01-30, 2009-02-06, 2009-02-13, 2009-02-20, 2009-02-27, 2009-03-06, 2009-03-13, 2009-03-20, 2009-03-27, 2009-04-03, 2009-04-10, 2009-04-17, 2009-04-24, 2009-05-01, 2009-05-08, 2009-05-15, 2009-05-22, 2009-05-29, 2009-06-05, 2009-06-12, 2009-06-19, 2009-06-26, 2009-07-03, 2009-07-10, 2009-07-17, 2009-07-24, 2009-07-31, 2009-08-07, 2009-08-14, 2009-08-21, 2009-08-28, 2009-09-04, 2009-09-11, 2009-09-18, 2009-09-25, 2009-10-02, 2009-10-09, 2009-10-16, 2009-10-23, 2009-10-30, 2009-11-06, 2009-11-13, 2009-11-20, 2009-11-27, 2009-12-04, 2009-12-11, 2009-12-18, 2009-12-25, 2010-01-01, 2010-01-08, 2010-01-15, 2010-01-22, 2010-01-29, 2010-02-05, 2010-02-12, 2010-02-19, 2010-02-26, 2010-03-05, 2010-03-12, 2010-03-19, 2010-03-26, 2010-04-02, 2010-04-09, 2010-04-16, 2010-04-23, 2010-04-30, 2010-05-07, 2010-05-14, 2010-05-21, 2010-05-28, 2010-06-04, 2010-06-11, 2010-06-18, 2010-06-25, 2010-07-02, 2010-07-09, 2010-07-16, 2010-07-23, 2010-07-30, 2010-08-06, 2010-08-13, 2010-08-20, 2010-08-27, 2010-09-03, 2010-09-10, 2010-09-17, 2010-09-24, 2010-10-01, 2010-10-08, 2010-10-15, 2010-10-22, 2010-10-29, 2010-11-05, 2010-11-12, 2010-11-19, 2010-11-26, 2010-12-03, 2010-12-10, 2010-12-17, 2010-12-24, 2010-12-31, 2011-01-07, 2011-01-14, 2011-01-21, 2011-01-28, 2011-02-04, 2011-02-11, 2011-02-18, 2011-02-25, 2011-03-04, 2011-03-11, 2011-03-18, 2011-03-25, 2011-04-01, 2011-04-08, 2011-04-15, 2011-04-22, 2011-04-29, 2011-05-06, 2011-05-13, 2011-05-20, 2011-05-27, 2011-06-03, 2011-06-10, 2011-06-17, 2011-06-24, 2011-07-01, 2011-07-08, 2011-07-15, 2011-07-22, 2011-07-29, 2011-08-05, 2011-08-12, 2011-08-19, 2011-08-26, 2011-09-02, 2011-09-09, 2011-09-16, 2011-09-23, 2011-09-30, 2011-10-07, 2011-10-14, 2011-10-21, 2011-10-28, 2011-11-04, 2011-11-11, 2011-11-18, 2011-11-25, 2011-12-02, 2011-12-09, 2011-12-16, 2011-12-23, 2011-12-30, 2012-01-06, 2012-01-13, 2012-01-20, 2012-01-27, 2012-02-03, 2012-02-10, 2012-02-17, 2012-02-24, 2012-03-02, 2012-03-09, 2012-03-16, 2012-03-23, 2012-03-30, 2012-04-06, 2012-04-13, 2012-04-20, 2012-04-27, 2012-05-04, 2012-05-11, 2012-05-18, 2012-05-25, 2012-06-01, 2012-06-08, 2012-06-15, 2012-06-22, 2012-06-29, 2012-07-06, 2012-07-13, 2012-07-20, 2012-07-27, 2012-08-03, 2012-08-10, 2012-08-17, 2012-08-24, 2012-08-31, 2012-09-07, 2012-09-14, 2012-09-21, 2012-09-28, 2012-10-05, 2012-10-12, 2012-10-19, 2012-10-26, 2012-11-02, 2012-11-09, 2012-11-16, 2012-11-23, 2012-11-30, 2012-12-07, 2012-12-14, 2012-12-21, 2012-12-28, 2013-01-04, 2013-01-11, 2013-01-18, 2013-01-25, 2013-02-01, 2013-02-08, 2013-02-15, 2013-02-22, 2013-03-01, 2013-03-08, 2013-03-15, 2013-03-22, 2013-03-29, 2013-04-05, 2013-04-12, 2013-04-19, 2013-04-26, 2013-05-03, 2013-05-10, 2013-05-17, 2013-05-24, 2013-05-31, 2013-06-07, 2013-06-14, 2013-06-21, 2013-06-28, 2013-07-05, 2013-07-12, 2013-07-19, 2013-07-26, 2013-08-02, 2013-08-09, 2013-08-16, 2013-08-23, 2013-08-30, 2013-09-06, 2013-09-13, 2013-09-20, 2013-09-27, 2013-10-04, 2013-10-11, 2013-10-18, 2013-10-25, 2013-11-01, 2013-11-08, 2013-11-15, 2013-11-22, 2013-11-29, 2013-12-06]


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.

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

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

Using a JDBC driver compliant with JDBC 4.2 or later, you may exchange java.time objects directly with your database. No need for strings nor 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.

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

First off, I would not bother with weeks. Set the Calendar to the beginning of the range, and figure out which DOW it is, then increment to get to the next Friday, then simply loop adding 7 days until you are at the end of the range.

Actually, since you are always only going forward, should be something like:

int daysToAdd = FridayDOW - currentDOW;
if (daysToAdd < 0) daysToAdd += 7; 
Date startDate = currentDate.add(Calendar.DAYS, daysToAdd);

Yeah, like that.

Ok, actually, for kicks, here it is in Java 8:

@Test
public void canFindAllFridaysInRange(){
    start = LocalDate.of(2013, 5, 10);
    end = LocalDate.of(2013, 8,30);

    DayOfWeek dowOfStart = start.getDayOfWeek();
    int difference = DayOfWeek.FRIDAY.getValue() - dowOfStart.getValue();
    if (difference < 0) difference += 7;

    List<LocalDate> fridaysInRange = new ArrayList<LocalDate>();

    LocalDate currentFriday = start.plusDays(difference);
    do {
        fridaysInRange.add(currentFriday);
        currentFriday = currentFriday.plusDays(7);
    } while (currentFriday.isBefore(end));

    System.out.println("Fridays in range: " + fridaysInRange);
}

Got to love the new date classes!! Of course a lambda would condense this further.

Rob
  • 11,446
  • 7
  • 39
  • 57
1

This code will print all dates having Friday.

public class Friday {
   public static void main(String[] args) throws ParseException {
     String start = "01/01/2013";
     String end = "12/01/2013";
     SimpleDateFormat dateFormat=new SimpleDateFormat("dd/MM/yyyy");
     Calendar scal=Calendar.getInstance();
     scal.setTime(dateFormat.parse(start));
     Calendar ecal=Calendar.getInstance();
     ecal.setTime(dateFormat.parse(end));

     ArrayList<Date> fridayDates=new ArrayList<>();

     while(!scal.equals(ecal)){
         scal.add(Calendar.DATE, 1);
         if(scal.get(Calendar.DAY_OF_WEEK)==Calendar.FRIDAY){
             fridayDates.add(scal.getTime());
         }
     }

     System.out.println(fridayDates);
 }
}
Sunil Singh Bora
  • 771
  • 9
  • 24
1

Here a solution based on new stream-features of Java-8 and using my library Time4J (v4.18 or later):

String start = "01/01/2009";
String end = "12/09/2013";

ChronoFormatter<PlainDate> f =
    ChronoFormatter.ofDatePattern("dd/MM/yyyy", PatternType.CLDR, Locale.ROOT);

PlainDate startDate =
    f.parse(start).with(PlainDate.DAY_OF_WEEK.setToNextOrSame(Weekday.FRIDAY));
PlainDate endDate = f.parse(end);

Stream<PlainDate> fridays =
    DateInterval.stream(Duration.of(1, CalendarUnit.WEEKS), startDate, endDate);

fridays.forEachOrdered(System.out::println);

// output
2009-01-02 
2009-01-09 
... 
2013-08-30 
2013-09-06

// other example: list of fridays in ISO-8601-format
List<String> result =
  DateInterval.between(startDate, endDate)
    .stream(Duration.of(1, CalendarUnit.WEEKS))
    .map((date) -> date.toString()) // or maybe use dd/MM/yyyy => f.format(date)
    .collect(Collectors.toList());

By the way, Java-9 will offer a similar solution (but with exclusive end date boundary), see also this enhancement-issue.

Meno Hochschild
  • 42,708
  • 7
  • 104
  • 126
0

with Lamma Date :

List<Date> fridays = Dates.from(2015, 12, 1).to(2016, 1, 1).byWeek().on(DayOfWeek.FRIDAY).build();
for (Date friday: fridays) {
    System.out.println(friday);
}
Max
  • 2,065
  • 24
  • 20
0
public static List<Date> getWeekNumberList(Date currentMonthDate) {
    List<Date> dates = new ArrayList<>(10);
    Calendar startCalendar = Calendar.getInstance();
    startCalendar.setTime(currentMonthDate);
    startCalendar.set(Calendar.DAY_OF_MONTH,
            startCalendar.getActualMinimum(Calendar.DAY_OF_MONTH));
    Calendar endCalendar = Calendar.getInstance();
    endCalendar.setTime(currentMonthDate);
    endCalendar.set(Calendar.DAY_OF_MONTH,
            endCalendar.getActualMaximum(Calendar.DAY_OF_MONTH));

    Date enddate = endCalendar.getTime();
    while (startCalendar.getTime().before(enddate)) {
        if (startCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY) {
            Date result = startCalendar.getTime();
            dates.add(result);
            startCalendar.add(Calendar.WEEK_OF_MONTH, 1);
        } else {

            startCalendar.add(Calendar.DAY_OF_MONTH, 1);
        }
    }
    return dates;
}
nAkhmedov
  • 3,522
  • 4
  • 37
  • 72
  • The `Date` and `Calendar` classes are now legacy, having proved to be confusing, poorly designed, and troublesome. Supplanted by the java.time classes. With new conversion methods added to the old classes, there is no reason to use the old date-time classes. – Basil Bourque Jan 15 '17 at 17:07
0

Using Java 8+

LocalDate s= LocalDate.now();
LocalDate e= LocalDate.now().plusMonths(5);

List<LocalDate> dates2 = s.with(TemporalAdjusters.next(DayOfWeek.FRIDAY)).datesUntil(e, Period.ofWeeks(1)).collect(Collectors.toList());
dates2.forEach(x->System.out.println(x));
Tomthomas
  • 59
  • 2