Weekend Scripter: Understanding TimeSpan Objects

Weekend Scripter: Understanding TimeSpan Objects

  • Comments 3
  • Likes

Summary: Guest bloggers, June Blender and Justin Hall, investigate strange behavior with TimeSpan objects in the Search-ADAccount cmdlet.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have a blog written by two guests: June Blender and Justin Hall. First, a little bit about Justin (June is already well known to our readers).

Photo of Justin Hall

Justin Hall is a technical writer and editor who has covered Active Directory and related technologies at Microsoft since 2001. He publishes articles on Microsoft TechNet and contributes to the Active Directory Documentation Team Blog.

Justin explains a misrepresentation…

Michael Smith, an Active Directory MVP, recently mentioned an error in the documentation for the Search-ADAccount cmdlet. The examples in the Help misrepresent the TimeSpan parameter by treating the argument as if it was a number of days, when it is actually a number of ticks. The examples showed how to use TimeSpan with a switch parameter, such as AccountExpiring or AccountInactive. But because the examples mistreated the TimeSpan argument, they produce unexpected results. For instance, the example for AccountExpiring said:

The following example shows how to specify a search for accounts that expire in the next 10 days.

-AccountExpiring -TimeSpan 10

But that example really searches for accounts that expire in 10 ticks, or .001 milliseconds. For the TimeSpan parameter argument to be 10 days, it must be presented like this:

-TimeSpan “10”

     -or-

-TimeSpan 10.00:00:00

This seemed like a simple documentation error to me. And because Windows PowerShell documentation is updatable, I could correct the Help topic, any then user who runs Update-Help will download the corrected Help for that cmdlet. So I asked June how to make the correction because she has much more expertise with Windows PowerShell documentation.

June reviewed the feedback and the topic and saw something odd. Why was it that when a parameter takes a TimeSpan object and you give it the integer 10, it interprets it as 10 ticks, and if you give it 10:00, it interprets it as 10 hours (not 10 days), but if you give it "10" as a string, it interprets it as 10 days? I will stop here so June can better explain what she discovered.

June explains...

Yes, as Justin said, our MVP friend, Michael Smith, was correct indeed, but we couldn't figure out why. So we turned to Windows PowerShell super-developer, Lee Holmes, for the answer. (Lee wrote a more advanced blog post about this question, too. Take a peek at Understanding PowerShell's Type Conversion Magic.)

It turns out that it's all about constructors. When we use a parameter value to create a new object, such as using the TimeSpan parameter of the Search-ADAccount cmdlet to create a new TimeSpan object, Windows PowerShell uses the constructors for the TimeSpan class in the .NET library, because…well…there's no other way to do it. And the interesting behavior that we saw results from the TimeSpan class constructors.

I'll explain more about constructors soon, and we'll take a trip to the Microsoft .NET library to look at the constructors for the TimeSpan class. While we're doing this, keep in mind that you can use this technique to figure out what Windows PowerShell needs to do to interpret the values of ANY parameter. Windows PowerShell might have some additional help for you, but when you learn about constructors, you have another detective tool in your scripting toolbox.

About .NET constructors

A .NET constructor tells you how to create or "construct" an instance of a .NET class. Every .NET class that allows you to create instances has at least one constructor, and some have many constructors, so they provide many different ways to create the object.

You can think of a group of constructors for a .NET class as the parameter sets for creating an instance of that .NET class. They tell you all of the valid parameter and parameter value combinations.

You can also think of constructors as recipes for creating an instance of the class. When there are different ways to create the class, there are multiple recipes, all with different ingredients.

Let's look at the constructors for the TimeSpan object. To see them, go to the System.Timespan class page in MSDN.  The TimeSpan Constructor is listed as the first subtopic.

Image of menu

Click TimeSpan Constructor (or scroll down to the first item on the TimeSpan page). These should look somewhat familiar—like parameter sets. Each one gives you a recipe for creating a TimeSpan object.

Image of menu

The TimeSpan class has four constructors, or four different ways of creating a time span.

Let's look at them one at a time. Here's the first constructor:

TimeSpan(Int64)

