19

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());
    }
Nick Klauer
  • 5,893
  • 5
  • 42
  • 68

16 Answers16

38

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);
Carl Manaster
  • 39,912
  • 17
  • 102
  • 155
  • This is the best answer. I was just looking for this and all I had to do was read the documentation for DAY_OF_WEEK_IN_MONTH ... or this answer. – Allen Jun 04 '12 at 12:45
  • The simplest and the best – Axel Aug 23 '13 at 11:20
  • Note that `Calendar.lenient` must be reset to its default value (`true`), using `pCal.setLenient(true)`. Otherwise an `IllegalArgumentException` will be thrown. – MC Emperor May 25 '16 at 07:14
27

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();
}
Community
  • 1
  • 1
ColinD
  • 108,630
  • 30
  • 201
  • 202
  • Great answer, but why use the concrete GregorianCalendar rather than the abstract Calendar returned by Calendar.getInstance()? – Mwanji Ezana Sep 16 '08 at 21:29
  • 2
    great answer. seems to be the shortest and most concise. I'd mod it up again if i could. – Randyaa Sep 17 '08 at 04:52
  • 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 Sep 28 '09 at 14:16
  • No, n % 7 + 1 actually returns n + 1 unless n == 7 in which case it returns 1. – ColinD Sep 28 '09 at 16:25
  • (For n = 1 through 7 anyway.) – ColinD Sep 28 '09 at 16:27
  • [here](https://praveenlobo.com/blog/get-last-friday-of-the-month-in-java/) is a generalized implementation of this answer to work with *any* day. If possible, use `java.time` instead. – Praveen Lobo Aug 07 '18 at 04:41
18

java.time

Using java.time library built into Java 8 and later, you may use TemporalAdjusters.lastInMonth:

val now = LocalDate.now() 
val lastInMonth = now.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY))

You may choose any day from the DayOfWeek enum.

If you need to add time information, you may use any available LocalDate to LocalDateTime conversion like

lastFriday.atStartOfDay() // e.g. 2015-11-27T00:00
Michael
  • 41,989
  • 11
  • 82
  • 128
Przemek
  • 7,111
  • 3
  • 43
  • 52
  • A better replacement for `GregorianCalendar` would be [`ZonedDateTime`](http://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html) rather than `LocalDateTime`. The “Local” means *any* locality, with no particular time zone at all, so it does *not* represent a specific moment on the timeline. In contrast, a `ZonedDateTime` is a specific moment on the timeline, with an assigned time zone ([`ZoneId`](http://docs.oracle.com/javase/8/docs/api/java/time/ZoneId.html)). – Basil Bourque Jun 25 '16 at 21:14
  • 1
    When calling `LocalDate.now`, I suggest always passing the desired/expected time zone, [`ZoneId`](http://docs.oracle.com/javase/8/docs/api/java/time/ZoneId.html). If omitted, the JVM’s current default time zone is applied silently. That default can be changed at any moment *during runtime* by any code in any app in any thread within the JVM. So it is unwise to depend on such a value. – Basil Bourque Jun 25 '16 at 21:16
10

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.

  • [here](https://praveenlobo.com/blog/get-last-friday-of-the-month-in-java/) is a generalized implementation of this answer to work with *any* day. If possible, use `java.time` instead. – Praveen Lobo Aug 07 '18 at 04:43
7

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
  • 1,796
  • 2
  • 16
  • 13
2

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

Community
  • 1
  • 1
Adam Davis
  • 91,931
  • 60
  • 264
  • 330
2

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?");
}}
Community
  • 1
  • 1
Aaron
  • 874
  • 3
  • 17
  • 34
2

here's how to get the last friday, or whatever week day, of the month:

Calendar thisMonth = Calendar.getInstance();
dayOfWeek = Calendar.FRIDAY; // or whatever
thisMonth.set(Calendar.WEEK_OF_MONTH, thisMonth.getActualMaximum(Calendar.WEEK_OF_MONTH);;
thisMonth.set(Calendar.DAY_OF_WEEK, dayOfWeek);
int lastDay = thisMonth.get(Calendar.DAY_OF_MONTH); // this should be it.
josephus
  • 8,284
  • 1
  • 37
  • 57
1

Below program is for the last Friday of each month. it can be used to get the last of any day of the week in any month. The variable offset=0 means current month(system date), offset=1 means next month, so on. The getLastFridayofMonth(int offset) method will return the last Friday.

import java.text.SimpleDateFormat;
import java.util.Calendar;

public class LastFriday {

  public static Calendar getLastFriday(Calendar cal,int offset){
    int dayofweek;//1-Sunday,2-Monday so on....
    cal.set(Calendar.MONTH,cal.get(Calendar.MONTH)+offset);
    cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH)); //set calendar to last day of month
    dayofweek=cal.get(Calendar.DAY_OF_WEEK); //get the day of the week for last day of month set above,1-sunday,2-monday etc
    if(dayofweek<Calendar.FRIDAY)  //Calendar.FRIDAY will return integer value =5 
      cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH)-7+Calendar.FRIDAY-dayofweek);
    else
      cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH)+Calendar.FRIDAY-dayofweek); 

    return cal;
  }

  public static String  getLastFridayofMonth(int offset) { //offset=0 mean current month
    final String DATE_FORMAT_NOW = "dd-MMM-yyyy";
    Calendar cal = Calendar.getInstance();
    SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_NOW);
    cal=getLastFriday(cal,offset);
    return sdf.format(cal.getTime()); 

  }

  public static void main(String[] args) {
    System.out.println(getLastFridayofMonth(0)); //0 = current month
    System.out.println(getLastFridayofMonth(1));//1=next month
    System.out.println(getLastFridayofMonth(2));//2=month after next month
  }

}
Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
1

