Getting SNTP Network Time with PowerShell (Improved!)

This was originally posted here, with further notes.

This is an improved version that includes the network delay as per the NTP rfc and uses the resulting offset to calculate a more accurate time.

The full code is here: https://skydrive.live.com/redir?resid=7CB58BE453F7E567!3445

Pipe to “Format-List *” to see all returned information.  Usage:

PS:\> Get-NtpTime uk.pool.ntp.org

NtpTime          : 16/09/2012 14:26:46
OffsetSeconds    : -0.19
NtpVersionNumber : 3
Mode_text        : server
Stratum          : 3
PollInterval     : 00:00:01
PrecisionSeconds : 1.9073486328125E-06

 

PS:\> Get-NtpTime uk.pool.ntp.org | fl *

NtpTime          : 16/09/2012 14:26:51
Offset           : -190.25927734375
OffsetSeconds    : -0.19
Delay            : 18.0205078125
t1ms             : 3556790811477.35
t2ms             : 3556790811296.1
t3ms             : 3556790811296.1
t4ms             : 3556790811495.37
t1               : 16/09/2012 14:26:51
t2               : 16/09/2012 14:26:51
t3               : 16/09/2012 14:26:51
t4               : 16/09/2012 14:26:51
LI               : 0
LI_text          : no warning
NtpVersionNumber : 3
Mode             : 4
Mode_text        : server
Stratum          : 3
Stratum_text     : secondary reference (via NTP or SNTP)
PollIntervalRaw  : 0
PollInterval     : 00:00:01
Precision        : -19
PrecisionSeconds : 1.9073486328125E-06
Raw              : {28, 3, 0, 237, 0, 0, 2, 183, 0, 0, 0, 0, 80, 93, 163, 202, 212, 0, 80, 97…}

If you don’t like comments in your code (and just want the resulting timestamp), here’s the bare bones version:

<#
Chris Warwick, @cjwarwickps, August 2012
chrisjwarwick.wordpress.com 
#>

$Server = 'pool.ntp.org'
$StartOfEpoch=New-Object DateTime(1900,1,1,0,0,0,[DateTimeKind]::Utc)   

[Byte[]]$NtpData = ,0 * 48
$NtpData[0] = 0x1B    # NTP Request header in first byte

$Socket = New-Object Net.Sockets.Socket([Net.Sockets.AddressFamily]::InterNetwork,
                                        [Net.Sockets.SocketType]::Dgram,
                                        [Net.Sockets.ProtocolType]::Udp)
$Socket.Connect($Server,123)
$t1 = Get-Date    # Start of transaction... the clock is ticking...
[Void]$Socket.Send($NtpData)
[Void]$Socket.Receive($NtpData)  
$t4 = Get-Date    # End of transaction time
$Socket.Close()

$IntPart = [BitConverter]::ToUInt32($NtpData[43..40],0)   # t3
$FracPart = [BitConverter]::ToUInt32($NtpData[47..44],0)
$t3ms = $IntPart * 1000 + ($FracPart * 1000 / 0x100000000)

$IntPart = [BitConverter]::ToUInt32($NtpData[35..32],0)   # t2
$FracPart = [BitConverter]::ToUInt32($NtpData[39..36],0)
$t2ms = $IntPart * 1000 + ($FracPart * 1000 / 0x100000000)

$t1ms = ([TimeZoneInfo]::ConvertTimeToUtc($t1) - $StartOfEpoch).TotalMilliseconds
$t4ms = ([TimeZoneInfo]::ConvertTimeToUtc($t4) - $StartOfEpoch).TotalMilliseconds
 
$Offset = (($t2ms - $t1ms) + ($t3ms-$t4ms))/2

$StartOfEpoch.AddMilliseconds($t4ms + $Offset).ToLocalTime() 
Advertisements

7 thoughts on “Getting SNTP Network Time with PowerShell (Improved!)”

  1. Great script, thanks for sharing! For some reason it always hits the exception at the following line here: #Throw “Network time offset exceeds maximum ($($MaxOffset)ms)”. When simply commenting that line out it seems to run well.
    Perhaps also good to mention how you can actually run the script. You can do this by adding saving the script to your local disk and name it i.e. script.ps1. Next, add the function in it to the environment first by running . .\script.ps1 (so dot, space, dot, slash, script.ps1). After that you can run Get-NtpTime in your PowerShell window.

  2. Hi,
    Thanks for your script. I have used it to write my own – hopefully better – w32tm.
    I had a problem with my vps as the clock get around 25 seconds slower per hour.
    I tried first w32tm which was just bad and then ntp :(
    If you are interested I can send it to you – just send me a mail.
    Gooly

  3. This morning the local internet connection crashed and I realized that your script hangs in this case.
    You may try:
    Try {
    $Socket.Connect($Server,123)
    $result = $Socket.BeginConnect($Server,123, $null, $null);
    $success = $result.AsyncWaitHandle.WaitOne(2000, $true);
    if ( !$success ) {
    Write-Error “$Server : $_”
    Throw “$Server : Failed to connect to server $Server”
    }
    }
    Catch {
    Write-Error “$Server : $_”
    Throw “$Server : Failed to connect to server $Server”
    }

    Now the internet is back so I didn’t check in this case, so please check if you are interested!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s