PowerShell Problem Solver: Converting Universal Time using PowerShell

Usually when I write these articles, it is in response to a problem or question I’ve encountered. But this week, I’m the one that needs some help. Recently my personal blog was experiencing some access problems. During the course of investigation, I came across some log information that included a date time value, but the value looked like 1426582884.043993. Fortunately, the related column heading was called ctime. A little more digging showed that this value is related to Universal Coordinated Time (UTC). The ctime value the number of seconds that have passed since the epoch.

You can think of the epoch as the big bang of computing, except that we know the date is January 1, 1970. In the Unix world, time is often calculated from this starting point. In my case, my blog was running on a Linux-based host, which is why the log information used this value. I needed a way to convert 1426582884.043993 into a meaningful date time value. You might run into this issue if you need to process some log files. Fortunately, this wasn’t too difficult to resolve.
I knew I could create a datetime object for the epoch.

$epoch = [datetime]"1/1/1970"

This object has a method called AddSeconds, so all I needed to do was specify my ctime value as a parameter.

$epoch.AddSeconds(1426582884.043993)

Using the AddSeconds method in Windows PowerShell. (Image Credit: Jeff Hicks)
Using the AddSeconds method in Windows PowerShell. (Image Credit: Jeff Hicks)

More than likely this value is GMT or UTC, and since I’m not in that time zone, the next step would be to convert this into my local time zone. Since I want to solve this with PowerShell, I’ll need a way to determine my offset from GMT. I can use WMI and query the Win32_Timezone class.

$tz = Get-CimInstance win32_timezone

The bias property will show me the number of minutes I am offset from GMT. In mycase, $tz.bias has a value of -300.

$utc = $epoch.AddSeconds(1426582884.043993)
$utc.AddMinutes($tz.Bias)

Using the bias property in Windows PowerShell. (Image Credit: Jeff Hicks)
Using the bias property in Windows PowerShell. (Image Credit: Jeff Hicks)

That’s better and pretty close, but there is that pesky detail about Daylight Savings Time. As I’m working on this problem, I am in DST, which means my effective offset from GMT is not -300 by -240. The Win32_TimeZone class has another property called DaylightBias, which is the number of minutes to subtract from the normal offset bias. This would then be more accurate:

$utc.AddMinutes(($tz.Bias)-($tz.DaylightBias))

The DaylightBias property in Windows PowerShell. (Image Credit: Jeff Hicks)
The DaylightBias property in Windows PowerShell. (Image Credit: Jeff Hicks)

Of course, I need way to determine if I need to make this calculation. I think the easiest way is to query the Win32_ComputerSystem class and look at the DaylightInEffect property.
Analyzing the DaylightInEffect property. (Image Credit: Jeff Hicks)
Analyzing the DaylightInEffect property. (Image Credit: Jeff Hicks)

If this is True, then I can subtract the Daylight bias value.

Naturally since this is something I will likely need to do again, I’ll wrap all of this up into an advanced function.

Function Convert-Ctime {
<#
.Synopsis
Convert a ctime value to a datetime value
.Description
Convert a ctime value to a more meaningful datetime value. The default behavior is to convert to local time, including any daylight saving time offset. Or you can view the time in GMT.
.Parameter GMT
Don't convert to local time.
.Example
PS C:\> Convert-cTime 1426582884.043993
Tuesday, March 17, 2015 5:01:24 AM
#>
[cmdletbinding()]
Param(
[Parameter(Position=0,Mandatory,
HelpMessage = "Enter a ctime value",
ValueFromPipeline)]
[ValidateNotNullorEmpty()]
[double]$Ctime,
[switch]$GMT
)
Begin {
Write-Verbose "Starting $($MyInvocation.Mycommand)"
#define universal starting time
[datetime]$utc = "1/1/1970"
#test for Daylight Saving Time
Write-Verbose "Checking DaylightSavingTime"
$dst = Get-Ciminstance -ClassName Win32_Computersystem -filter "DaylightInEffect = 'True'"
} #begin
Process {
Write-Verbose "Processing $ctime"
#add the ctime value which should be the number of
#seconds since 1/1/1970.
$gmtTime = $utc.AddSeconds($ctime)
if ($gmt) {
#display default time which should be GMT if
#user used -GMT parameter
Write-verbose "GMT"
$gmtTime
}
else {
#otherwise convert to the local time zone
Write-Verbose "Converting to $gmtTime to local time zone"
#get time zone information from WMI
$tz = Get-CimInstance -ClassName Win32_TimeZone
#the bias is the number of minutes offset from GMT
Write-Verbose "Timezone offset = $($tz.Bias)"
#Add the necessary number of minutes to convert
#to the local time.
$local = $gmtTime.AddMinutes($tz.bias)
if ($dst) {
Write-Verbose "DST in effect with bias = $($tz.daylightbias)"
$local.AddMinutes(-($tz.DaylightBias))
}
else {
#write the local time
$local
}
}
} #process
End {
Write-Verbose "Ending $($MyInvocation.Mycommand)"
} #end
} #close Convert-Ctime function

I can either specify a value or pipe one in. I also added plenty of Verbose output so you can see what is going on in the function.

The Convert-cTime function in action.  (Image Credit: Jeff Hicks)
The Convert-cTime function in action. (Image Credit: Jeff Hicks)


With this I can import my log data and display it in a more meaningful way.

import-csv .\Hits.csv | select @{Name="Date";Expression={Convert-ctime $_.ctime}},url

The import-csv cmdlet in Windows PowerShell. (Image Credit: Jeff Hicks)
The import-csv cmdlet in Windows PowerShell. (Image Credit: Jeff Hicks)

Even though I’m always going on about PowerShell and objects, sometimes we still need to parse and format simple text. Fortunately, PowerShell can make even that task a breeze.