February 17, 2022 at 8:37 PM by Dr. Drang
The release of Keyboard Maestro 10, with its addition of subroutines, and a couple of recent questions on the Keyboard Maestro forum regarding date calculations got me thinking that I should make a subroutine that calculates how many days it will be until the next Monday, Tuesday, Wednesday, or whatever weekday. And while I’m at it, I should do the same for the most recent Monday, Tuesday, etc. The calculations could be done in the same subroutine, with the difference being that upcoming days return a positive number and previous days return a negative number.
There are two reasons I decided to have the subroutine return a number instead of a date:
- Most import, Keyboard Maestro doesn’t have a Date type. While I could have the subroutine return a date string, like “2022-02-17,” that would make it difficult to use in macros that want a date string in a different format.
Keyboard Maestro has a token,
%ICUDateTimePlus%that allows you to specify a date in the past or future and format it however you like. So, for example, if it’s Thursday (which it is as I type this) and I want the date of last Sunday in the format “13 Feb 2022,” I could call
%ICUDateTimePlus%-4%Days%d MMM yyyy%
So by having my subroutine return the number of days until or since the weekday of interest, I can plug that return value into the
%ICUDateTimePlus%token in the macro that calls the subroutine.
Date calculations of this type (when is the next/previous weekday?) are typically done using modulo arithmetic, which is basically division where you return only the remainder. Ideally, the language you’re programming in assigns the numbers 0 through 6 to the days of the week (which weekday is Day 0 doesn’t matter as long as you know what it is) and you add or subtract days mod 7 to get the weekday of interest.
Keyboard Maestro has a
DOW() function which, if given no argument, returns an integer representation of today’s day of the week. The return value ranges from 1 (Sunday) to 7 (Saturday) instead of 0 to 6, but we can work around that.
Let’s say the number of the target weekday is stored in the variable
LocalTargetWeekday. Then if we’re looking for the next target weekday, we do this calculation,
(LocalTargetWeekday - DOW() + 7) MOD 7
and if we’re looking for the previous target weekday, we do this
(LocalTargetWeekday - DOW() - 7) MOD 7
The important thing to know here is that Keyboard Maestro’s
MOD operator returns a positive number if the number before
MOD is positive and a negative number if the number before
MOD is negative. This is why I add 7 in the “next weekday” formula and subtract 7 in the “previous weekday” formula. By doing so, I force the value before
MOD to be positive or negative as needed. And adding or subtracting multiples of 7 doesn’t change the remainder.
Let’s show how the calculation works. If it’s Thursday (5) and I want to know how many days until the next Monday (2), the calculation is\[(2 - 5 + 7) \bmod 7 = 4 \bmod 7 = 4\]
The number of days until next Saturday (7) is\[(7 - 5 + 7) \bmod 7 = 9 \bmod 7 = 2\]
Similarly, to get the number of days until the previous Monday, it’s\[(2 - 5 - 7) \bmod 7 = -10 \bmod 7 = -3\]
And the number of days until the previous Saturday is\[(7 - 5 - 7) \bmod 7 = -5 \bmod 7 = -5\]
All of which are the answers we expect, including the signs.
There is still one ambiguity left. Suppose we want the number of days until the next Thursday. The formula gives\[(5 - 5 + 7) \bmod 7 = 7 \bmod 7 = 0\]
Is this what we want? Maybe, maybe not. I can certainly anticipate some situations in which you want next to mean strictly next, in which case the correct answer is 7, and others in which you want it to mean today or next, in which case 0 is correct. So the subroutine should handle both of those situations.
Here it is.
Instead of building the subroutine yourself, you can just download it.
As you can see, the subroutine is called with three arguments:
- The target weekday as a number using Keyboard Maestro’s scheme.
- Whether you’re looking forward (the default) or backward.
- Whether your definition of next or previous is strict (the default) or not.
As you can see, any word that starts with “b” will make the subroutine look for the previous weekday, and any word that starts with “n” will make the search not strict. Also, Keyboard Maestro’s way of matching strings with the “starts with” option is not case-sensitive, so you can use “B” and “N,” too.
The subroutine would be used in a macro like this:
I used to end a lot of my posts with music videos in which the lyrics matched up with something I wrote about. I should go back to doing that.