Get the Nth Working Day of the Month
25 Jan 2020 · Comments: · Tags: PowerShell, DateTime, WindowsTaskScheduler, ScheduledTaskSummary
I recently needed to schedule a task with Windows Task Scheduler to run at midday on the 5th weekday (Mon-Fri) of every month in 2020. Windows Task Scheduler lacks the functionality to configure such a schedule, so I needed to create a separate trigger for each month. Rather than do this by hand, I wrote a function in PowerShell to calculate the Nth weekday of a given month and year.
Function: Get-NthWorkingDayOfMonth
The function I wrote can be found here on Github.
Usage
In the following example, the 10th working day in January 2020 is obtained:
(NB: By default a working week is implicitly Monday to Friday, this can be
customised using the -WorkingDaysOfWeek
parameter)
Depending on your use case you may wish to exclude certain days of the month
from being treated as working days (such as public holidays), this can be
achieved using the -DayOfMonthToExclude
parameter. In the following example,
the 1st day of the month is excluded as a working day:
For further examples and use instructions, see Get-NthWorkingDayOfMonth.
UPDATE 2020-09-13
In September 2020 I wrote a function Get-UKBankHoliday
to retrieve UK bank holiday dates. Here’s an example of how it can be used in
conjunction with Get-NthWorkingDayOfMonth
to obtain the 3rd working day in
April 2021 excluding UK bank holidays (in England and Wales) as working days:
Unobtainable Dates
If an Nth value is specified in combination with a month and year that cannot be satisfied then a terminating error is thrown, EG:
PS C:\Bitbucket> Get-NthWorkingDayOfMonth -Nth 25 -Month 1 -Year 2020 There isn't a 25th working day (Monday, Tuesday, Wednesday, Thursday, Friday) in January 2020. At C:\Bitbucket\Get-NthWorkingDayOfMonth.ps1:112 char:5 + Throw "There isn't a $($Nth)$($OrdinalIndicator) working day ($($ ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : OperationStopped: (There isn't a 2...n January 2020.:String) [], RuntimeException + FullyQualifiedErrorId : There isn't a 25th working day (Monday, Tuesday, Wednesday, Thursday, Friday) in January 2020. PS C:\Bitbucket>
Demonstration: My Use Case
As mentioned in the summary, I needed to schedule a task using Windows Task
Scheduler to run on the 5th weekday (Mon-Fri) of every month in 2020. I started
by using Get-NthWorkingDayOfMonth
to produce a list of dates. Obtaining the
5th working day of each month where a working week consists of the days Mon-Fri
is not an outlandish requirement, so I didn’t expect any errors to be returned
but best to be safe…
Once satisfied there was nothing awry with the collection of dates, my plan was to populate a scheduled task with a series of twelve date/time type triggers, one for each month. In my case, the scheduled task already existed, I just needed to replace the existing triggers with new date/times for 2020. I opted to do the following:
- Export the task to an XML file using the Windows Task Scheduler UI.
- Use
Get-NthWorkingDayOfMonth
to produce a series of date values formatted appropriately for use in the XML file:
- Paste the values into the XML file (removing any existing values).
- Import the updated file with the
schtasks.exe
command line tool:
An alternative (and in hindsight simpler) approach would have been to use
PowerShell’s New-ScheduledTaskTrigger
cmdlet to replace the existing triggers
with twelve new triggers:
Finally, to ensure that the scheduled task had been correctly populated with the triggers, I used the following:
Further Reading
Working on this task lead me to dabble with various scheduling tools and specifications to find out how they handled different conditions. These are just some of my notes pertaining to this research.
iCalendar
In my function Get-NthWorkingDayOfMonth
I took the decision to throw a
terminating error if an Nth value is specified in combination with a month and
year that cannot be satisfied. I was curious to see how this problem is handled
by the iCalendar specification.
The specification’s latest (as at Jan 2020) RFC (5545) acknowledges this issue as follows (excerpt from page 43):
Microsoft Outlook
The Outlook 2016 UI prevents a user from scheduling a recurrent event on a specific Nth weekday (Mon-Fri) of every month beyond the fourth:
I wondered if you could circumvent this limitation by scheduling a recurrent
event on the 4th weekday of every month, saving it to an iCalendar file (.ics),
editing the file in a text editor, replacing BYSETPOS=4
with BYSETPOS=5
and
importing the file. The answer is no, you can’t. When attempting to import the
file nothing happens, there’s no error message. I also tried by constructing a
minimal iCalendar file (opposed to the one produced by Outlook), this also
failed to import.
Comments