The first constructor creates a TimeSpan object from one Int64 (one big number). The description explains that when you create a time span from just one number, .NET interprets the number as the number of ticks. Interesting, eh?

The second constructor takes three 32-bit numbers:

TimeSpan(Int32, Int32, Int32)

The description says that when you use three numbers to create a TimeSpan object, .NET interprets the three numbers as hours, minutes, and seconds—in that order.

The third constructor takes four 32-bit numbers:

TimeSpan(Int32, Int32, Int32, Int32)

When you create a TimeSpan object with four numbers, .NET interprets them as days, hours, minutes, and seconds.

The last constructor takes five 32-bit numbers:

TimeSpan(Int32, Int32, Int32, Int32, Int32)

.NET interprets the numbers as days, hours, minutes, seconds, and milliseconds.

Aha! So this explains most of what we see when we use the TimeSpan parameter of Search-ADAccount, or other parameters that take TimeSpan values, such as the IdleTimeOut and IdleDuration parameters of New-ScheduledJobObjectFor the following examples, I'm going to use the IdleTimeout parameter of New-ScheduledJobObject, because the commands are a bit simpler, but the idea is exactly the same as Search-ADAccount.

When the value of IdleTimeout is one number, such as 10, it's interpreted as the number of ticks, or 10 ticks, because it uses the first constructor, TimeSpan(Int64), which interprets its value as ticks.

Image of command output 

When the value of IdleTimeout is two or three numbers (if you give Windows PowerShell two numbers, it fills in the third for you), it uses the second constructor, TimeSpan(Int32, Int32, Int32), which interprets the values as hours, minutes, and seconds. In this case, the 10 is interpreted as 10 hours.

Image of command output 

As the constructors showed, to get days, you need to give it four numbers. So, when the 10 is the first of four numbers, it's interpreted as 10 days.

Image of command output 

And if you give it five numbers, it uses the last constructor. The first number is still interpreted as days and the last number is interpreted as milliseconds.

Image of command output 

So why does the string value "10" produce days, as Michael noted, instead of hours or something else? For this, we have to look at the methods of the TimeSpan class. Windows PowerShell needs a method that converts a string into a TimeSpan object. The TimeSpan class has one—it's called Parse. The Parse method is pretty complex, because strings can vary so much.

[ws][-]{ d | [d.]hh:mm[:ss[.ff]] }[ws]

But if you give the Parse method a string, it interprets the first non-white space characters as days.

Image of command output

And back to Justin...

So that explains why the examples were producing unexpected results. And the difference in this case is significant because if you are searching for accounts that are set to expire, any accounts that expire in the next 10 days are interesting, and accounts that expire in the next .001 milliseconds probably don’t exist.

Special thanks to Michael Smith for alerting us to the documentation problem; to Lee Holmes for educating us about how this works; and to June for asking why, investigating the cause of the error, and writing this explanation in language that is easy to understand.

~Justin and June

Thank you, Justin and June. This is an awesome post and a great explanation! Join me tomorrow when I will talk about chasing errant ACLs in a folder full of files. It is cool—trust me.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • @Justin/June/Ed

    Yes - this is excellent.  Useful, interesting and clear.

    Here area couple more that show the strange behavior of the TimeSpan class:

    [timespan]10.  #10 Days   (dot causes evaluation as string)

    [timespan]'10:00'  #10 hours

    [timespan]'10.10'  # INVALID

    [timespan]'10.10:00' $ 10 days 10 hours  

  • Great article June and Justin!

    On a side note about constructors, I wrote a function that can help to show all of the possible parameters and parameter sets for a constructor here: gallery.technet.microsoft.com/.../Get-Constructor-a8911ebe

    Get-Constructor timespan

    TimeSpan Constructors

    ---------------------

    Int64 ticks

    Int32 hours, Int32 minutes, Int32 seconds

    Int32 days, Int32 hours, Int32 minutes, Int32 seconds

    Int32 days, Int32 hours, Int32 minutes, Int32 seconds, Int32 milliseconds

  • Thanks to both of you. There are so many ways to get it right and get it wrong. I'll check out that post, Boe. Thanks, again.