Velocitip: Find the first 𝒀-day (e.g. first Friday) of next month

You want to print the date of the first Monday or first Friday — let’s call it “first 𝒀-day” — of the next month. That’s when you host a recurring event.

Possible? 100%. Without a bunch of Velocity under your belt? To be frank, 0%.

As always, start with the standard includes from the seminal post on days and times 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 = $field.in($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" )

Now it’s time for the fun stuff. In terms of lines of code, it’s short. But it’s long on possible confusion. Even I had to shake my head clear-out-the-cobwebs style to remember why it works!

#set( $nextMonth = $date.getCalendar() )
#set( $ret = $nextMonth.setTimeZone($defaultTimeZone) )
#set( $ret = $nextMonth.add($calConst.MONTH, 1) )
#set( $ret = $nextMonth.set($calConst.DAY_OF_MONTH, 1) )
#set( $ret = $nextMonth.setFirstDayOfWeek($calConst.TUESDAY) ) ## the day *after* N-day
#set( $ret = $nextMonth.set($calConst.DAY_OF_WEEK, $calConst.MONDAY) ) ## N-day itself
The first Monday of next month is: ${date.format("MM/dd/yyyy", $nextMonth, $defaultLocale, $defaultTimeZone)}

So we start by making a new Calendar object, based on the current timestamp, in your sending timezone instead of the underlying server timezone. (Yes, it starts out the same as $calNow, but since we’ll be mutating it it needs its own variable.)

Next are a couple of straightforward mutations:

  • jump the month forward by 1
  • set to the first of the month (regardless of the day of the week)

Then it gets admittedly weird.

The key is that with setFirstDayOfWeek() we’re creating a kind of “shifted calendar” where every week ends on our target day, in this case Monday. (If the first day of the week is Tuesday, by definition the last day of the week is Monday, right?) There’s no setLastDayOfWeek() but we get the same outcome.

Now we can safely set the day of the week to Monday and it will stay within the first Monday-anchored week. You need the shifted calendar because of how Java — since we’re in Java-land here, not Velocity — rolls the calendar when you set(DAY_OF_WEEK, ...).

The above may not make sense at first (or even fifth) pass, but play around and you’ll eventually figure it out. If not, use it anyway!