2

I'm not seeing a good way to set a date to a certain day of the week for a certain week of the month. Joda-Time's LocalDate does not have a withWeekOfMonth method. I can see a possible algorithm, but it seems way to complicated, so I'm going to assume I'm missing something. What I need is to determine the next date someone is paid. And if they are paid on the Second Thursday of the Month, what date is that.

Anyone already solved this problem?

Ok, I was able to come up with this, which seems to work fine.

/**  
 * Finds a date such as 2nd Tuesday of a month.  
 */  
public static LocalDate calcDayOfWeekOfMonth( final DayOfWeek pDayOfWeek, final int pWeekOfMonth, final LocalDate pStartDate )  
{  
    LocalDate result = pStartDate;  
    int month = result.getMonthOfYear();  
    result = result.withDayOfMonth( 1 );  
    result = result.withDayOfWeek( pDayOfWeek.ordinal() );  
    if ( result.getMonthOfYear() != month )  
    {  
        result = result.plusWeeks( 1 );  
    }  
    result = result.plusWeeks( pWeekOfMonth - 1 );  
    return result;  
}  
Kevin Panko
  • 8,356
  • 19
  • 50
  • 61
  • Your code is hard to read without comments of what line is supposed to do (and you'll probably find as hard to read in a few weeks). It's now ven clear the signature. And what is `DayOfWeek`? – leonbloy Oct 30 '12 at 19:23

3 Answers3

7

I personally don't know of any super-simple way of doing it, this is what I use to get it:

/**
 * Calculates the nth occurrence of a day of the week, for a given month and
 * year.
 * 
 * @param dayOfWeek
 *            The day of the week to calculate the day for (In the range of
 *            [1,7], where 1 is Monday.
 * @param month
 *            The month to calculate the day for.
 * @param year
 *            The year to calculate the day for.
 * @param n
 *            The occurrence of the weekday to calculate. (ie. 1st, 2nd,
 *            3rd)
 * @return A {@link LocalDate} with the nth occurrence of the day of week,
 *         for the given month and year.
 */
public static LocalDate nthWeekdayOfMonth(int dayOfWeek, int month, int year, int n) {
    LocalDate start = new LocalDate(year, month, 1);
    LocalDate date = start.withDayOfWeek(dayOfWeek);
    return (date.isBefore(start)) ? date.plusWeeks(n) : date.plusWeeks(n - 1);
}

Example:

System.out.println(nthWeekdayOfMonth(4, 1, 2012, 2));

Output:

2012-01-12
Daniel Arndt
  • 2,268
  • 2
  • 17
  • 22
  • Thanks. I ended up doing the following: – Ray Van Eperen Oct 23 '12 at 17:33
  • @RayVanEperen Hi, I can't see your code in the comment. If you have a solution you preferred, please post it as another answer. Thanks. – Daniel Arndt Oct 24 '12 at 18:00
  • Sorry, I put the solution above in the original message. – Ray Van Eperen Oct 24 '12 at 21:44
  • @DanielArdnt : is not easy to spot which of your hard coded integers are constants and which are variables (arguments to your function). Perhaps you could rewrite that as a function? – leonbloy Oct 30 '12 at 19:27
  • @leonbloy Good point. My answer has been re-written as a function, and I have provided a quick example. – Daniel Arndt Oct 31 '12 at 01:15
  • @Daniel What this check does? ` return (date.isBefore(start)) ? date.plusWeeks(n) : date.plusWeeks(n - 1);`. Anybody please explain. – Dave Ranjan Jan 27 '17 at 13:21
  • @DaveRanjan `start` is the start of the month. `withDayOfWeek()` changes that date to the `dayOfWeek` you're requesting (ie. Thursday). When you do this conversion, there are two possibilities: (1) The date returned is the last Thursday of the *previous* month (ie. before `start`) or (2) The date returned is the first Thursday of the month. If you're already on the first Thursday of the month, you only need to move ahead n-1 weeks. – Daniel Arndt Jul 04 '17 at 18:42
  • Using a `java.time.temporal.TemporalAdjuster` in *java.time* (the replacement for Joda-Time) is much easier. See modern solution in [Answer by Live and Let Live](https://stackoverflow.com/a/65460669/642706). – Basil Bourque Dec 27 '20 at 03:21
3

Solution using Java-8 java.time API

With the modern date-time API of Java, you can pass TemporalAdjusters#dayOfWeekInMonth as an argument to LocalDate#with to achieve this.

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

public class Main {
    public static void main(String[] args) {
        // Test with today's date, 26-Dec-2020
        System.out.println(
            localDateOnNthDayOfWeekOfMonth(DayOfWeek.THURSDAY, 2, LocalDate.now())
        );
    }

    public static LocalDate localDateOnNthDayOfWeekOfMonth
    (
        final DayOfWeek dayOfWeek, 
        final int ordinal,
        final LocalDate startDate
    ) {
        return startDate.with(
            TemporalAdjusters.dayOfWeekInMonth(ordinal, dayOfWeek)
        );
    }
}

Output:

2020-12-10

Learn about Java-8 date-time API from Trail: Date Time.

Note for Java 6 or 7 users:

For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
0

Example how to detect 2nd Monday

val DateTime.is2ndMonday
    get() = dayOfWeek == 1 && dayOfMonth > 7
Vitalii Movchan
  • 630
  • 7
  • 4