Weekdays of future passed

Some time ago, I wrote a little shell script called next, which used the date command to output the date some number of weekdays after the current date. So if I ran it today (Thursday, May 2, 2024) with this invocation,

next tue

it would return


which is the Tuesday of next week. If I ran it with this invocation,

next tue 2

it would return


which is the Tuesday after that, i.e., two Tuesdays after today.

This worked out pretty well, except that I had to run it in the Terminal1 and then copy the output if I wanted it to go into some document. The switching to the Terminal and back made next a lot less convenient than it should have been.

So I rewrote it as a Keyboard Maestro macro that I can run from anywhere and get the output onto the clipboard immediately. I call the macro Next Weekday, and you can download it (you may need to right-click on that link).

When Next Weekday is invoked, typically by ⌃⌘W, this window appears:

Next Weekday input window

You choose the day of the week from the popup menu (the default is Monday) and enter the number of those weekdays after today in the text field (the default is 1).

Assuming I choose two Tuesdays from today, as in the second next example above, the output is this:

Next Weekday output window

As you can see, it tells me what the date is and gives me the option to put it on the clipboard in either of two formats:

Or I can just Cancel the macro if all I want is to see the date.

Because Next Weekday has more options than next, it’s more complicated, but it still uses date to do most of the work. Here’s the code:

KM Next Weekday macro

The key is the shell script in the second step:

weeks=$(( $KMVAR_Local_Number - 1 ))
date -v+${weeks}w -v+1d -v+$KMVAR_Local_Chosen_Weekday '+%Y-%m-%d'

The first line subtracts 1 from the number of weekdays the user asks for and puts that into the variable $weeks. The second line does the work. The first two -v options tell date to move forward $weeks weeks and one day. The third -v option tells it to move to the weekday chosen by the user. The final argument is the output format.

If I asked for the second Tuesday after today, as in the example, date would be called like this:

date -v+1w -v+1d -v+Tuesday '+%Y-%m-%d'

which returns 2024-05-14. The -v option is pretty complicated, and I frankly don’t trust myself to give you a complete explanation of how it works. I suggest you RTFM and play around with it, possibly using the command above as your starting point.

OK, so that’s where we get the yyyy-mm-dd form of the date. It’s stored in the Local Next Weekday variable. The next shell script converts it to the long format through

date -jf '%Y-%m-%d' $KMVAR_Local_Next_Weekday +'%B %-e, %Y'

The -jf combination of options is the common way to convert dates from one format to another. The -f part has an argument that specifies the input format, and the -j part says that the input date should not be used to set the computer’s date and time.

Yes, date can be used to set the date, which sounds insane, but only the superuser can do that. Option letters usually have some relationship to what they do, but -j has always seemed to me like a weird choice to turn off the date-setting feature. I’ve decided it stands for Jesus, don’t try to change the date!

The third script in the macro is a Python script to turn the cardinal number (1, 2, 3, etc.) stored in Local Number into the ordinal number (1st, 2nd, 3rd, etc.) that’s used in the prompt of output window. This is an unnecessary bit of flair that I added to the macro because I already had the code to do it.

#!/usr/bin/env python

import os

n = int(os.environ['KMVAR_Local_Number'])

s = ('th', 'st', 'nd', 'rd') + ('th',)*10
v = n%100
if v > 13:

The last two steps of the macro are pretty straightforward. They display the output window and handle whatever button the user presses.

One last thing. If you think I misspelled “past” in the title of this post, you need to clear the X-Men out of your head. There’s a reason the examples get the dates of Tuesday afternoons.

  1. The terminal app I use is iTerm, but you know what I mean.