In Java 8, we can do it simply as:

LocalDate lastFridayOfMonth = LocalDate
                                    .now()
                                    .with(lastDayOfMonth())
                                    .with(previous(DayOfWeek.FRIDAY));
KayV
  • 12,987
  • 11
  • 98
  • 148
  • 1
    Actually we have to use previousOrSame(DayOfWeek.FRIDAY) instead of previous(DayOfWeek.FRIDAY) to get Exact last Friday of a Month – Digital Apr 17 '18 at 20:19
  • @Elenasys `lastDayOfMonth()`and `previous()` are static methods of [`TemporalAdjusters`](https://docs.oracle.com/javase/9/docs/api/java/time/temporal/TemporalAdjusters.html). However, [Przemek obtains the same more simply](https://stackoverflow.com/a/33867973/5772882) using the same class. And there sems to be a bug here, you need to use `previousOrSame` rather than `previous`, or else if the last day of the month is a Friday, you won’t get that day. – Ole V.V. May 26 '19 at 15:57
1

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 Thomas
  • 13,699
  • 10
  • 57
  • 70
1

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());
}
Benno Richters
  • 15,378
  • 14
  • 42
  • 45
0

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);
  }
jalopaba
  • 8,039
  • 2
  • 44
  • 57
0
public static int lastSundayDate()
{
    Calendar cal = getCalendarInstance();
    cal.setTime(new Date(getUTCTimeMillis()));
    cal.set( Calendar.DAY_OF_MONTH , 25 );
    return (25 + 8 - (cal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY ? cal.get(Calendar.DAY_OF_WEEK) : 8));
}
Narendra
  • 3,069
  • 7
  • 30
  • 51
0

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
  • 12,657
  • 7
  • 36
  • 36
-1
public static Calendar getNthDow(int month, int year, int dayOfWeek, int n) {
    Calendar cal = Calendar.getInstance();
    cal.set(year, month, 1);
    cal.set(Calendar.DAY_OF_WEEK, dayOfWeek);
    cal.set(Calendar.DAY_OF_WEEK_IN_MONTH, n);
    return (cal.get(Calendar.MONTH) == month) && (cal.get(Calendar.YEAR) == year) ? cal : null;
}