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.