public/Format-AsISO8601Duration.ps1

Function Format-AsISO8601Duration {

<#
    .SYNOPSIS
    Formats a time duration to ISO-8601 format
     
    .DESCRIPTION
    Formats a time duration to ISO-8601 format as described in section 'Durations' at https://en.wikipedia.org/wiki/ISO_8601.
 
    All parameters for date and time elements may have a decimal fraction eg. 0.5 years to indicate half a year.
 
    When using the Optimize parameter to prevent each date and time element to exceed their value, the following definitions are used:
    1 year = 12 months
    1 Month = 30 days
    1 day = 24 hours
    1 hour = 60 minutes
    1 minute = 60 seconds
 
 
    .EXAMPLE
    Format-AsISO8601Duration -Years 2 -Months 11 -Days 29 -Hours 23 -Minutes 59 -Seconds 61 -Optimize
    Format-AsISO8601Duration -Seconds 30
 
    .PARAMETER Years
    The numbers of years in the duration.
 
    .PARAMETER Months
    The numbers of months in the duration.
     
    .PARAMETER Days
    The numbers of days in the duration.
     
    .PARAMETER Hours
    The numbers of hours in the duration.
     
    .PARAMETER Minutes
    The numbers of minutes in the duration.
     
    .PARAMETER Seconds
    The numbers of seconds in the duration.
 
    .PARAMETER Optimize
    Prevents date and time values in a duration representation from exceeding their "carry over points" eg. transforming 65 seconds to 1 minute and 5 seconds.
 
 
    .INPUTS
    TO DO
     
    .OUTPUTS
    The duration formatted according to ISO-8601 as in P[n]Y[n]M[n]DT[n]H[n]M[n]S, where n represents the individual date and time elements.
     
    .LINK
    https://github.com/DennisWagner/SQLServerDevOpsTools
     
    .NOTES
    Written by (c) Dennis Wagner Kristensen, 2021 https://github.com/DennisWagner/SQLServerDevOpsTools
    This PowerShell script is released under the MIT license http://www.opensource.org/licenses/MIT
#>

    [CmdletBinding()]
    Param (
                [Parameter(Mandatory=$false)][decimal]$Years,
                [Parameter(Mandatory=$false)][decimal]$Months,
                [Parameter(Mandatory=$false)][decimal]$Days,
                [Parameter(Mandatory=$false)][decimal]$Hours,
                [Parameter(Mandatory=$false)][decimal]$Minutes,
                [Parameter(Mandatory=$false)][decimal]$Seconds,
                [Parameter(Mandatory=$false)][switch]$Optimize
    )
    BEGIN {
        If (-not $Years -and -not $Months -and -not $Days -and -not $Hours -and -not $Minutes -and -not $Seconds) {
            Throw "At least one date/time parameter must be supplied."
        }
        If ($Years -lt 0) {
            Throw "Parameter Years cannot be a negative number."
        }
        If ($Months -lt 0) {
            Throw "Parameter Months cannot be a negative number."
        }
        If ($Days -lt 0) {
            Throw "Parameter Days cannot be a negative number."
        }
        If ($Hours -lt 0) {
            Throw "Parameter Hours cannot be a negative number."
        }
        If ($Minutes -lt 0) {
            Throw "Parameter Minutes cannot be a negative number."
        }
        If ($Seconds -lt 0) {
            Throw "Parameter Seconds cannot be a negative number."
        }
    }

    PROCESS {

        $YearsOptimized = $Years
        $MonthsOptimized = $Months
        $DaysOptimized = $Days
        $HoursOptimized = $Hours
        $MinutesOptimized = $Minutes
        $SecondsOptimized = $Seconds

        If ($Optimize) {
        
            If ($SecondsOptimized -ge 60) {
                $MinutesOptimized += [Math]::Truncate($SecondsOptimized / 60)
                $SecondsOptimized %= 60
            }
            If ($MinutesOptimized -ge 60) {
                $HoursOptimized += [Math]::Truncate($MinutesOptimized / 60)
                $MinutesOptimized %= 60
            }
            If ($HoursOptimized -ge 24) {
                $DaysOptimized += [Math]::Truncate($HoursOptimized / 24)
                $HoursOptimized %= 24
            }
            If ($DaysOptimized -ge 30) {
                $MonthsOptimized += [Math]::Truncate($DaysOptimized / 30)
                $DaysOptimized %= 30
            }
            If ($MonthsOptimized -ge 12) {
                $YearsOptimized += [Math]::Truncate($MonthsOptimized / 12)
                $MonthsOptimized %= 12
            }
        }

        $ISO8601Str = "P"

        If ($YearsOptimized -gt 0) {
            $ISO8601Str += "$($YearsOptimized)Y"
        }
        If ($MonthsOptimized -gt 0) {
            $ISO8601Str += "$($MonthsOptimized)M"
        }
        If ($DaysOptimized -gt 0) {
            $ISO8601Str += "$($DaysOptimized)D"
        }
        If ($HoursOptimized -gt 0 -or $MinutesOptimized -gt 0 -or $SecondsOptimized -gt 0) {
            $ISO8601Str += "T"
            If ($HoursOptimized -gt 0) {
                $ISO8601Str += "$($HoursOptimized)H"
            }
            If ($MinutesOptimized -gt 0) {
                $ISO8601Str += "$($MinutesOptimized)M"
            }
            If ($SecondsOptimized -gt 0) {
                $ISO8601Str += "$($SecondsOptimized)S"
            }
        }
    }
    END {
        Write-Output $ISO8601Str
    }
}