I've been working on a few scripting projects and the data I'm working with contains lexical timespans. Say what? You have probably seen these things. This is a string like P0DT0H0M47S to represents a timespan. They aren't difficult for humans to read. This one says "0 days 0 hours 0 minutes 47 seconds". The format is how timespans, or durations, are stored in XML files. You can read more about this at https://www.w3.org/TR/xmlschema-2/#duration. But I want to see these value as more PowerShell-friendly timespans. Turns out, this is actually very easy.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
One place you can see these values is with scheduled tasks in Windows.
I know PT72H represents a 72 hour timespan. To convert this into a timespan object is simple. Once you know how and in a second you will. You can use a built-in XML method to convert the value.
[System.Xml.XmlConvert]::ToTimeSpan("PT72H")
And sure enough, this is 72 hours or 3 days. What about converting the other way?
$ts = New-TimeSpan -hours 72 [System.Xml.XmlConvert]::ToString($ts)
The string is admittedly different, but it still represents 72 hours.
The process is simple, but I still wrote a pair of PowerShell advanced functions to handle these tasks.
Function ConvertTo-LexicalTimespan { [cmdletbinding()] [OutputType("String")] Param( [Parameter(Position = 0, Mandatory,HelpMessage = "Enter a timespan object", ValueFromPipeline)] [ValidateNotNullOrEmpty()] [timespan]$Timespan ) Begin { Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)" } #begin Process { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Converting $Timespan" Try { [System.Xml.XmlConvert]::ToString($Timespan) } Catch { Throw $_ } } #process End { Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)" } #end } #close ConvertTo-LexicalTimespan Function ConvertFrom-LexicalTimespan { [cmdletbinding()] [OutputType("string", "timespan")] Param( [Parameter(Position = 0, Mandatory, HelpMessage = "Enter a lexical time string like P23DT3H43M. This is case-sensitive.", ValueFromPipeline)] [ValidateNotNullOrEmpty()] [string]$String, [Parameter(HelpMessage = "Format the timespan as a string")] [switch]$AsString ) Begin { Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)" } #begin Process { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Converting $($String.toUpper()) to a timespan" Try { #convert string to upper case to help the user out $r = [System.Xml.XmlConvert]::ToTimeSpan($String.ToUpper()) if ($AsString) { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Displaying as a timespan string" $r.toString() } else { $r } } Catch { Throw $_ } } #process End { Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)" } #end } #close ConvertFrom-LexicalTimespan
Let's see how these work.
Here's another raw example.
Or I can use code like this:
(get-scheduledtask dailywatcher).settings.idlesettings | select-object @{Name="Idle";Expression={ConvertFrom-LexicalTimespan $_.idleDuration}}, @{Name="Wait";Expression={ConvertFrom-LexicalTimespan $_.WaitTimeout}}
Now, I have more meaningful output.
Here's one final example using Get-WsManInstance to show connected pssessions.
But I think you'll agree that using this code produces a better result.
Get-WSManInstance -ComputerName DOM1 -ResourceURI Shell -Enumerate | Select-Object -Property ResourceURI,Owner,ClientIP, @{Name="ShellRunTime";Expression = {ConvertFrom-LexicalTimespan $_.ShellRunTime}}, @{Name="ShellInactivity";Expression = {ConvertFrom-LexicalTimespan $_.ShellInactivity}}, @{Name="IdleTimeOut";Expression = {ConvertFrom-LexicalTimespan $_.IdleTimeOut}}, @{Name="MaxIdleTimeOut";Expression = {ConvertFrom-LexicalTimespan $_.MaxIdleTimeOut}}, @{Name="Computername";Expression={"DOM1"}}
This is code I would put into a function to create meaningful output to the PowerShell pipeline. Or if I wanted something more interactive, I could use my PSTypeExtensionTools module and create extensions.
$t = "System.Xml.XmlElement#http://schemas.microsoft.com/wbem/wsman/1/windows/shell#Shell" $t | Add-PSTypeExtension -MemberType ScriptProperty -MemberName ShellRun -Value {[System.Xml.XmlConvert]::ToTimeSpan($this.ShellRunTime) } $t | Add-PSTypeExtension -MemberType ScriptProperty -MemberName ShellInactive -Value {[System.Xml.XmlConvert]::ToTimeSpan($this.ShellInactivity) } $t | Add-PSTypeExtension -MemberType ScriptProperty -MemberName Idle -Value {[System.Xml.XmlConvert]::ToTimeSpan($this.IdleTimeout) } $t | Add-PSTypeExtension -MemberType ScriptProperty -MemberName MaxIdle -Value {[System.Xml.XmlConvert]::ToTimeSpan($this.MaxIdleTimeout) }
This makes it easier to run the command and select the new properties.
Or I could create a custom format view. But I'll leave that exercise for you.
Very, very cool! It is so simple. I killed 4 hours to test and parse scheduled task intervals with regex. Thanks a lot.