Converting WMI dates in a pipeline
Get-WMIObject is one of the most used cmdlets in PowerShell and many of us system administrators use it on a daily basis to perform our day to day tasks. Often we need to get values of CIM_DATETIME properties from instances of a class. An example would be the Win32_OperatingSystem class (truncated property list):
PS > $os = Get-WMIObject Win32_OperatingSystem
PS > $os | Format-List *
(...)
Debug : False
Description :
Distributed : False
EncryptionLevel : 256
ForegroundApplicationBoost : 2
InstallDate : 20090506145635.000000+180
LargeSystemCache :
LastBootUpTime : 20090610153830.359599+180
LocalDateTime : 20090614140846.358000+180
Locale : 040d
Manufacturer : Microsoft Corporation
(...)
The lines in yelloe shows the CIM_DATETIME properties of the class. As you can see they are a bit cryptic to read. We can convert these properties to .NET objects by using two 'built-in' methods:
PS > $os | Get-Member con*
TypeName: System.Management.ManagementObject#root\cimv2\Win32_OperatingSystem
Name MemberType Definition
---- ---------- ----------
ConvertFromDateTime ScriptMethod System.Object ConvertFromDateTime();
ConvertToDateTime ScriptMethod System.Object ConvertToDateTime();
ScriptMethods are not part of the original WMI class, they are custom methods added by the PowerShell team to Types.ps1xml file. The about_Types.ps1xml page explains how Types.ps1xml files let you extend the Microsoft .NET Framework types of the objects that are used in Windows PowerShell (You can also look at this post). Here's a screenshot of the relevant part of the file:
We can use these methods to convert WMI dates to .NET DatetTime dates and vice versa with ConvertToDateTime and ConvertFromDateTime:
PS > $os.InstallDate
20090506145635.000000+180
# Convert to .NET [DateTime]
PS > $os.ConvertToDateTime($os.InstallDate)
Wednesday, May 06, 2009 2:56:35 PM
# convert back to DMTF date
PS > $os.ConvertFromDateTime('Wednesday, May 06, 2009 2:56:35 PM')
20090506145635.000000+180
# convert the current time to DMTF datetime
PS > $os.ConvertFromDateTime([DateTime]::Now)
20090614154854.144569+180
Instead of converting WMI CIM_DATETIME properties one by one we can use the ConvertFrom-DMTFDate utility filter. A filter is a type of function that runs on each object in the pipeline, it resembles a function with all its statements in a Process block.
filter ConvertFrom-DMTFDate{
# Create 'offline' PSObject so we can
# convert the properties values
$pso = $_ | Select-Object *
# Get all CIM_DATETIME properties
$props = $_.psbase.properties | where {$_.type -eq "DateTime" -AND $_.value}
# Converts a given DMTF datetime to [DateTime].
# The returned DateTime will be in the current
# time zone of the system.
$dmtf = [System.Management.ManagementDateTimeConverter]
if($props)
{
# convert CIM_DATETIME properties to .NET DateTime
# objects and write them object back to the pipeline
foreach($p in $props){
$pso.($p.name) = $dmtf::ToDateTime($p.value)
}
$pso
}
else
{
# write original object back to pipeline
$_
}
}
Now we can pipe to the filter and convert all WMI dates at once.
PS > Get-WMIObject Win32_OperatingSystem | ConvertFrom-DMTFDate | Format-List *
(...)
Debug : False
Description :
Distributed : False
EncryptionLevel : 256
ForegroundApplicationBoost : 2
InstallDate : 5/6/2009 2:56:35 PM
LargeSystemCache :
LastBootUpTime : 6/10/2009 3:38:30 PM
LocalDateTime : 6/14/2009 3:02:45 PM
Locale : 040d
Manufacturer : Microsoft Corporation
(...)
Much more easier to read, isn't it? We can also pipe the result to another cmdlet, such as Where-Object and filter the objects by the DateTime values using any PowerShell comparison operators.