Functions/Public/Get-TimeElapsed.ps1

function Get-TimeElapsed {
    <#
    .SYNOPSIS
        Calculates and formats the time elapsed since a given date.

    .DESCRIPTION
        The Get-TimeElapsed function calculates the difference between a given date/time
        and the current time, returning it in various formats. Supports both DateTime objects
        and string inputs with configurable date formats.

    .PARAMETER DateTime
        The starting date/time to calculate from. Can be a DateTime object or a string.

    .PARAMETER DateFormat
        When DateTime is a string, specifies the format to parse it.
        Default is 'yyyy-MM-ddTHH:mm:ss' (ISO 8601).
        Common formats: 'MM/dd/yyyy HH:mm:ss', 'dd-MM-yyyy', 'yyyy-MM-dd'

    .PARAMETER OutputFormat
        Controls the output format:
        - 'Friendly' (default): Returns "2d 5h ago" or "3h ago"
        - 'Days': Returns just the number of days as an integer
        - 'Hours': Returns total hours as a decimal
        - 'TimeSpan': Returns the raw TimeSpan object
        - 'Verbose': Returns "2 days, 5 hours, 30 minutes ago"

    .INPUTS
        System.DateTime or System.String. You can pipe dates to Get-TimeElapsed.

    .OUTPUTS
        Varies based on OutputFormat parameter.

    .EXAMPLE
        Get-TimeElapsed -DateTime (Get-Date).AddDays(-5)

        Returns: "5d 0h ago"

    .EXAMPLE
        Get-TimeElapsed -DateTime "01/15/2026 10:30:00" -DateFormat "MM/dd/yyyy HH:mm:ss"

        Parses the string date and returns elapsed time.

    .EXAMPLE
        Get-TimeElapsed -DateTime $lastLogin -OutputFormat Days

        Returns the number of days since last login as an integer.

    .EXAMPLE
        "2026-01-01T00:00:00" | Get-TimeElapsed -OutputFormat Verbose

        Returns: "35 days, 14 hours, 22 minutes ago"

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

        This function replaces the separate Get-DaysSince and Get-TimeSince functions
        by combining their functionality with additional output options.

    .LINK
        Format-TimeSpan
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        $DateTime,

        [Parameter()]
        [string]$DateFormat = 'yyyy-MM-ddTHH:mm:ss',

        [Parameter()]
        [ValidateSet('Friendly', 'Days', 'Hours', 'TimeSpan', 'Verbose')]
        [string]$OutputFormat = 'Friendly'
    )

    process {
        # Parse the input to DateTime
        $parsedDate = $null

        if ($DateTime -is [DateTime]) {
            $parsedDate = $DateTime
        }
        elseif ($DateTime -is [string]) {
            # Try multiple common formats
            $formats = @(
                $DateFormat,
                'yyyy-MM-ddTHH:mm:ss',
                'yyyy-MM-ddTHH:mm:ssZ',
                'MM/dd/yyyy HH:mm:ss',
                'dd/MM/yyyy HH:mm:ss',
                'yyyy-MM-dd HH:mm:ss',
                'yyyy-MM-dd'
            )

            foreach ($fmt in $formats) {
                try {
                    $parsedDate = [DateTime]::ParseExact($DateTime, $fmt, [System.Globalization.CultureInfo]::InvariantCulture)
                    break
                }
                catch {
                    continue
                }
            }

            if ($null -eq $parsedDate) {
                # Last resort: try Parse
                try {
                    $parsedDate = [DateTime]::Parse($DateTime)
                }
                catch {
                    throw "Unable to parse date string '$DateTime'. Please specify the format using -DateFormat parameter."
                }
            }
        }
        else {
            throw "DateTime parameter must be a DateTime object or a string. Received: $($DateTime.GetType().Name)"
        }

        # Calculate the time difference
        $now = Get-Date
        $timeDifference = $now - $parsedDate

        # Handle negative timespan (future dates)
        $isFuture = $timeDifference.TotalSeconds -lt 0
        if ($isFuture) {
            $timeDifference = $parsedDate - $now
        }

        # Format output based on requested format
        switch ($OutputFormat) {
            'Days' {
                return [int]$timeDifference.Days
            }

            'Hours' {
                return [math]::Round($timeDifference.TotalHours, 2)
            }

            'TimeSpan' {
                return $timeDifference
            }

            'Verbose' {
                # Use Format-TimeSpan with Full precision for detailed output
                $suffix = if ($isFuture) { 'from now' } else { 'ago' }
                $formatted = Format-TimeSpan -TimeSpan $timeDifference -Precision Full
                return "$formatted $suffix"
            }

            'Friendly' {
                # Use Format-TimeSpan with Compact for short output
                $suffix = if ($isFuture) { 'from now' } else { 'ago' }
                $formatted = Format-TimeSpan -TimeSpan $timeDifference -Compact
                return "$formatted $suffix"
            }
        }
    }
}