Public/Show-Calendar.ps1

Function Show-Calendar {
  <#
      .SYNOPSIS
          Display a small monthly calendar.

      .DESCRIPTION
          Displays the specified month in calendar view with the current date highlighed. An optional date can also be highlighed. The view can also be positioned in the console window.

      .PARAMETER Date
          The month and year to display in DateTime format. If not specified the current month and year is used.

      .PARAMETER Month
          The month to display, 1 - 12.

      .PARAMETER Year
          The year to display. Defaults to current year.

      .PARAMETER Rotate
          Rotate the calendar display, similar to ncal in Linux

      .PARAMETER HighlightDay
          Specifies one or more days to highlight

      .PARAMETER HighlightDate
          Specifies a particular date to highlight.

      .PARAMETER Position
          Specified the host coordinates to display the calendar. Does not work with ISE or VSCode.

      .EXAMPLE
          Show-Calendar
          Shows the current month and year.

      .EXAMPLE
          Show-Calendar -Date (Get-Date -Date '01-2000') -HighlightDate '2000-01-20'
          Shows the first month of 2000 and highlights the specific date of the 20th.

      .EXAMPLE
          Show-Calendar -Month 01 -Year 2000 -HighlightDay (11..14)
          Shows the first month of 2000 and highlights the days 11th, 12th, 13th and 14th.

      .EXAMPLE
          Show-Calendar -Position ([System.Management.Automation.Host.Coordinates]::New(20, 10))
          Shows the current month and year at cursor position 20 across, 10 down.

      .NOTES
          For additional information please see my GitHub wiki page
          Based originally on https://github.com/jdhitsolutions/PSCalendar
          Which was based on https://www.leeholmes.com/blog/2008/12/03/showing-calendars-in-your-oof-messages/

      .LINK
          https://github.com/My-Random-Thoughts
  #>

  [CmdletBinding(DefaultParameterSetName = 'dateTime')]
  Param(
    [Parameter(ParameterSetName = 'dateTime')]
    [datetime]$Date = (Get-Date -Day 1).Date,

    [Parameter(ParameterSetName = 'monthYear', Mandatory)]
    [ValidateRange(1, 12)]
    [int]$Month,

    [Parameter(ParameterSetName = 'monthYear')]
    [int]$Year = ((Get-Date).Year),

    [switch]$Rotate,

    [ValidateRange(1, 31)]
    [AllowEmptyString()]
    [int[]]$HighlightDay,

    [datetime[]]$HighlightDate,

    [System.Management.Automation.Host.Coordinates]$Position
  )

  Begin {
    $DisplayColour = @{
      Title     = [System.ConsoleColor]::Yellow    # January 2000
      DayOfWeek = [System.ConsoleColor]::White     # Mo Tu We Th Fr Sa Su
      Date      = [System.ConsoleColor]::Gray      # 1 2 3 4 ...
      Today     = [System.ConsoleColor]::Green     # 1
      Highlight = [System.ConsoleColor]::Cyan      # 1
    }

    If ($PSCmdlet.ParameterSetName -eq 'monthYear') {
      $Date = ((Get-Date -Day 1 -Month $Month -Year $Year).Date)
    }

    # Ensure we starting at the 1st of the month
    If ($Date.Day -ne 1) { $Date = $Date.AddDays( - ($Date.Day - 1)) }

    $currCulture = [System.Globalization.CultureInfo]::CurrentCulture
    [datetime]$currentDate = ((Get-Date).Date)
    [datetime]$calDay = $Date
    [string]  $title = $Date.ToString('MMMM yyyy')
    [string[]]$shortDayNames = (($currCulture.DateTimeFormat.ShortestDayNames) * 2)
    [int]     $firstDayOfWeek = $currCulture.DateTimeFormat.FirstDayOfWeek.value__
    [datetime]$endOfMonth = (Get-Date -Year $Date.Year -Month $Date.Month -Day $($currCulture.DateTimeFormat.Calendar.GetDaysInMonth($Date.Year, $Date.Month)))
    [object[]]$dayArray = @(@(), @(), @(), @(), @(), @(), @())
    [string]  $dayCounter = '01234560123456'

    For ($i = 0; $i -lt 7; $i++) {
      # Ensure day names are always two characters long
      [string[]]$dayHeader += $(($shortDayNames[$i + $firstDayOfWeek]).PadLeft(2).Substring(0, 2) + ' ')
    }

    # Pad start of month if required
    While ($calDay.DayOfWeek.value__ -ne $firstDayOfWeek) {
      $calDay = $calDay.AddDays(-1)
    }

    If ($Position) { $originalCursorPosition = $Host.UI.RawUI.CursorPosition }
    Else { $Position = $Host.UI.RawUI.CursorPosition }
  }

  Process {
    While ($calDay.Date -le $endOfMonth.Date) {
      [string]$cDay = $($calDay.Day)
      If (($calDay.Month -lt $Date.Month) -or (($calDay.Year -lt $Date.Year))) { $cDay = ' ' }
      $dayArray[($calDay.DayOfWeek.value__)] += $cDay
      $calDay = $calDay.AddDays(1)
    }

    $completeMonth = [pscustomobject]@{}
    For ($i = 0; $i -le 6; $i++) {
      Add-Member -InputObject $completeMonth `
        -MemberType NoteProperty `
        -Name "Day$($dayCounter.Substring($i + $firstDayOfWeek, 1))" `
        -Value $dayArray[$($dayCounter.Substring($i + $firstDayOfWeek, 1))]
    }

    [string[]]$dayOfWeek = $completeMonth.psObject.Properties.Name

    $host.UI.RawUI.CursorPosition = $Position
    Write-Host $titlePadding$title -ForegroundColor $DisplayColour.Title
    $Position.Y++

    $params = @{
      Position      = $Position
      dayHeader     = $dayHeader
      dayOfWeek     = $dayOfWeek
      completeMonth = $completeMonth
      HighlightDate = $HighlightDate
      Date          = $Date
      DisplayColour = $DisplayColour
    }

    If (-not $Rotate) { __internal_DisplayCalendar_Default @params }
    Else { __internal_DisplayCalendar_Rotated @params }

    If ($originalCursorPosition) { $host.UI.RawUI.CursorPosition = $originalCursorPosition }
  }

  End {
  }
}

Function __internal_GetHighlight {
  Param (
    $Value,
    $HighlightDay,
    $HighlightDate,
    $DisplayColour
  )

  $fgColour = $DisplayColour.Date
  If (-not $Value.Trim()) { Return $fgColour }

  # Is HIGHLIGHT
  If (($HighlightDate) -contains (Get-Date -Day $value -Month $Date.Month -Year $Date.Year).Date) {
    $fgColour = $DisplayColour.Highlight
  }

  If ($HighlightDay -contains $value) {
    $fgColour = $DisplayColour.Highlight
  }

  # Is TODAY
  If (($currentDate.Day -eq $value      ) -and
    ($currentDate.Month -eq $Date.Month) -and
    ($currentDate.Year -eq $Date.Year )) { $fgColour = $DisplayColour.Today }

  Return $fgColour
}

Function __internal_DisplayCalendar_Default {
  Param (
    $Position,
    $dayHeader,
    $dayOfWeek,
    $completeMonth,
    $HighlightDate,
    $Date,
    $DisplayColour
  )

  $host.UI.RawUI.CursorPosition = $Position
  Write-Host $($dayHeader -join '') -ForegroundColor $DisplayColour.DayOfWeek
  $Position.Y++

  For ($i = 0; $i -lt 6; $i++) {
    $host.UI.RawUI.CursorPosition = $Position
    For ($j = 0; $j -lt $dayOfWeek.Count; $j++ ) {
      [string]$value = ($completeMonth.$($dayOfWeek[$j])[$i])

      $fgColour = __internal_GetHighlight -Value $value -HighlightDay $HighlightDay -HighlightDate $HighlightDate -DisplayColour $DisplayColour
      Write-Host "$($value.PadLeft(2, ' ')) " -ForegroundColor $fgColour -NoNewline
    }

    $Position.Y++
    Write-Host ''
  }
}

Function __internal_DisplayCalendar_Rotated {

  Param (
    $Position,
    $dayHeader,
    $dayOfWeek,
    $completeMonth,
    $HighlightDate,
    $Date,
    $DisplayColour
  )

  For ($j = 0; $j -lt $dayOfWeek.Count; $j++) {
    $host.UI.RawUI.CursorPosition = $Position
    Write-Host $($dayHeader[$j]) -ForegroundColor $DisplayColour.DayOfWeek -NoNewline

    For ($i = 0; $i -lt 6; $i++) {
      [string]$value = ($completeMonth.$($dayOfWeek[$j])[$i])

      $fgColour = __internal_GetHighlight -Value $value -HighlightDay $HighlightDay -HighlightDate $HighlightDate -DisplayColour $DisplayColour
      Write-Host "$($value.PadLeft(2, ' ')) " -ForegroundColor $fgColour -NoNewline
    }

    $Position.Y++
    Write-Host ''
  }
}