Wednesday, January 14, 2009

Date and Time in PowerShell (and WMI)

In yesterday’s PowerShell script of the day entry,posted over on my PowerShell scripts blog, I re-implemented an MSDN sample (originally written in VBScript) that calculates up time. The script first used a WMI class (Win32_OperatingSystem) to determine when a computer started. Then the script gets the current time, and works out and displays the difference (i.e. the current uptime).

The .NET framework contains a class, System.DateTime, that provides a variety of time/date related features.The PowerShell Cmdlet Get-Date returns a System.DateTime object that contains the current date and time. You can use the methods and properties of the class to get aspects of that current date and time as you need. For example:

PSH [D:\foo]: $d=Get-Date
PSH [D:\foo]: $d.Date

14 January 2009 00:00:00

PSH [D:\foo]: $d.Year
PSH [D:\foo]: $d.IsDaylightSavingTime()

You can, of course, pipe your DateTime object to Get-member to see the other properties and methods on this class (or refer to the MSDN documentation). There’s a lot of pretty rich date and time handling available.

The problem I had with yesterday’s script is that WMI uses a different format for date and time. The Win32_OperatingSystem WMI object has a property, LastBootUpTime which returns the date and time when the OS was last booted. However, WMI returns this as a string that is formatted rather differently to System.DateTime, as you can see here:

PSH [D:\foo]: $os=Get-WmiObject Win32_OperatingSystem
PSH [D:\foo]: $os.LastBootUpTime

This demonstrates that .NET, and therefore PowerShell, uses a native date/time formats that are different. For many admins (and for most native level developers) this is not a big deal. But if you need to inter-operate, you have a small issue of converting between the two formats.

As it turns out, solving this issue is simple. The developers of .NET created a simple solution. First, there’s a .NET class,  System.Management.ManagementDateTimeConverter, which does date and time conversion. This class has a method, ToDateTime, which converts a WMI date string into a .NET DateTime object. I used this  method in the WMIDateStringToDate function in Get-Uptime.ps1.

You can either implement a function (i.e. WmiDateStringToDate) in your profile or cut/pasted it into any WMI script you write. Naturally, you could just use the .NET method natively. As follows:

PSH [D:\foo]: $os=get-wmiobject win32_operatingsystem
PSH [D:\foo]: $time = $os.lastbootuptime
PSH [D:\foo]: [System.Management.ManagementDateTimeconverter]::ToDateTime($time)

12 January 2009 14:24:58

PSH [D:\foo]: ([System.Management.ManagementDateTimeconverter]::ToDateTime($time)).hour

As with most things PowerShell, easy stuff is very easy while complex stuff is often just a method call away.


Unknown said...

In Powershell 2.0 I've seen ($os.ConvertToDateTime($os.lastBootupTime)). When I try to use this I get an error saying that the invoke method doesn't exist from ConvertToDateTime. I've reverted back to using the full .NET name as described in this post.

What's the difference?

Cyke said...

It looks like in the script you were examining, the object $os contained the Method "ConvertToDateTime".

If, after object creation, the properties on the $os object were examined by using the line "$os | get-member", you would see it as a Method-type property.

Conversely, the object that you were using does not have that same Method in it (probably a System.DateTime class object), so you can't call the Method that isn't there.

But as you've discovered, if that Method isn't built into the Class that your object is instantiated from, you can always call the full .NET name, so that works fine too!