Hey, Scripting Guy! I need to be able to add and subtract dates so that I can use the results in custom queries for various Windows event logs using Windows PowerShell. Is doing this type of date manipulation easy or hard to do with Windows PowerShell? If it is not too difficult to do, could you whip up some examples of searching date ranges?
-- SH
Hello SH,
Microsoft Scripting Guy Ed Wilson here. One of the things I used to hate about writing VBScript code was working with dates. It is not that it was difficult, but it did seem a bit arcane. There must have been almost 20 different functions involved in date creation, parsing, and manipulation. Maybe not quite that many, but it seemed so. To make matters worse, I began to develop an aversion to parsing dates, and as a result I would do strange things to avoid parsing dates, which in turn made it more difficult (due to lack of familiarity) when I had to parse a date. So the vicious circle continued to wobble out of control until one day I forced myself to spend a day writing nothing but date manipulation code.
Luckily, date parsing and manipulation in Windows PowerShell is much easier. However, you won’t know it is easier until you sit down and spend some time working with the system.datetime .NET Framework object. Why, you may ask, do I refer to the system.datetime .NET Framework object when just a minute ago I was talking about dates? Because a date in Windows PowerShell is an instance of a datetime (you can leave the word “system” off if you wish) object. You can use the Get-Member cmdlet to see what type of object you are working with. The Get-Date cmdlet returns the current date and time. When the current date and time are piped to the Get-Member cmdlet, the members of a System.DateTime .NET Framework class are displayed. This is shown here:
PS C:\> Get-DateMonday, August 02, 2010 10:38:36 AMPS C:\> Get-Date | Get-Member TypeName: System.DateTimeName 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 Compa...Equals Method bool Equals(System.Object value), bool Equals...GetDateTimeFormats Method string[] GetDateTimeFormats(), string[] GetDa...GetHashCode Method int GetHashCode()GetType Method type GetType()GetTypeCode Method System.TypeCode GetTypeCode()IsDaylightSavingTime Method bool IsDaylightSavingTime()Subtract Method System.TimeSpan Subtract(System.DateTime valu...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 for...ToUniversalTime Method System.DateTime ToUniversalTime()DisplayHint NoteProperty Microsoft.PowerShell.Commands.DisplayHintType...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-Stri...PS C:\>
The members, methods, and properties of the System.DateTime .NET Framework class that are displayed are instance methods and properties. This means that they work on an instance of the class. Normally, an instance of a class is created when one uses a constructor. Constructors are detailed on MSDN. For example, the constructor for the System.DateTime class indicates that you can use a number of different ways to create an instance of the class. One constructor will accept three integers for input. However, when I supply the month, day, and year, an error arises. A closer look at the constructor indicates that the constructor wants year, month, and day. When this is supplied, a new instance of a system.datetime object is created. Because no time value was supplied to the constructor, the time is set to midnight. Here the New-Object cmdlet is used to create an instance of a system.datetime object:
PS C:\> $dte = New-Object system.datetime(8,1,2010) New-Object : Exception calling ".ctor" with "3" argument(s): "Year, Month, and Day p arameters describe an unrepresentable DateTime." At line:1 char:18 + $dte = New-Object <<<< system.datetime(8,1,2010) + CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationE xception + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell .Commands.NewObjectCommand PS C:\> $dte = New-Object system.datetime(2010,8,1) PS C:\> $dte Sunday, August 01, 2010 12:00:00 AM PS C:\>
Now and today are both static properties from the system.datetime .NET Framework class that return instances of the class. To call a static property, place the class name inside square brackets, use two colons, and then add the property name. To prove that it is a datetime object that is returned, use the gettype method that is always available from any system.object. This is shown here:
PS C:\> [datetime]::nowMonday, August 02, 2010 10:48:39 AMPS C:\> ([datetime]::now).gettype()IsPublic IsSerial Name BaseType-------- -------- ---- --------True True DateTime System.ValueTypePS C:\> [datetime]::todayMonday, August 02, 2010 12:00:00 AMPS C:\> ([datetime]::today).gettype()IsPublic IsSerial Name BaseType-------- -------- ---- --------True True DateTime System.ValueTypePS C:\>
Interestingly enough, the datetime object that is returned via the Get-Date cmdlet is the same object that is returned by the static now property from the datetime class. Use of one or the other is a matter of preference. I generally use the Get-Date cmdlet because it is easier to type. Each methodology is illustrated here:
PS C:\> [datetime]::now Monday, August 02, 2010 10:51:32 AM PS C:\> Get-Date Monday, August 02, 2010 10:51:35 AM PS C:\>
After you have a datetime object, you can use the methods that are exposed by that object. For example, you can easily use one of the various methods to add minutes, hours, days, months, years, etc., to the current date to create a new date in the future or in the past (if you pass a negative number):
PS C:\> $dte = Get-DatePS C:\> $dte | Get-Member -MemberType method add* TypeName: System.DateTimeName 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)PS C:\> $dteMonday, August 02, 2010 10:58:58 AMPS C:\> $dte.AddDays(1)Tuesday, August 03, 2010 10:58:58 AMPS C:\> $dte.AddDays(-1)Sunday, August 01, 2010 10:58:58 AMPS C:\> $dte.AddHours(8)Monday, August 02, 2010 6:58:58 PMPS C:\> $dte.AddHours(-8)Monday, August 02, 2010 2:58:58 AMPS C:\>
This can easily be incorporated with a cmdlet such as Get-EventLog. If I want to see event log entries from the application log that occurred in the last hour, I can use code that looks like this:
PS C:\> Get-EventLog -LogName application -After $dte.AddMinutes(-60) Index Time EntryType Source InstanceID Message ----- ---- --------- ------ ---------- ------- 19016 Aug 02 10:24 Information Office Software P... 1073742827 The Software... 19015 Aug 02 10:24 Information Office Software P... 1073742827 The Software... 19014 Aug 02 10:24 Information Office Software P... 1073742827 The Software... 19013 Aug 02 10:24 0 Office Software P... 1073742726 The Software... 19012 Aug 02 10:24 Information Office Software P... 1073742890 Initializati... 19011 Aug 02 10:24 Information Office Software P... 1073742724 The Software...PS C:\>
When working with two datetime objects, you can subtract them and a timespan object is returned. This is shown here:
PS C:\> $dte = Get-DatePS C:\> $sDate = Get-DatePS C:\> $sDate - $dteDays : 0Hours : 0Minutes : 0Seconds : 8Milliseconds : 533Ticks : 85332000TotalDays : 9.87638888888889E-05TotalHours : 0.00237033333333333TotalMinutes : 0.14222TotalSeconds : 8.5332TotalMilliseconds : 8533.2PS C:\> ($sDate - $dte).gettype()IsPublic IsSerial Name BaseType-------- -------- ---- --------True True TimeSpan System.ValueTypePS C:\>
A system.TimeSpan .NET Framework class has a number of interesting properties and methods. These are shown here:
PS C:\> $ts = $sDate - $dtePS C:\> $ts | gm TypeName: System.TimeSpanName MemberType Definition---- ---------- ----------Add Method System.TimeSpan Add(System.TimeSpan ts)CompareTo Method int CompareTo(System.Object value), int CompareTo(Sy...Duration Method System.TimeSpan Duration()Equals Method bool Equals(System.Object value), bool Equals(System...GetHashCode Method int GetHashCode()GetType Method type GetType()Negate Method System.TimeSpan Negate()Subtract Method System.TimeSpan Subtract(System.TimeSpan ts)ToString Method string ToString()Days Property System.Int32 Days {get;}Hours Property System.Int32 Hours {get;}Milliseconds Property System.Int32 Milliseconds {get;}Minutes Property System.Int32 Minutes {get;}Seconds Property System.Int32 Seconds {get;}Ticks Property System.Int64 Ticks {get;}TotalDays Property System.Double TotalDays {get;}TotalHours Property System.Double TotalHours {get;}TotalMilliseconds Property System.Double TotalMilliseconds {get;}TotalMinutes Property System.Double TotalMinutes {get;}TotalSeconds Property System.Double TotalSeconds {get;}PS C:\>
In general, I allow Windows PowerShell to display all of the properties of the timespan object. For example, if I am interested in seeing how long it takes my Windows 7 desktop to start and to present the Explorer desktop, I can use the Get-Process cmdlet to return the starttime of both the smss process and the explorer process. I can then subtract the two times, which will return a timespan object. The results are shown in the following image. Keep in mind that to return the starttime property of the smss process, the Windows PowerShell console must be running with administrator rights.
SH, that is all there is to using methods from the DateTime object to perform date manipulation. Date Week will continue tomorrow when we will talk about working with Windows Management Instrumentation and dates.
We invite 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