Velocitip: Find the 𝑿-th 𝒀-day (e.g. 4th Wednesday) of this month

Your developer community meets on the 4th Wednesday of each month. You want to send monthly invites from Marketo without having to hard-code anything. Earlier, we learned how to find the first 𝒀-day of next month, and this is just different enough to get its own post.

As always, start with the standard datetime includes in Velocity:

#set( $defaultTimeZone = $date.getTimeZone().getTimeZone("America/New_York") )
#set( $defaultLocale = $date.getLocale() )
#set( $calNow = $date.getCalendar() )
#set( $ret = $calNow.setTimeZone($defaultTimeZone) )
#set( $calConst = $$calNow) )
#set( $ISO8601DateOnly = "yyyy-MM-dd" )
#set( $ISO8601DateTime = "yyyy-MM-dd'T'HH:mm:ss" )
#set( $ISO8601DateTimeWithSpace = "yyyy-MM-dd HH:mm:ss" )
#set( $ISO8601DateTimeWithMillisUTC = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" )
#set( $ISO8601DateTimeWithMillisTZ = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" )

Then add your campaign-specific settings, i.e. 4th Wednesday is $nth = 4, $day = "WEDNESDAY":

#set( $nth = 4 )
#set( $day = "WEDNESDAY" )

($day must be in all-caps English, since that’s how Java’s Calendar constants are stored.)

Next the hard/weird stuff:

#set( $dayConst = $calConst.get($day) )
#if( $dayConst < 7  ) 
#set( $nextDayConst = $math.add($dayConst,1) )
#set( $nextDayConst = 1 )
#set( $thisMonth = $date.getCalendar() )
#set( $ret = $thisMonth.setTimeZone($defaultTimeZone) )
#set( $ret = $thisMonth.set($calConst.DAY_OF_MONTH, 1) )
#set( $ret = $thisMonth.setFirstDayOfWeek($nextDayConst) ) ## the day *after* Y-day
#set( $ret = $thisMonth.set($calConst.DAY_OF_WEEK, $dayConst) ) ## Y-day itself
#set( $ret = $thisMonth.add($calConst.WEEK_OF_MONTH, $math.sub($nth,1) ) )

Now output $thisMonth any way you want:

The ${nth}th/nd ${display.capitalize($day.toLowerCase())}
of ${date.format("MMMM yyyy", $thisMonth, $defaultLocale, $defaultTimeZone)}
is ${date.format("MM/dd/yyyy", $thisMonth, $defaultLocale, $defaultTimeZone)}
The 4th/nd Wednesday
of May 2024
is 05/22/2024

Brief code explainer

At the risk of making your head spin:

  1. Find the next day of the week after day 𝑿, using Java’s 1-based week where Sunday = 1, Monday = 2, etc.
  2. Set the current day to 1st day of the month, which is naturally in the 1st week of the month as well.
  3. Shift the calendar (also done in the previous post) so weeks start on the next day from step (1). This ensures we’re in a “World of Wednesdays” if you will.
  4. Set the current day of the the week to day 𝑿.
  5. Jump forward 𝒀-1 weeks. Since we’re already in week 1 of the month, we don’t need to jump the full 𝒀.

Easy, right? 😛