Skip to content

Instantly share code, notes, and snippets.

@indented-automation
Last active August 24, 2022 09:12
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save indented-automation/a4f187a73835fb7c64f34b847b022672 to your computer and use it in GitHub Desktop.
Save indented-automation/a4f187a73835fb7c64f34b847b022672 to your computer and use it in GitHub Desktop.
Gets the next date
function Get-TaskStartDate {
<#
.SYNOPSIS
Get a start date from a string expression.
.DESCRIPTION
Finds the start date from a string expression.
.EXAMPLE
Get-TaskStartDate -Day '1st Monday'
Gets the next occurrence of the first monday of a month.
.EXAMPLE
Get-TaskStartDate -Day Tuesday
Gets the next tuesday in the month.
.EXAMPLE
Get-TaskStartDate -Day '2nd tuesday' -Offset 2
Gets a date two days after the 2nd tuesday of the month.
.EXAMPLE
Get-TaskStartDate -Day 'last fri'
Gets the last friday of the month.
#>
[CmdletBinding()]
param (
# The Day may be an expression, such as 3rd Wednesday, or a day of the week such as Tuesday.
[Parameter(ValueFromPipeline)]
[string]$Day,
# Apply an offset (in days) to the start date.
[int]$Offset,
# Use a specific culture to parse the string.
[System.Globalization.CultureInfo]$Culture = (Get-Culture)
)
begin {
function Get-DayInMonth {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[DayOfWeek]$Day,
# The starting point.
[DateTime]$Date = (Get-Date -Day 1),
# Week number
[int]$Week = 1,
# Apply an offset (in days) to the start date.
[int]$Offset,
[switch]$Backwards
)
while ($Date.DayOfWeek -ne $Day) {
$Date = $Date.AddDays(1 * (1, -1)[$Backwards.IsPresent])
}
$Date.AddDays(7 * ($Week - 1) + $Offset)
}
function Convert-DayName {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]$Day,
[Parameter(Mandatory)]
[string[]]$DayNames,
[switch]$Try
)
$Day = $Day.ToLower()
$index = $DayNames.IndexOf($Day)
if ($index -eq -1) {
if ($Try) {
return
} else {
throw 'Unexpected day format'
}
}
if ($index -ge 14) {
$index -= 14
} elseif ($index -ge 7) {
$index -= 7
}
$index -as [DayOfWeek]
}
$today = [DateTime]::Today
$offSetParam = @{
Offset = $Offset
}
$dateFormat = $Culture.DateTimeFormat
$dayNames = @(
$dateFormat.DayNames
$dateFormat.AbbreviatedDayNames
$dateFormat.ShortestDayNames
) | ForEach-Object ToLower
}
process {
try {
$convertedDay = Convert-DayName -Day $Day -DayNames $dayNames -Try
switch -regex ($Day) {
{ $convertedDay -as [string] } {
$start = $today
do {
$date = Get-DayInMonth -Day $convertedDay -Date $start @offSetParam
$start = $start.AddDays(1)
} until ($date -ge $today)
break
}
'^\d+$' {
$date = Get-Date -Day $Day
if ($date -lt $today) {
$date = $date.AddMonths(1)
}
break
}
'^(?<week>\d+)\S* +(?<day>[a-z]+)$' {
$matches['day'] = Convert-DayName -Day $matches['day'] -DayNames $dayNames
$matches.Remove(0)
$date = Get-DayInMonth @matches -Offset $Offset
if ($date -lt $today) {
$date = Get-DayInMonth @matches -Date (Get-Date -Day 1).AddMonths(1) @offSetParam
}
break
}
'(?<modifier>\w+) +(?<day>[a-z]+)$' {
switch ($matches['modifier']) {
'first' { Get-TaskStartDate ("1st {0}" -f $matches['day']) }
'second' { Get-TaskStartDate ("2nd {0}" -f $matches['day']) }
'third' { Get-TaskStartDate ("3rd {0}" -f $matches['day']) }
'forth' { Get-TaskStartDate ("4th {0}" -f $matches['day']) }
'last' {
$startDate = (Get-Date -Day 1).AddMonths(1).AddDays(-1)
$date = Get-DayInMonth -Day $matches['day'] -Date $startDate -Backwards @offSetParam
if ($date -lt $today) {
$date = Get-DayInMonth -Day $matches['day'] -Date $startDate.AddMonths(1) @offSetParam
}
}
}
}
default {
throw 'Invalid date expression'
}
}
$date.Date
} catch {
Write-Error -ErrorRecord $_
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment