Using Windows PowerShell to Work with Dates

Using Windows PowerShell to Work with Dates

  • Comments 6
  • Likes

 

Hey, Scripting Guy! Question

Hey, Scripting Guy! I need to be able to use Windows PowerShell to work with dates. Dates have always been a difficult thing to work with, and I just dread scripting anything that has to do with dates. It seemed that VBScript had a dozen functions that were used to work with dates and times. Is there something similar for Windows PowerShell?

-- GS

 

Hey, Scripting Guy! Answer

Hello GS,

Microsoft Scripting Guy Ed Wilson here. I have never had too much problems with dates. Basically, I just pop them in my mouth whole like candy. The best dates I ever had were in Egypt. Oh, wait a minute, you don’t mean those kinds of dates. Sorry. In the old days, databases used to have three fields for the date. One for day, one for month and one for year. You may have heard about the Y2K bug? That was because the field for year was only big enough for the last two numbers of the year … and therefore the year 2000 would be represented as 00. This is a serious problem if you are dealing with medical records and the person is born in 5/4/00. You do not know whether to send the person to geriatrics or to pediatrics.

When working on one of those old systems, you had to build up the date by reading three different fields. If I were going to do a similar thing in Windows PowerShell, it might look like this:

PS C:\> $dd = "05"
PS C:\> $mm = "09"
PS C:\> $yy = "10"
PS C:\> "$dd/$mm/$yy"
05/09/10
PS C:\>

One of the great things about Windows PowerShell is that it is object oriented. Everything in Windows PowerShell is an object, even the string I just built up…whoops, I spoiled the surprise. Let’s pretend I did not spoil the surprise (I like spoiling my supper too by eating desert first, but that is a different story—although it could have something to do with dates). The following code is really ugly, but it works because of the “object-ness” of Windows PowerShell. To find out what kind of object you are working with, you can use the GetType method as shown here:

PS C:\> "$dd/$mm/$yy".GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object


PS C:\>

I said the previous code was ugly, but that is only because it looks strange. In general, I would use a set of parentheses before calling a method like GetType:

PS C:\> ("$dd/$mm/$yy").GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object


PS C:\>

Actually using the parentheses is a good idea, because at times you can get yourself into trouble. This is shown here, where I attempt to get the type of the number 1:

PS C:\> 1.GetType()
Bad numeric constant: 1..
At line:1 char:3
+ 1. <<<< GetType()
+ CategoryInfo : ParserError: (1.:String) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : BadNumericConstant
PS C:\>

The problem is not because the number is not an object and does not have a type. The problem is that Windows PowerShell is not sure what to do. The parentheses help in that regard. Just as in high school algebra, the parentheses tells Windows PowerShell to perform the operation inside before looking outside the parentheses. As shown here, the number 1 is in fact an object:

PS C:\> (1).GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Int32                                    System.ValueType


PS C:\>

Windows PowerShell treats the number 1 as an int32. It is actually smaller than that, and you can specifically cast it to an int16. This is shown here:

PS C:\> [int16]1 | gm


   TypeName: System.Int16

Name        MemberType Definition
----        ---------- ----------
CompareTo   Method     int CompareTo(System.Object value), int CompareTo(System.Int16 value)
Equals      Method     bool Equals(System.Int16 obj), bool Equals(System.Object obj)
GetHashCode Method     int GetHashCode()
GetType     Method     type GetType()
GetTypeCode Method     System.TypeCode GetTypeCode()
ToString    Method     string ToString(System.IFormatProvider provider), string ToString(string ...

Casting? What is that? Windows PowerShell has a dynamic type system, and I can convert one data type to another data type on the fly. By default, Windows PowerShell treats all numbers as int32. As seen earlier, I can constrain the type, force the type, or cast the type (whatever jargon you wish to use) to make my int32 into an int16. Cool? Ok, perhaps it depends on your geek factor. Let’s get back to the problem at hand: dates. We have just seen that we had a string when we put the numbers together. Remember this?

PS C:\> "$dd/$mm/$yy"
05/09/10
PS C:\> "$dd/$mm/$yy".GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object


PS C:\>

One of the really cool things about objects is they have methods and properties. The problem is that our date is a string. Dates are awesome in cakes and in cookies, but not if they are strings. The members of a system.string class are suboptimal for dealing with dates. This is shown in the following image.

Image of members of system.string class

While it is true that I could do string manipulation to parse the date, it would be better if we had a datetime object to work with. To do this, we can cast the string to a system.datetime .NET Framework object. We can then use the GetType method to ensure the conversion has taken place; although this last step is basically redundant because the results of the cast are immediately observable. Remember me saying that using the “ugly code” could get you into trouble? This is shown here, where an error is thrown. The fix is to use the parentheses:

PS C:\> [datetime]"$dd/$mm/$yy"

Sunday, May 09, 2010 12:00:00 AM


PS C:\> [datetime]"$dd/$mm/$yy".GetType()
Cannot convert the "System.String" value of type "System.RuntimeType" to type "System.DateTime".
At line:1 char:32
+ [datetime]"$dd/$mm/$yy".GetType <<<< ()
    + CategoryInfo          : NotSpecified: (:) [], RuntimeException
    + FullyQualifiedErrorId : RuntimeException

PS C:\> ([datetime]"$dd/$mm/$yy").GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     DateTime                                 System.ValueType


PS C:\>

In addition to gaining a nicer display of the date, the conversion from string to date has provided a number of useful members. Using the Get-Member cmdlet displays the methods and properties that are supplied by the system.datetime .NET Framework class:

PS C:\> [datetime]"$dd/$mm/$yy" | Get-Member


   TypeName: System.DateTime

Name                 MemberType     Definition
----                 ----------     ----------
Add                  Method         System.DateTime Add(System.TimeSpan value)
AddDays              Method         System.DateTime AddDays(double value)
AddHours             Method         System.DateTime AddHours(double value)
AddMilliseconds      Method         System.DateTime AddMilliseconds(double value)
AddMinutes           Method         System.DateTime AddMinutes(double value)
AddMonths            Method         System.DateTime AddMonths(int months)
AddSeconds           Method         System.DateTime AddSeconds(double value)
AddTicks             Method         System.DateTime AddTicks(long value)
AddYears             Method         System.DateTime AddYears(int value)
CompareTo            Method         int CompareTo(System.Object value), int CompareTo(System.Dat...
Equals               Method         bool Equals(System.Object value), bool Equals(System.DateTim...
GetDateTimeFormats   Method         string[] GetDateTimeFormats(), string[] GetDateTimeFormats(S...
GetHashCode          Method         int GetHashCode()
GetType              Method         type GetType()
GetTypeCode          Method         System.TypeCode GetTypeCode()
IsDaylightSavingTime Method         bool IsDaylightSavingTime()
Subtract             Method         System.TimeSpan Subtract(System.DateTime value), System.Date...
ToBinary             Method         long ToBinary()
ToFileTime           Method         long ToFileTime()
ToFileTimeUtc        Method         long ToFileTimeUtc()
ToLocalTime          Method         System.DateTime ToLocalTime()
ToLongDateString     Method         string ToLongDateString()
ToLongTimeString     Method         string ToLongTimeString()
ToOADate             Method         double ToOADate()
ToShortDateString    Method         string ToShortDateString()
ToShortTimeString    Method         string ToShortTimeString()
ToString             Method         string ToString(), string ToString(string format), string To...
ToUniversalTime      Method         System.DateTime ToUniversalTime()
Date                 Property       System.DateTime Date {get;}
Day                  Property       System.Int32 Day {get;}
DayOfWeek            Property       System.DayOfWeek DayOfWeek {get;}
DayOfYear            Property       System.Int32 DayOfYear {get;}
Hour                 Property       System.Int32 Hour {get;}
Kind                 Property       System.DateTimeKind Kind {get;}
Millisecond          Property       System.Int32 Millisecond {get;}
Minute               Property       System.Int32 Minute {get;}
Month                Property       System.Int32 Month {get;}
Second               Property       System.Int32 Second {get;}
Ticks                Property       System.Int64 Ticks {get;}
TimeOfDay            Property       System.TimeSpan TimeOfDay {get;}
Year                 Property       System.Int32 Year {get;}
DateTime             ScriptProperty System.Object DateTime {get=if ((& { Set-StrictMode -Version...


PS C:\>

 

GS, it is obvious that you are not the only one who thought working with dates was annoying. The large collection of methods and properties exposed by the datetime class can help to smooth out the difficulty of working with dates. The benefits are immediately available, and most are extremely easy to use. For example, what was the day of the week for our date that we created?

PS C:\> ([datetime]"$dd/$mm/$yy").dayOfWeek
Sunday
PS C:\>

What was the day of the year? 

PS C:\> ([datetime]"$dd/$mm/$yy").dayOfyear
129
PS C:\>

What about if I need to add 45 days to my original date? It is no problem using the addDays method:

PS C:\> ([datetime]"$dd/$mm/$yy").addDays(45)
Wednesday, June 23, 2010 12:00:00 AM
PS C:\>

GS, that is all there is to using Windows PowerShell to work with dates. Date Week will continue tomorrow when we will talk about date manipulation.

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

 

Ed Wilson and Craig Liebendorfer, Scripting Guys

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hey guys, that's cool. But a quick question, what if my string was in "mm/dd/yy" format? How can I let PowerShell know that the string is in that format so it can cast it correctly?

  • @sjl you can use the -uformat parameter to control the way PowerShell displays the date. Here is an example:

    PS C:\> Get-Date -UFormat %m/%d/%y

    01/30/12

  • The date is 5th September format is day/month/year, yet when you use datetime it says date is 9th May. How do you keep the non USA date format?

  • [datetime]::Now.ToString([cultureinfo]'en-US')

    [datetime]::Now.ToString([cultureinfo]'fr-FR')

    [datetime]::Now.ToString([cultureinfo]'en-GB')

  • [datetime]::Now.ToString([cultureinfo]'en-US')
    [datetime]::Now.ToString([cultureinfo]'fr-FR')
    [datetime]::Now.ToString([cultureinfo]'en-GB')

  • This is the command I used when converting an ADUser LastLogonTimeStamp into normal date format.

    [datetime]::FromFileTime((Get-ADUser $UserID -Properties *).LastLogonTimeStamp)