Public/OS/Get-TimeInfo.ps1

<#
Copyright © 2024 Integris. For internal company use only. All rights reserved.
#>


FUNCTION Get-TimeInfo {
    <#
    .SYNOPSIS
    Retrieves and displays detailed time information for the system.
 
    .DESCRIPTION
    This function collects various time-related details, including local time, Windows time, GMT time, time zone information, and NTP server configuration. It also calculates the difference between the system time and the NTP time.
 
    .PARAMETER NoTimeZone
    If specified, skips the retrieval of time zone information.
 
    .EXAMPLE
    Get-TimeInfo
 
    This command retrieves and displays detailed time information for the system.
 
    .EXAMPLE
    Get-TimeInfo -NoTimeZone
 
    This command retrieves and displays detailed time information for the system, excluding time zone information.
 
    .NOTES
    This function uses various modules and web requests to gather time information and may require appropriate permissions to execute.
    #>


    [CmdletBinding()]
    Param (
        [switch]$NoTimeZone = $False
    )

    $NTPTimeInstalled = Test-IntegrisModuleDependency -Name NTPTime -Force

    # Temp Vars
    $Results = @()
    $NTPTime = ""
    $TimezoneDiff = ""

    # Results Vars
    $Hostname = $env:COMPUTERNAME
    $ApproximateLocation = ""
    $LocalTime = ""
    $WindowsTime = ""            
    $WindowsTimeGMT = ""
    $GMTTime = ""        
    $SecondsDifference =  ""
    $WindowsTimeZone = ""
    $WindowsUTCOffSet = ""
    $LocalTimeZone = ""
    $LocalUTCOffSet = ""            
    $NTPServer = ""
    $TimeDifference = ""

    # Get NTP Server Info
    IF ($True) {
        TRY {
            $NTPConfig = & "w32tm" /query /configuration
            $ServerList = @()
            IF ($null -eq ($NTPConfig | Select-String "NTPServer:")) {
                $ServerList = "None"
            }
            ELSE {
                $ServerString = ($NTPConfig | Select-String "NTPServer:")
                $ServerList = $ServerString.ToString().Replace("NtpServer: ","").Replace(" (Local)","").Split(" ")
            }

            $NTPServer = $ServerList
        }
        CATCH {
            $NTPServer = "[Error.Command.w32tm.Unknown]"
        }
    }

    # Get Actual Time
    IF ($True) {
        IF ($NTPTimeInstalled -eq $False) {
            Write-Verbose "Required module NTPTime is missing and could not be installed. Please install module an try again: https://www.powershellgallery.com/packages/NTPTime" 
            $LocalTime = "[Error.Module.NTPTime.NotInstalled]" 
            $GMTTime = "[Error.Module.NTPTime.NotInstalled]"
            $SecondsDifference = "[Error.Module.NTPTime.NotInstalled]"
        }
        TRY { $NTPTime = (Get-NTPTime -MaxOffset 1999999999 -ErrorAction Continue).NTPTime }
        CATCH {
            Write-Verbose "NTP request to pool.ntp.org failed. If this error persists, check connectivity and/or security tools."
            $LocalTime = "[Error.Module.NTPTime.RequestFailed]" 
            $GMTTime = "[Error.Module.NTPTime.RequestFailed]"
            $SecondsDifference = "[Error.Module.NTPTime.RequestFailed]"
        }
    }

    # Get Geo Location Data
    IF ($True) {
        TRY { $GeoData = Invoke-RestMethod -Method Get -Uri "http://ip-api.com/json/$PublicIPv4" }
        CATCH {
            TRY { $GeoData = Invoke-RestMethod -Method Get -Uri "http://ip-api.com/json/$PublicIPv4" }
            CATCH {
                Write-Verbose "WebRequest failed. Check firewall settings or other web security applications, such as ThreatLocker or OpenDNS. IP-API.com must be whitelisted on port 80."
            
                $ApproximateLocation = "[Error.WebRequest.IP-API.com:80.RequestFailed]"
            }
        }
    }
  
    # Get TimeZone Data
    IF ($NoTimeZone -eq $False) {
        TRY {
            $TimeZoneID = $GeoData.timezone
            $TimeZoneEncoded = [uri]::EscapeDataString($TimeZoneID)
            $TimeZoneData = Invoke-RestMethod -Method Get -Uri "https://timeapi.io/api/timezone/zone?timeZone=$TimeZoneEncoded"
            $TimeZoneUTCOffset = $TimeZoneData.currentUtcOffset.seconds / 3600
        }
        CATCH {
            TRY {
                $TimeZoneID = $GeoData.timezone
                $TimeZoneEncoded = [uri]::EscapeDataString($TimeZoneID)
                $TimeZoneData = Invoke-RestMethod -Method Get -Uri "https://timeapi.io/api/timezone/zone?timeZone=$TimeZoneEncoded"
                $TimeZoneUTCOffset = $TimeZoneData.currentUtcOffset.seconds / 3600
            }
            CATCH {
                Write-Verbose "WebRequest failed. Check firewall settings or other web security applications, such as ThreatLocker, OpenDNS, or timeapi.io must be whitelisted on port 80."; 
                $LocalTimeZone = "[Error.WebRequest.API.timeapi.io:80.RequestFailed]"
                $LocalUTCOffSet = "[Error.WebRequest.API.timeapi.io:80.RequestFailed]"
            }
        }
    }

    # Get Current Windows Time
    $WindowsTime = Get-Date

    $TimeDifference = $NTPTime - $WindowsTime

    IF ($ApproximateLocation -notlike "*Error.*") { $ApproximateLocation = $GeoData.City+", "+$GeoData.RegionName+" "+$GeoData.Zip }   
    IF ($GMTTime -notlike "*Error.*") { $GMTTime = [System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId($NTPTime, 'Greenwich Standard Time') }
    IF ($SecondsDifference -notlike "*Error.*") { $TimeDifference = $NTPTime - $WindowsTime; $SecondsDifference = [Math]::Round([Math]::Abs($TimeDifference.TotalSeconds),2) }
    TRY { $WindowsTimeZone = (Get-TimeZone).StandardName } CATCH { $WindowsTimeZone = "[Error.Command.Get-TimeZone.Unknown]" }
    TRY {
        [int]$WindowsUTCOffSet = (Get-TimeZone).BaseUtcOffset.ToString().Replace("0","").Replace(":","") 
        $WindowsTimeGMT = [System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId($WindowsTime, 'Greenwich Standard Time')
    } CATCH {
        $WindowsUTCOffSet = "[Error.Command.Get-TimeZone.Unknown]" 
        $WindowsTimeGMT = "[Error.Command.Get-TimeZone.Unknown]" 
    }   
    IF ($LocalTimeZone -notlike "*Error.*") { $LocalTimeZone = $TimeZoneID }
    IF ($LocalUTCOffSet -notlike "*Error.*") { $LocalUTCOffSet = [int]$TimeZoneUTCOffset }
    IF ($WindowsUTCOffSet -like "*Error.*") {"[Error.Command.Get-TimeZone.Unknown]" }
    ELSEIF ($LocalTimeZone -like "*Error.*" ) {"[Error.WebRequest.API.Geonames.org:80.RequestFailed]" }
    ELSE { $TimezoneDiff = $LocalUTCOffSet - $WindowsUTCOffSet }
    IF ($LocalTime -notlike "*Error.*") { $LocalTime = $NTPTime.AddHours($TimezoneDiff) }


    ### Get Local Timezone Offset
    ### This sections checks the timezone information to confirm if the current date should have a DST adjustment
    $DSTOffset = Get-CimInstance Win32_TimeZone
    $Date = (Get-Date)
    $AfterDST = $False
    $BeforeSST = $False
    IF (($Date.Month -gt $DSTOffset.DaylightMonth) -or ($Date.Month -eq $DSTOffset.DaylightMonth -and $Date.Day -ge $DSTOffset.DaylightDay)) { $AfterDST = $True }
    IF (($Date.Month -lt $DSTOffset.StandardMonth) -or ($Date.Month -eq $DSTOffset.StandardMonth -and $Date.Day -ge $StandardDay.DaylightDay)) { $BeforeSST = $True }
    IF ($AfterDST -and $BeforeSST) {    
        $WindowsTime = $WindowsTime.AddMinutes(-$DSTOffset.DaylightBias)
        $WindowsUTCOffSet = $WindowsUTCOffSet - ($DSTOffset.DaylightBias / 60)
    }

    
    $Results += New-Object PSObject -WarningAction SilentlyContinue -Property @{
        Hostname = $Hostname
        ApproximateLocation = $ApproximateLocation
        LocalTime = $LocalTime
        WindowsTime = $WindowsTime           
        WindowsTimeGMT = $WindowsTimeGMT
        LocalTimeGMT = $GMTTime
        SecondsDifference = $SecondsDifference
        WindowsTimeZone = $WindowsTimeZone
        WindowsUTCOffSet = $WindowsUTCOffSet
        LocalTimeZone = $LocalTimeZone
        LocalUTCOffSet = $LocalUTCOffSet       
        NTPServer = $NTPServer
    }
    
    RETURN $Results | Select-Object Hostname, ApproximateLocation, WindowsTime, LocalTime, WindowsTimeGMT, LocalTimeGMT, SecondsDifference, WindowsTimeZone, WindowsUTCOffSet, LocalTimeZone, LocalUTCOffSet, NTPServer
}