views:

5542

answers:

13

I am working on a project where the requirement is to have a date calculated as being the last Friday of a given month. I think I have a solution that only uses standard Java, but I was wondering if anyone knew of anything more concise or efficient. Below is what I tested with for this year:

    for (int month = 0; month < 13; month++) {
        GregorianCalendar d = new GregorianCalendar();
        d.set(d.MONTH, month);
        System.out.println("Last Week of Month in " + d.getDisplayName(d.MONTH, Calendar.LONG, Locale.ENGLISH) + ": " + d.getLeastMaximum(d.WEEK_OF_MONTH));
        d.set(d.DAY_OF_WEEK, d.FRIDAY);
        d.set(d.WEEK_OF_MONTH, d.getActualMaximum(d.WEEK_OF_MONTH));
        while (d.get(d.MONTH) > month || d.get(d.MONTH) < month) {
            d.add(d.WEEK_OF_MONTH, -1);
        }
        Date dt = d.getTime();
        System.out.println("Last Friday of Last Week in  " + d.getDisplayName(d.MONTH, Calendar.LONG, Locale.ENGLISH) + ": " + dt.toString());
    }
A: 

There are a number of useful methods for calculating the day of the week. With a little bit of added algebra, one of these should be adaptable to your problem.

Daniel Spiewak
+1  A: 

That looks like a perfectly acceptable solution. If that works, use it. That is minimal code and there's no reason to optimize it unless you have to.

scubabbl
Sorry, that code is neither minimal, good, nor even correct...
Mwanji Ezana
+1  A: 

That won't work. Your loop is going to loop 13 times (0 thru 12).

To what answer is this referring?
Bno
It refers to the question
WW
A: 

Slightly easier to read, brute-force approach:

public int getLastFriday(int month, int year) {
    Calendar cal = Calendar.getInstance();
    cal.set(year, month, 1, 0, 0, 0); // set to first day of the month
    cal.set(Calendar.MILLISECOND, 0);

    int friday = -1;
    while (cal.get(Calendar.MONTH) == month) { 
        if (cal.get(Calendar.DAY_OF_WEEK) == Calendar.FRIDAY) { // is it a friday?
            friday = cal.get(Calendar.DAY_OF_MONTH);
            cal.add(Calendar.DAY_OF_MONTH, 7); // skip 7 days
        } else {
            cal.add(Calendar.DAY_OF_MONTH, 1); // skip 1 day
        }
    }
    return friday;
}
binil
+4  A: 

I would use a library like Jodatime. It has a very useful API and it uses normal month numbers. And best of all, it is thread safe.

I think that you can have a solution with (but possibly not the shortest, but certainly more readable):

DateTime now = new DateTime();   
DateTime dt = now.dayOfMonth().withMaximumValue().withDayOfWeek(DateTimeConstants.FRIDAY);
if (dt.getMonthOfYear() != now.getMonthOfYear()) {
  dt = dt.minusDays(7);
}    
System.out.println(dt);
Hans Doggen
+5  A: 

You never need to loop to find this out. For determining the "last Friday" date for this month, start with the first day of next month. Subtract the appropriate number of days depending on what (numerical) day of the week the first day of the month falls on. There's your "last Friday." I'm pretty sure it can be boiled down to a longish one-liner, but I'm not a java dev. So I'll leave that to someone else.

+1  A: 

Though I agree with scubabbl, here is a version without an inner while.

int year = 2008;
for (int m = Calendar.JANUARY; m <= Calendar.DECEMBER; m++) {
    Calendar cal = new GregorianCalendar(year, m, 1);
    cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
    int diff = Calendar.FRIDAY - cal.get(Calendar.DAY_OF_WEEK);
    if (diff > 0) {
        diff -= 7;
    }
    cal.add(Calendar.DAY_OF_MONTH, diff);
    System.out.println(cal.getTime());
}
Bno
+2  A: 

You need to know two things - the number of days in the month, and the weekday the first of the month falls on.

If the first day of the month is a

  • Sunday, then the last Friday is always the 27th.
  • Monday, then the last Friday is always the 26th.
  • Tuesday, then the last Friday is always the 25th.
  • Wednesday, then the last Friday is the 24th, unless there are 31 days in the month, then it's the 31st
  • Thursday, then the last Friday is the 23rd, unless there are 30 days or more in the month, then it's the 30th.
  • Friday, then the last Friday is the 22nd, unless there are 29 days or more in the month, then it's the 29th.
  • Saturday, then the last Friday is always the 28th.

