Set a Reminder for the 1st or Last Weekday of Each Month

To add to the various options already given, another technique you could use also employs the cron trigger, which gets set to an initial value that would represent the first weekday of next month, and triggers every minute between the hours of 8am and midday.

So, at the time of writing, I would set the cron trigger value to "* 08-12 2 9 * 2019", which represents 8-12:* on day 2 in Sep in 2019 (which is a Monday).

This solves the problem of having your computer sleeping and missing a cron trigger that is set to a range of times that are too narrow (e.g. 8-9am, which won't trigger if you wake your computer at 9.30am); or too infrequent (e.g. on the hour, or every half hour, which also risks missing an alert).

Setting it to trigger every minute over a four-hour time span means you and your computer only need to be awake for, at most, 60 seconds any time in the morning after 8am. When the macro is triggered, the first action will calculate the first weekday of the following month, and programmatically assign a new value to the cron trigger. Therefore, the macro will only ever trigger once per month, and on the precise day that you need it to.

Here's an AppleScript:

use application id "com.apple.systemevents"
use application id "com.stairways.keyboardmaestro.editor"
use scripting additions

property text item delimiters : space

-- Set the date to the 1st of the month
tell the (current date) to set [¬
	firstWeekdayOfTheMonth, ¬
	day, its month, time] to [¬
	it, 1, (its month) + 1, 0]

tell the firstWeekdayOfTheMonth
	-- If the 1st of the month is a weekend,
	-- then shift the date forward to Monday
	if its weekday is in [Saturday, Sunday] then ¬
		set the day to day + (its weekday) mod 5
	
	-- Day, month, year numerical values
	set [d, m, y] to [day, its month as integer, year]
end tell

-- The Keyboard Maestro bits
set cronValue to {"*", "08-12", d, m, "*", y} as text
set plist to {Cron:cronValue, MacroTriggerType:"Cron"}
make new property list item with properties {value:plist}
set the xml of trigger 1 of macro "__TEMP__" to the result's text

and it would be the first action in the macro:

The only edit you need to make to the script is to change the reference to the macro on the last line:

set the xml of trigger 1 of macro "__TEMP__" to the result's text

My temporary macro is called "__TEMP__", but you would name yours whatever you like, and adjust that line in the script accordingly (I actually prefer to reference macros by their UUID, which is unique and won't ever change in the lifetime of the macro. Thus, my final line would look like this:

set the xml of trigger 1 of macro id "2AE4C053-CA6D-4D2D-80C7-A1F72259FC09" to the result's text

But using the name is absolutely fine, as long as you remember to update it in your script if you ever change the name of your macro.)


The rest of the macro, which I haven't included, I imagine is very straightforward: show an alert; insert a pause for however long you wish; then close the alert.

I was playing around with displaying the alert using a Custom Floating HTML Prompt, which can be dismissed either by clicking the close button, or pressing the escape key. Here's what it looks like:

And you can nab my code below if you fancy using it:

<html>
	<head>
		<style>
			* {

			}

			body {
				margin: 0; padding: 0;
				font-family: Menlo, Hack, monospace;
				font-size: 13pt;
			}

			.container {
				position: absolute;
				display: block;
				margin: 0; padding: 0;
				left: 0; top: 0; right: 0;
				width: 100%;
			}

			.msgTitle {
				position: relative;
				display: block;
				margin: 0; padding: 0;
				left: 0; top: 0;
				font-size: 18pt;
				font-weight: 600;
				text-transform: capitalize;
			}

			.msgBody {
				position: relative;
				display: block;
				margin: 0; padding: 5px;
				margin-top: 1em;
				font-weight: 300;
				color: deeppink;
				text-align: justify;
			}

			.date {
				position: relative;
				display: block;
				margin: 0; padding: 0;
				font-family: PT Mono, monospace;
				font-size: 8pt;
				font-weight: 100;
				font-style: oblique;
				color: grey;
			}
		</style>
	</head>
	<body>
		<div class="container">
			<h2 class="msgTitle">Alert notification title</h2>
			<p class="date">current date & time</p>
			<div class="msgBody">
				I was playing around with displaying the alert using a <b><code>Custom Floating HTML Prompt</code></b>, which can be dismissed either by clicking the close button, or pressing the escape key.
			</div>
		</div>

		<script type="text/javascript">
			const _self = window.self;
			const date = new Date().toLocaleString();
			
			const msgTitle = document.querySelector('h2.msgTitle');
			const msgDate = document.querySelector('p.date');
			const msgBody = document.querySelector('div.msgBody');
			const container = document.querySelector('div.container');

			const km = window.KeyboardMaestro;
	
			container.addEventListener('keydown', (event) => {
				if (event.keyCode == 27 || event.keyCode == 10) {
					return closeMsg();
				}
			});

			function KMInit() {
				_self.status = msgTitle.innerText;
				return;
			}
	
			function KMDidShowWindow() {
				const yOffset = _self.outerHeight - _self.innerHeight;
				_self.resizeTo(400);
				_self.resizeTo(container.clientWidth, container.clientHeight+yOffset);
				_self.moveTo(0.5 * (Screen.width - _self.innerWidth),
					0.5 * (Screen.height - _self.innerHeight));
				document.body.style.width = _self.innerWidth;
				document.body.style.height = _self.innerHeight;
				msgDate.innerText = date;
				_self.status = msgTitle.innerText;
				document.title = date;
			}
	
			function KMWillCloseWindow() {
				return;
			}

			closeMsg = () => {
				_self.KeyboardMaestro.Cancel();
				return _self.close();
			}
		</script>
	<//body>
</html>