Functions/Public/Format-TimeSpan.ps1

function Format-TimeSpan {
    <#
    .SYNOPSIS
        Formats a TimeSpan object into a human-readable string.

    .DESCRIPTION
        The Format-TimeSpan function converts a TimeSpan object into a readable format
        such as "2 min 30 sec", "1 hr 15 min", or "2 days 3 hr". Automatically adjusts
        the output based on the duration.

    .PARAMETER TimeSpan
        The TimeSpan object to format.

    .PARAMETER Precision
        Controls how many time units to include.
        - 'Auto' (default): Shows the two most significant non-zero units
        - 'Full': Shows all non-zero units
        - 'Short': Shows only the most significant unit

    .PARAMETER Compact
        If specified, uses abbreviated format (e.g., "2m 30s" instead of "2 min 30 sec").

    .INPUTS
        System.TimeSpan. You can pipe TimeSpan objects to Format-TimeSpan.

    .OUTPUTS
        System.String. Returns a formatted time string.

    .EXAMPLE
        Format-TimeSpan -TimeSpan ([TimeSpan]::FromMinutes(90))

        Returns: "1 hr 30 min"

    .EXAMPLE
        [TimeSpan]::FromSeconds(150) | Format-TimeSpan

        Returns: "2 min 30 sec"

    .EXAMPLE
        Format-TimeSpan -TimeSpan ([TimeSpan]::FromDays(1.5)) -Compact

        Returns: "1d 12h"

    .EXAMPLE
        $stopwatch.Elapsed | Format-TimeSpan -Precision Full

        Returns all non-zero time components.

    .NOTES
        Author: Sune Alexandersen Narud
        Version: 1.0.0
        Date: February 2026
    #>


    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [TimeSpan]$TimeSpan,

        [Parameter()]
        [ValidateSet('Auto', 'Full', 'Short')]
        [string]$Precision = 'Auto',

        [Parameter()]
        [switch]$Compact
    )

    process {
        # Define unit labels
        if ($Compact) {
            $units = @{
                Days    = 'd'
                Hours   = 'h'
                Minutes = 'm'
                Seconds = 's'
            }
            $separator = ' '
        }
        else {
            $units = @{
                Days    = ' day', ' days'
                Hours   = ' hr'
                Minutes = ' min'
                Seconds = ' sec'
            }
            $separator = ' '
        }

        # Build components array
        $components = @()

        if ($TimeSpan.Days -gt 0) {
            if ($Compact) {
                $components += "$($TimeSpan.Days)$($units.Days)"
            }
            else {
                $label = if ($TimeSpan.Days -eq 1) { $units.Days[0] } else { $units.Days[1] }
                $components += "$($TimeSpan.Days)$label"
            }
        }

        if ($TimeSpan.Hours -gt 0) {
            $components += "$($TimeSpan.Hours)$($units.Hours)"
        }

        if ($TimeSpan.Minutes -gt 0) {
            $components += "$($TimeSpan.Minutes)$($units.Minutes)"
        }

        if ($TimeSpan.Seconds -gt 0 -or $components.Count -eq 0) {
            $components += "$($TimeSpan.Seconds)$($units.Seconds)"
        }

        # Apply precision
        switch ($Precision) {
            'Short' {
                return $components[0]
            }
            'Full' {
                return $components -join $separator
            }
            'Auto' {
                if ($components.Count -le 2) {
                    return $components -join $separator
                }
                return ($components | Select-Object -First 2) -join $separator
            }
        }
    }
}