There are only three special cases. A single switch statement and three if statements (or ternary operators if you like every case to have a single line...)

Work it out on paper. Don't need any special libraries, functions, julian conversions, etc (well, except to get the weekday the 1st falls on, and maybe the number of days that month... )

Aaron implemented it in Java.

Adam Davis
+11  A: 

Based on marked23's suggestion:

public Date getLastFriday( int month, int year ) {
   Calendar cal = Calendar.getInstance();
   cal.set( year, month + 1, 1 );
   cal.add( Calendar.DAY_OF_MONTH, -( cal.get( Calendar.DAY_OF_WEEK ) % 7 + 1 ) );
   return cal.getTime();
}
ColinD
Great answer, but why use the concrete GregorianCalendar rather than the abstract Calendar returned by Calendar.getInstance()?
Mwanji Ezana
great answer. seems to be the shortest and most concise. I'd mod it up again if i could.
Randyaa
cal.get( Calendar.DAY_OF_WEEK ) % 7 + 1; The %7+1 is completely unnecessary here. cal.get(Calendar.DAY_OF_WEEK) will return a value from 1 to 7 here already (Sunday being 1, Saturday being 7). Modding by 7 then adding 1 literally does nothing.
Daniel Bingham
No, n % 7 + 1 actually returns n + 1 unless n == 7 in which case it returns 1.
ColinD
(For n = 1 through 7 anyway.)
ColinD
A: 

ColinD's solution shouldn't work: try running it with December 2008 and it'll try setting the 13th month of a year. However, replacing "cal.set(...)" with "cal.add(Calendar.MONTH, 1)" should work fine.

rassie
Actually, it works fine with December 2008 and because a Calendar is lenient by default. And you can't just replace cal.set() by cal.add() because then you won't be at the first day of the month.
Mwanji Ezana
+1  A: 

code for Adam Davis's algorithm

public static int getLastFriday(int month, int year)
{
Calendar cal = Calendar.getInstance();
cal.set(year, month, 1, 0, 0, 0); // set to first day of the month
cal.set(Calendar.MILLISECOND, 0);

int firstDay = cal.get(Calendar.DAY_OF_WEEK);
int daysOfMonth = cal.getMaximum(Calendar.DAY_OF_MONTH);

switch (firstDay)
{
    case Calendar.SUNDAY :
        return 27;
    case Calendar.MONDAY :
        return 26;
    case Calendar.TUESDAY :
        return 25;
    case Calendar.WEDNESDAY :
        if (daysOfMonth == 31) return 31;
        return 24;
    case Calendar.THURSDAY :
        if (daysOfMonth >= 30) return 30;
        return 23;
    case Calendar.FRIDAY :
        if (daysOfMonth >= 29) return 29;
        return 22;
    case Calendar.SATURDAY :
        return 28;
}
throw new RuntimeException("what day of the month?");
}}
Aaron
Awesome! Thanks.
Adam Davis
A: 

Hope this helps..

public static void getSundaysInThisMonth(int monthNumber, int yearNumber){ //int year =2009; //int dayOfWeek = Calendar.SUNDAY; // instantiate Calender and set to first Sunday of 2009 Calendar cal = new GregorianCalendar(); cal.set(Calendar.MONTH, monthNumber-1); cal.set(Calendar.YEAR, yearNumber); cal.set(Calendar.DATE, 1); int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK); int dateOfWeek = cal.get(Calendar.DATE); while (dayOfWeek != Calendar.SUNDAY) { cal.set(Calendar.DATE, ++dateOfWeek); dayOfWeek = cal.get(Calendar.DAY_OF_WEEK); } cal.set(Calendar.DATE, dateOfWeek);

int i = 1;
while (cal.get(Calendar.YEAR) == yearNumber && cal.get(Calendar.MONTH)==monthNumber-1)
{
        System.out.println("Sunday " + " " + i + ": " + cal.get(Calendar.DAY_OF_MONTH));
        cal.add(Calendar.DAY_OF_MONTH, 7);
        i++;
}

} public static void main(String args[]){ getSundaysInThisMonth(1,2009); }

+3  A: 

Let Calendar.class do its magic for you ;)

pCal.set(GregorianCalendar.DAY_OF_WEEK,Calendar.FRIDAY);
pCal.set(GregorianCalendar.DAY_OF_WEEK_IN_MONTH, -1);