Example PowerShell code to generate paydays and major US holidays with or without full calendar. The Holiday function could be shorter and more elegant, but it works for my needs.
<#
Basic functions to get bi-weekly pay and major holidays.
May need alterations on Windows or Mac. I am on Linux.
Author: C. Nichols
#>
function Get-Paydays () {
param (
[int]$FirstPayMonth = 1,
[int]$FirstPayDay = 12,
[int]$Year = 2024
)
$WeekNum = @{
Sunday=0;
Monday=1;
Tuesday=2;
Wednesday=3;
Thursday=4;
Friday=5;
Saturday=6
}
if (-not($Year)) {
$CurrentYear = (Get-Date).Year
} else {
$CurrentYear = $Year
}
$FirstPayOfYear = Get-Date -Year $CurrentYear -Month $FirstPayMonth -Day $FirstPayDay
$EndOfYear = Get-Date -Year $CurrentYear -Month 12 -Day 31
$CurrentDate = $FirstPayOfYear
$Paydays = New-Object -TypeName "System.Collections.ArrayList"
While ($CurrentDate -lt $EndOfYear)
{
If ($CurrentDate.DayOfWeek -eq 'Friday') {
$MonthName = (Get-Culture).DateTimeFormat.GetMonthName($CurrentDate.Month)
$DayName = $CurrentDate.DayOfWeek.ToString()
$PayDate = [PsCustomObject]@{
MonthName=$MonthName;
Month=$CurrentDate.Month;
WeekIndex=$WeekNum[$DayName];
DayName=$DayName;
Day=$CurrentDate.Day;
Year=$CurrentYear;
Desc="payday"
}
$Paydays += $PayDate
}
$CurrentDate = $CurrentDate.AddDays(14)
}
return $Paydays
}
function Get-Holidays() {
PARAM (
[int]$Year=2024
)
$WeekNum = @{
Sunday=0;
Monday=1;
Tuesday=2;
Wednesday=3;
Thursday=4;
Friday=5;
Saturday=6
}
$FIXED_HOLIDAYS = @{12=@{25="Christmas Day"};
1=@{1="New Year's Day"};
6=@{19="Juneteenth"};
7=@{4="Independence Day"};
11=@{11="Veteran's Day"}
}
$HOLIDAYS = @{1=@{3="Martin Luther King, Jr.Birthday"}; # 3rd Monday in Jan.
2=@{3="President's Day"}; # 3rd Monday in Feb. (President's Day)
#5="memorial"; # Last Monday in May. Handled separately.
9=@{1="Labor Day"}; # 1st Monday in Sept.
10=@{2="Columbus Day"}; # 2nd Monday in Oct.
11=@{4="Thanksgiving"} # 4th Thursday in Nov.
}
$HolidayCalendar = @{}
$Calendar = @{}
$Rows = New-Object -TypeName "System.Collections.ArrayList"
if (-not($Year)) {
$Year = (Get-Date).Year
}
# Link month name to day names for year.
ForEach ($Month in 1..12) {
$DaysInMonth = [datetime]::DaysInMonth($Year,$Month)
ForEach ($Day in 1..$DaysInMonth) {
$DateObject = Get-Date -Year $Year -Month $Month -Day $Day
$DayName = $DateObject.DayOfWeek
if (-not($Calendar.ContainsKey($Month))) {
$Calendar[$Month] = @{}
}
$Calendar[$Month][$Day] = $DayName
}
}
# Get a increamental count of each weekday in month.
$MonthKeys = $Calendar.Keys
ForEach ($MonthKey in $MonthKeys | Sort-Object) {
$DayHash = $Calendar[$MonthKey]
$MonCount = 0
$MemArray = @()
$DayKeys = $DayHash.Keys
ForEach ($DayKey in $DayKeys | Sort-Object) {
$MonthDays = $DayHash[$DayKey]
$Holiday = ""
if ($FIXED_HOLIDAYS.ContainsKey($MonthKey)) {
$Holiday = $FIXED_HOLIDAYS[$MonthKey][$DayKey]
}
if ($MonthDays -eq "Monday") {
$MonCount += 1
if ($HOLIDAYS.ContainsKey($MonthKey)) {
if ($HOLIDAYS[$MonthKey].ContainsKey($MonCount)) {
$Holiday = $HOLIDAYS[$MonthKey][$MonCount]
}
}
}
if ($MonthKey -eq 5) {
if ($MonthDays -eq "Monday") {
$MemArray += $DayKey
}
}
if (-not($HolidayCalendar.ContainsKey($MonthKey))) {
$HolidayCalendar[$MonthKey] = @{}
}
if (-not($HolidayCalendar[$MonthKey].ContainsKey($DayKey))) {
$HolidayCalendar[$MonthKey][$DayKey] = ""
}
$HolidayCalendar[$MonthKey][$DayKey] = $Holiday
}
if ($MemArray[-1]) {
$Memday = $MemArray[-1]
$HolidayCalendar[5][$Memday] = "Memorial Day"
}
}
$MthKeys = $HolidayCalendar.Keys
ForEach ($mk in $MthKeys | Sort-Object) {
$MonthName = (Get-Culture).DateTimeFormat.GetMonthName($mk)
#Write-Host $MonthName
$DayKeys = $HolidayCalendar[$mk].Keys
ForEach ($dk in $DayKeys | Sort-Object) {
$DateObject = Get-Date -Year $Year -Month $mk -Day $dk
$DayName = $DateObject.DayOfWeek.ToString()
$HolidayDesc = $null
if ($HolidayCalendar[$mk][$dk]) {
$HolidayDesc = $HolidayCalendar[$mk][$dk]
}
if ($DayName -eq "Saturday") {
$DateObject = $DateObject.AddDays(-1)
$DayName = $DateObject.DayOfWeek.ToString()
}
if ($DayName -eq "Sunday") {
$DateObject = $DateObject.AddDays(1)
$DayName = $DateObject.DayOfWeek.ToString()
}
$Columns = [PsCustomObject]@{
MonthName=$MonthName;
Month=$mk;
WeekIndex=$WeekNum[$DayName];
DayName=$DayName;
Day=$dk;
Year=$Year;
Desc=$HolidayDesc
}
$Rows += $Columns
}
}
return $Rows
}
<# Main ::
Merge and offset clashes.
#>
$WeekNum = @{
Sunday=0;
Monday=1;
Tuesday=2;
Wednesday=3;
Thursday=4;
Friday=5;
Saturday=6
}
$MergedRows = @()
$DateHash = @{}
# Ge major holidays.
$MyHolidays = Get-Holidays #-Year 2026
$MyPaydays = Get-Paydays #-Year 2026 -FirstPayMonth 1 -FirstPayDay 9
ForEach ($p in $MyPaydays) {
$Pdate = Get-Date -Year $p.Year -Month $p.Month -Day $p.Day
if (-not($DateHash.ContainsKey($Pdate.Date))) {
$DateHash[$Pdate.Date] = $p
}
}
ForEach ($h in $MyHolidays) {
if ($h.Desc) {
$Hdate = Get-Date -Year $h.Year -Month $h.Month -Day $h.Day
if (-not($DateHash.ContainsKey($Hdate.Date))) {
$DateHash[$Hdate.Date] = $h
} else {
$Pdate = Get-Date -Year $DateHash[$Hdate.Date].Year -Month $DateHash[$Hdate.Date].Month -Day $DateHash[$Hdate.Date].Day
$Pdate = $Pdate.AddDays(-1)
$MonthName = (Get-Culture).DateTimeFormat.GetMonthName($Pdate.Month)
$DayName = $Pdate.DayOfWeek.ToString()
$Column = [PsCustomObject]@{
MonthName=$MonthName;
Month=$Pdate.Month;
WeekIndex=$WeekNum[$DayName];
DayName=$DayName;
Day=$Pdate.Day;
Year=$Pdate.Year;
Desc="payday"
}
$DateHash[$Hdate.Date] = $h
$DateHash[$Pdate.Date] = $Column
}
}
}
# create report.
$ReportRows = @()
$DateKeys = $DateHash.Keys
ForEach ($mdate in $DateKeys | Sort-Object) {
Write-Host $DateHash[$mdate]
$ReportRows += $DateHash[$mdate]
}
# Save as CSV.
#$ReportRows | Export-Csv -Path "~/PrCal.csv" -NoTypeInformation
<# OUTPUT
@{MonthName=January; Month=1; WeekIndex=1; DayName=Monday; Day=1; Year=2024; Desc=New Year's Day}
@{MonthName=January; Month=1; WeekIndex=5; DayName=Friday; Day=12; Year=2024; Desc=payday}
@{MonthName=January; Month=1; WeekIndex=1; DayName=Monday; Day=15; Year=2024; Desc=Martin Luther King, Jr.Birthday}
@{MonthName=January; Month=1; WeekIndex=5; DayName=Friday; Day=26; Year=2024; Desc=payday}
@{MonthName=February; Month=2; WeekIndex=5; DayName=Friday; Day=9; Year=2024; Desc=payday}
@{MonthName=February; Month=2; WeekIndex=1; DayName=Monday; Day=19; Year=2024; Desc=President's Day}
@{MonthName=February; Month=2; WeekIndex=5; DayName=Friday; Day=23; Year=2024; Desc=payday}
@{MonthName=March; Month=3; WeekIndex=5; DayName=Friday; Day=8; Year=2024; Desc=payday}
@{MonthName=March; Month=3; WeekIndex=5; DayName=Friday; Day=22; Year=2024; Desc=payday}
@{MonthName=April; Month=4; WeekIndex=5; DayName=Friday; Day=5; Year=2024; Desc=payday}
@{MonthName=April; Month=4; WeekIndex=5; DayName=Friday; Day=19; Year=2024; Desc=payday}
@{MonthName=May; Month=5; WeekIndex=5; DayName=Friday; Day=3; Year=2024; Desc=payday}
@{MonthName=May; Month=5; WeekIndex=5; DayName=Friday; Day=17; Year=2024; Desc=payday}
@{MonthName=May; Month=5; WeekIndex=1; DayName=Monday; Day=27; Year=2024; Desc=Memorial Day}
@{MonthName=May; Month=5; WeekIndex=5; DayName=Friday; Day=31; Year=2024; Desc=payday}
@{MonthName=June; Month=6; WeekIndex=5; DayName=Friday; Day=14; Year=2024; Desc=payday}
@{MonthName=June; Month=6; WeekIndex=3; DayName=Wednesday; Day=19; Year=2024; Desc=Juneteenth}
@{MonthName=June; Month=6; WeekIndex=5; DayName=Friday; Day=28; Year=2024; Desc=payday}
@{MonthName=July; Month=7; WeekIndex=4; DayName=Thursday; Day=4; Year=2024; Desc=Independence Day}
@{MonthName=July; Month=7; WeekIndex=5; DayName=Friday; Day=12; Year=2024; Desc=payday}
@{MonthName=July; Month=7; WeekIndex=5; DayName=Friday; Day=26; Year=2024; Desc=payday}
@{MonthName=August; Month=8; WeekIndex=5; DayName=Friday; Day=9; Year=2024; Desc=payday}
@{MonthName=August; Month=8; WeekIndex=5; DayName=Friday; Day=23; Year=2024; Desc=payday}
@{MonthName=September; Month=9; WeekIndex=1; DayName=Monday; Day=2; Year=2024; Desc=Labor Day}
@{MonthName=September; Month=9; WeekIndex=5; DayName=Friday; Day=6; Year=2024; Desc=payday}
@{MonthName=September; Month=9; WeekIndex=5; DayName=Friday; Day=20; Year=2024; Desc=payday}
@{MonthName=October; Month=10; WeekIndex=5; DayName=Friday; Day=4; Year=2024; Desc=payday}
@{MonthName=October; Month=10; WeekIndex=1; DayName=Monday; Day=14; Year=2024; Desc=Columbus Day}
@{MonthName=October; Month=10; WeekIndex=5; DayName=Friday; Day=18; Year=2024; Desc=payday}
@{MonthName=November; Month=11; WeekIndex=5; DayName=Friday; Day=1; Year=2024; Desc=payday}
@{MonthName=November; Month=11; WeekIndex=1; DayName=Monday; Day=11; Year=2024; Desc=Veteran's Day}
@{MonthName=November; Month=11; WeekIndex=5; DayName=Friday; Day=15; Year=2024; Desc=payday}
@{MonthName=November; Month=11; WeekIndex=1; DayName=Monday; Day=25; Year=2024; Desc=Thanksgiving}
@{MonthName=November; Month=11; WeekIndex=5; DayName=Friday; Day=29; Year=2024; Desc=payday}
@{MonthName=December; Month=12; WeekIndex=5; DayName=Friday; Day=13; Year=2024; Desc=payday}
@{MonthName=December; Month=12; WeekIndex=3; DayName=Wednesday; Day=25; Year=2024; Desc=Christmas Day}
@{MonthName=December; Month=12; WeekIndex=5; DayName=Friday; Day=27; Year=2024; Desc=payday}
#>