I know for a fact that I've done this the caveperson way. And I'd like to learn how to do it better or differently. Would I would like to do is choose a starting month and a markdown header level and then have the clipboard populate with LEVEL STARTMONTH.
I chose a 12-nested if statement (!). Are there better alternatives? Date math? A different action?
Ultiamtely, I'd like to ALSO choose the number of months. Example: start in october, go three months outputs:
You can use an array variable to hold the months, and then a Repeat loop to build the string. Here's one such solution that seems to work, though I only very lightly tested it.
Macros are always disabled when imported into the Keyboard Maestro Editor.
The user must ensure the macro is enabled.
The user must also ensure the macro's parent macro-group is enabled.
System information
macOS 14.4.1
Keyboard Maestro v11.0.2
It allows you to enter the number of months, and I assumed if you chose a value that wrapped around a year-end, you'd want to start over with January again. Run it and it looks like this:
I like @griffman's solution. But, for fun, here's one that uses "date math" to both build a list for the prompt and to output the markdown months.
Possible advantages -- it should localise to the user's language and it's easy to change the "display" format in both prompt and final output (eg change MMMM in the %ICUDateTimeFor% token to MMM for short-form month names).
Should also be tidied to stop the repeated "seconds to add to get to the next month" calculations...
FWIW some elements for doing this kind of thing in an Execute JavaScript for Automation action, perhaps in a subroutine.
This kind of thing can generate month names (long and short form) in whichever locale the system is set in, or in a particular language if the two-letter locale code is given explicitly.
Expand disclosure triangle to view JS source
(() => {
"use strict";
const kmvar = {
"local_Two_letter_Locale_Code": "", // e.g. "en", "fr", "de", "it"
"local_Start_Month_Number": 4,
"local_Number_of_months": 9,
"local_Markdown_Heading_Level": 2,
"local_Month_Names_Abbreviated": "" // 1 if true
};
const main = () =>
monthNameSequence(
kmvar.local_Two_letter_Locale_Code
)(
kmvar.local_Start_Month_Number
)(
kmvar.local_Number_of_months
)(
Boolean(kmvar.local_Month_Names_Abbreviated)
)
.map(
asMarkdownHeading(
kmvar.local_Markdown_Heading_Level
)
)
.join("\n");
// monthNameSequence :: String -> Int ->
// Int -> Bool -> [String]
const monthNameSequence = localeName =>
// localeName can be a two-letter string
// If the string is empty or undefined,
// the system locale is used by default.
firstMonthN => monthCount => abbreviated =>
Array.from(
{length: monthCount},
(_, i) => firstMonthN + i
)
.map(iMonth => new Date(0, iMonth - 1)
.toLocaleString(
localeName || undefined,
{
month: abbreviated
? "short"
: "long"
}
)
);
// asMarkdownHeading :: Int -> String -> String
const asMarkdownHeading = n =>
// The given string as Markdown Heading level n
s => `${"#".repeat(n)} ${s}`;
return main();
})();
Do note that mine, as written, will break around the "36 months in advance month" because it only works in 30-day "months". You could make it more resilient by using an offset of 2,629,746 each loop (seconds in a year / 12) but really, unless you are frequently changing language or format, stick with @griffman's version!