I often will figure out how to do something and later struggle to remember how to do it a months later. Rather than trying to remember what piece of code I wrote, why not write about. Assuming I can remember! Anyway, here's today's "PSRemembery".
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
I often use code like this, and I expect many of you do as well.
$os = Get-CimInstance win32_operatingsystem
$os | Select-Object CSName,LastBootUpTime,
@{Name="Uptime";Expression={(Get-Date) - $_.lastbootuptime}}
This is pretty straight forward. Subtract the LastBootUpTime property from the current datetime to get a timespan object that shows how long this computer has been up and running. But...I want to get rid of the milliseconds value. It's irrelevant as far as I'm concerned and takes up space that I might want to use for another property. I need to format that value.
I'll create a variable with this timespan value.
$up = (Get-Date) - $os.lastbootuptime
As you can see, the timespan object has a ToString() method which PowerShell is using when it formats the result for me.
It turns out I can use this method to format the string to meet my needs. But it is tricky and this is the part I always forget. You can use case-sensitive tokens.
This gets rid of the millesecond value but it is a bit hard to decipher. I can add other bits of text to the format string but I have to tell PowerShell to treat them as literal strings.
$up.ToString("dd\.hh\:mm\:ss")
I am inserting the puncuation to make the timespan easier to read.
If I wanted to include say the milliseconds to 2 decimal points I could do that as well. Let's take this a step further. I know some people like to see the string that includes 'days', 'hours', and 'minutes' (or some variation) as part of the output. I can do that as well. Here are a few examples.
$up.ToString("dd'dy:'hh'hr:'mm'min:'ss'sec'")
$up.ToString("dd' days 'hh' hours 'mm' minutes 'ss' seconds'")
Instead of escaping punctuation, I wrap the text in single quotes which tells PowerShell not to treat it as a special character. The spaces are part of the single quoted values.
Another option is to use the .NET format operator -f.
"{0:dd}d:{0:hh}h:{0:mm}m:{0:ss}s" -f $up
The left side of the operator contains the replacement tokens. The right side contains the objects that are processed. The 0 tells PowerShell to use the first obect on the right side. Notice I'm using the same case-senstive tokens.
The -f operator is handy way of building string messages that you might want to write to a file or display as a message.
But getting back to my initial need, with this formatting information I could run commands like these to get better formatted output.
$os | Select-Object CSName,LastBootUpTime,
@{Name="Uptime";Expression={((Get-Date) - $_.lastbootuptime).tostring("dd\.hh\:mm")}}
$os | Select-Object CSName,LastBootUpTime,
@{Name="Uptime";Expression={"{0:dd}d:{0:hh}h:{0:mm}m:{0:ss}s" -f ((Get-Date) - $_.lastbootuptime)}}
In the first example I decided that even the number of seconds was irrelevant so I dropped them.
One last comment is that even though my Uptime property started as a timespan, after this formatting I have turned the property into a string. My original code where I subtracted the LastBootUpTime property is still a timespan object. PowerShell just displays it as a string. This might affect your expression if you are piping to Sort-Object or Where-Object. Remember that Get-Member is your friend.
And I hope I can remember I wrote this the next time I'm trying to recall how I formatted a timespan to strip off the milliseconds. As always, questions and comments are welcome.
I knew that most objects have a default ToString() method, and I’ve used it for a timespan object many times. I didn’t realize (or simply forgot) that the timespan ToString() method has 4 constructors, one without a parameter and three others that take a format string.
I’ll definitely be using this shortcut instead of the -f operator which has been my go-to method of choice. Thanks for calling attention to that method!
Speaking of the -f operator, I use it quite a bit. Just keep in mind to double any curly braces that are needed in the string output. Likewise if your string is enclosed with single quotes, you must use two single quotes to include a single quote or, if your string enclosed with double quotes, you must use two double quotes to include a double quote in the string. For example: “{{“”0}} {0}” -f ‘test’ will be: {“0} test
If $elapsedTime is this:
Days : 0
Hours : 0
Minutes : 5
Seconds : 34
Milliseconds : 160
Ticks : 3341604305
TotalDays : 0.00386759757523148
TotalHours : 0.0928223418055556
TotalMinutes : 5.56934050833333
TotalSeconds : 334.1604305
TotalMilliseconds : 334160.4305
why is the day value 01? from this?
“{0:dd}d:{0:HH}h:{0:mm}m:{0:ss}s” -f ([datetime]$elapsedTime)
results in
01d:00h:05m:34s
If $ElapsedTime is a TimeSpan object you don’t need to treat it as a Datetime value. Jut use -f $elapsedtime. I can’t even get this to work for me as PowerShell won’t convert a timespan to a datatime. All I I can think is that if this working for you, as a datetime it is rounding something.