Quantcast
Channel: prgmr.io » casting
Viewing all articles
Browse latest Browse all 5

Taking PowerShell on a date

$
0
0

I needed to filter the event logs from a remote computer for power off events. The user was powering it off himself every now and then, but wouldn’t believe me when I said so. No problem, thanks to the event logs all his shutdown requests are logged so it’s only a matter of culling it out.

I could have user Event Viewer but PowerShell is way easier. I only need to check the System event logs for any events with ID 1074. Further, for every shutdown, events with this ID are logged twice – one starting with the line “The process C:Windowssystem32winlogon.exe …” the other starting with the line “The process Explorer.EXE has initiated the power off …” – so I’d like to filter the output to just include one of these lines. Finally, I’d also like the day of the week to be shown.

So here’s what I did:

Get-WinEvent -ComputerName $computer -FilterHashTable @{Logname="System";ID="1074"} |
  ?{ $_.Message -match "^The process Explorer.EXE has initiated the power off" } |
  ft TimeCreated,@{Name="Day";Expression={ $_.TimeCreated.DayOfWeek }}

So far so good. I use Get-WinEvent to return the event logs I want, use the Message property of these returned objects to filter logs containing just the text we want. And finally I use the TimeCreated property of these objects to display the time and day. This TimeCreated property is the subject of this post so let’s focus on that further.

First, how do we get the members and properties of the objects returned by Get-WinEvent? Here’s how:

PS> Get-WinEvent -MaxEvents 1 | gm
   TypeName: System.Diagnostics.Eventing.Reader.EventLogRecord
Name                 MemberType   Definition
----                 ----------   ----------
Dispose              Method       System.Void Dispose()
Equals               Method       bool Equals(System.Object obj)
FormatDescription    Method       string FormatDescription(), string FormatDescription(System.Collections.Generic.IE...
GetHashCode          Method       int GetHashCode()
GetPropertyValues    Method       System.Collections.Generic.IList[System.Object] GetPropertyValues(System.Diagnosti...
GetType              Method       type GetType()
ToString             Method       string ToString()
ToXml                Method       string ToXml()
Message              NoteProperty System.String Message=A process has exited....
...
TimeCreated          Property     System.Nullable`1[[System.DateTime, mscorlib, Version=2.0.0.0, Culture=neutral, Pu...
UserId               Property     System.Security.Principal.SecurityIdentifier UserId {get;}
Version              Property     System.Nullable`1[[System.Byte, mscorlib, Version=2.0.0.0, Culture=neutral, Public...

Notice the TimeCreated property.

PS> Get-WinEvent -MaxEvents 1 | select TimeCreated | gm
   TypeName: Selected.System.Diagnostics.Eventing.Reader.EventLogRecord
Name        MemberType   Definition
----        ----------   ----------
Equals      Method       bool Equals(System.Object obj)
GetHashCode Method       int GetHashCode()
GetType     Method       type GetType()
ToString    Method       string ToString()
TimeCreated NoteProperty System.DateTime TimeCreated=02/06/2013 11:01:17
PS> (Get-WinEvent -MaxEvents 1).TimeCreated | gm
   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)
...
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 1; $this.DisplayHin...

The TimeCreated property is an object of class DateTime. To get the day of the date instance, use the TimeCreated.DayofWeek property. Which is what I extract via the Format-Table expression in my initial code:

... | ft TimeCreated,@{Name="Day";Expression={ $_.TimeCreated.DayOfWeek }}

This post from the “Hey, Scripting Guy! Blog” is a good read on Get-WinEvent and the DateTime class.

Focussing on the DateTime class, this is the type of the object returned by the get-date cmdlet too. Although not obvious from the syntax of the get-date cmdlet, you can use it to return a DateTime object instance of the date you specify as string. Like thus:

PS> get-date
02 June 2013 11:18:45
PS> get-date "31/05/2013 12:24:11"
31 May 2013 12:24:11

What’s more, you can even format the string:

PS> get-date "31/05/2013 12:24:11" -Format "dd"
31
PS> get-date "31/05/2013 12:24:11" -Format "ddd"
Fri
PS> get-date "31/05/2013 12:24:11" -Format "dddd"
Friday
PS> get-date "31/05/2013 12:24:11" -Format "'The day is' dddd"
The day is Friday

The -Format switch takes a format specifier. As seen above “dd” stands for the date, “ddd” stands for the day in short-hand, “dddd” stands for the date in longhand, and so on.

You can also typecast a string to the DateTime class:

PS> [datetime]"05/31/2013"
31 May 2013 00:00:00

The typecast technique has a limitation though in that it expects the input to be in the format of US, i.e. “MM/dd/YYYY”. The DateTime class has a static member called Parse(), however, that is smart enough the use the country/ culture of the computer.

PS> [datetime] | gm  -Static
   TypeName: System.DateTime
Name            MemberType Definition
----            ---------- ----------
Compare         Method     static int Compare(System.DateTime t1, System.DateTime t2)
DaysInMonth     Method     static int DaysInMonth(int year, int month)
Equals          Method     static bool Equals(System.DateTime t1, System.DateTime t2), static bool Equals(System.Obj...
FromBinary      Method     static System.DateTime FromBinary(long dateData)
FromFileTime    Method     static System.DateTime FromFileTime(long fileTime)
FromFileTimeUtc Method     static System.DateTime FromFileTimeUtc(long fileTime)
FromOADate      Method     static System.DateTime FromOADate(double d)
IsLeapYear      Method     static bool IsLeapYear(int year)
Parse           Method     static System.DateTime Parse(string s), static System.DateTime Parse(string s, System.IFo...
ParseExact      Method     static System.DateTime ParseExact(string s, string format, System.IFormatProvider provide...
ReferenceEquals Method     static bool ReferenceEquals(System.Object objA, System.Object objB)
SpecifyKind     Method     static System.DateTime SpecifyKind(System.DateTime value, System.DateTimeKind kind)
TryParse        Method     static bool TryParse(string s, System.DateTime&, mscorlib, Version=2.0.0.0, Culture=neutr...
TryParseExact   Method     static bool TryParseExact(string s, string format, System.IFormatProvider provider, Syste...
MaxValue        Property   static System.DateTime MaxValue {get;}
MinValue        Property   static System.DateTime MinValue {get;}
Now             Property   System.DateTime Now {get;}
Today           Property   System.DateTime Today {get;}
UtcNow          Property   System.DateTime UtcNow {get;}
PS> [datetime]::Parse("31/05/2013")
31 May 2013 00:00:00

How do we parse the date if it’s in a different format though? Both the get-date cmdlet and the [datetime]::Parse() method fail if the input string does not match the date format of the computer:

PS> get-date "05/31/2013 12:24:11"
Get-Date : Cannot bind parameter 'Date'. Cannot convert value "05/31/2013 12:24:11" to type "System.DateTime". Error: "
String was not recognized as a valid DateTime."
At line:1 char:9
+ get-date <<<<  "05/31/2013 12:24:11"
    + CategoryInfo          : InvalidArgument: (:) [Get-Date], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.GetDateCommand
PS> [datetime]::Parse("05/31/2013 12:24:11")
Exception calling "Parse" with "1" argument(s): "String was not recognized as a valid DateTime."
At line:1 char:18
+ [datetime]::Parse <<<< ("05/31/2013 12:24:11")
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

It is possible to specify the date format of the input string. This only works with the Parse() method though. Like thus (thanks to this blog post):

PS> $usculture = [Globalization.cultureinfo]::GetCultureInfo("en-US")
PS> [datetime]::Parse("05/31/2013 12:24:11",$usculture)
31 May 2013 12:24:11

The Globalization.cultureinfo class has a GetCultureInfo() method, so what we are doing above is we create a variable that is an instance of the “en-US” culture and then pass this variable to the Parse() method so it knows which format to parse the input in. Of course we could have also specified the whole thing on one line:

PS> [datetime]::Parse("05/31/2013 12:24:11",[Globalization.cultureinfo]::GetCultureInfo("en-US"))
31 May 2013 12:24:11

Let’s examine the Globalization.cultureinfo class. First, let’s examine its static members and properties:

PS> [Globalization.cultureinfo] | gm -Static
   TypeName: System.Globalization.CultureInfo
Name                            MemberType Definition
----                            ---------- ----------
CreateSpecificCulture           Method     static System.Globalization.CultureInfo CreateSpecificCulture(string name)
Equals                          Method     static bool Equals(System.Object objA, System.Object objB)
GetCultureInfo                  Method     static System.Globalization.CultureInfo GetCultureInfo(int culture), stat...
GetCultureInfoByIetfLanguageTag Method     static System.Globalization.CultureInfo GetCultureInfoByIetfLanguageTag(s...
GetCultures                     Method     static System.Globalization.CultureInfo[] GetCultures(System.Globalizatio...
ReadOnly                        Method     static System.Globalization.CultureInfo ReadOnly(System.Globalization.Cul...
ReferenceEquals                 Method     static bool ReferenceEquals(System.Object objA, System.Object objB)
CurrentCulture                  Property   static System.Globalization.CultureInfo CurrentCulture {get;}
CurrentUICulture                Property   static System.Globalization.CultureInfo CurrentUICulture {get;}
InstalledUICulture              Property   static System.Globalization.CultureInfo InstalledUICulture {get;}
InvariantCulture                Property   static System.Globalization.CultureInfo InvariantCulture {get;}

The GetCulture() method can be used to list all available cultures:

PS> [globalization.cultureinfo]::GetCultures("")
Cannot convert argument "0", with value: "", for "GetCultures" to type "System.Globalization.CultureTypes": "Cannot con
vert value "" to type "System.Globalization.CultureTypes" due to invalid enumeration values. Specify one of the followi
ng enumeration values and try again. The possible enumeration values are "NeutralCultures, SpecificCultures, InstalledW
in32Cultures, AllCultures, UserCustomCulture, ReplacementCultures, WindowsOnlyCultures, FrameworkCultures"."
At line:1 char:41
+ [globalization.cultureinfo]::GetCultures <<<< ("")
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
# the error message tells us what to pass to the method
PS> [globalization.cultureinfo]::GetCultures("allCultures")
LCID             Name             DisplayName
----             ----             -----------
1                ar               Arabic
2                bg               Bulgarian
3                ca               Catalan
4                zh-CHS           Chinese (Simplified)
5                cs               Czech
6                da               Danish
7                de               German
...

To get details about a specific culture use the GetCultureInfo() method:

PS> [Globalization.cultureinfo]::GetCultureInfo("en-US")
LCID             Name             DisplayName
----             ----             -----------
1033             en-US            English (United States)

The LCID corresponds to the Locale ID assigned by Microsoft. To see all the properties use the get-member cmdlet or format-list to see the values:

PS> [Globalization.cultureinfo]::GetCultureInfo("en-US") | gm
   TypeName: System.Globalization.CultureInfo
Name                           MemberType Definition
----                           ---------- ----------
ClearCachedData                Method     System.Void ClearCachedData()
Clone                          Method     System.Object Clone()
Equals                         Method     bool Equals(System.Object value)
GetConsoleFallbackUICulture    Method     System.Globalization.CultureInfo GetConsoleFallbackUICulture()
GetFormat                      Method     System.Object GetFormat(type formatType)
GetHashCode                    Method     int GetHashCode()
GetType                        Method     type GetType()
ToString                       Method     string ToString()
Calendar                       Property   System.Globalization.Calendar Calendar {get;}
CompareInfo                    Property   System.Globalization.CompareInfo CompareInfo {get;}
CultureTypes                   Property   System.Globalization.CultureTypes CultureTypes {get;}
DateTimeFormat                 Property   System.Globalization.DateTimeFormatInfo DateTimeFormat {get;set;}
DisplayName                    Property   System.String DisplayName {get;}
EnglishName                    Property   System.String EnglishName {get;}
...
PS> [Globalization.cultureinfo]::GetCultureInfo("en-US") | fl
Parent                         : en
LCID                           : 1033
KeyboardLayoutId               : 1033
Name                           : en-US
IetfLanguageTag                : en-US
DisplayName                    : English (United States)
NativeName                     : English (United States)
EnglishName                    : English (United States)
TwoLetterISOLanguageName       : en
ThreeLetterISOLanguageName     : eng
ThreeLetterWindowsLanguageName : ENU
CompareInfo                    : CompareInfo - 1033
TextInfo                       : TextInfo - 1033
IsNeutralCulture               : False
CultureTypes                   : SpecificCultures, InstalledWin32Cultures, FrameworkCultures
NumberFormat                   : System.Globalization.NumberFormatInfo
DateTimeFormat                 : System.Globalization.DateTimeFormatInfo
Calendar                       : System.Globalization.GregorianCalendar
OptionalCalendars              : {System.Globalization.GregorianCalendar, System.Globalization.GregorianCalendar}
UseUserOverride                : False
IsReadOnly                     : True

On the topic of dates, the DateTimeFormat property seems interesting:

PS> [Globalization.cultureinfo]::GetCultureInfo("en-US").DateTimeFormat
AMDesignator                     : AM
Calendar                         : System.Globalization.GregorianCalendar
DateSeparator                    : /
FirstDayOfWeek                   : Sunday
CalendarWeekRule                 : FirstDay
FullDateTimePattern              : dddd, MMMM dd, yyyy h:mm:ss tt
LongDatePattern                  : dddd, MMMM dd, yyyy
LongTimePattern                  : h:mm:ss tt
MonthDayPattern                  : MMMM dd
PMDesignator                     : PM
RFC1123Pattern                   : ddd, dd MMM yyyy HH':'mm':'ss 'GMT'
ShortDatePattern                 : M/d/yyyy
ShortTimePattern                 : h:mm tt
SortableDateTimePattern          : yyyy'-'MM'-'dd'T'HH':'mm':'ss
TimeSeparator                    : :
UniversalSortableDateTimePattern : yyyy'-'MM'-'dd HH':'mm':'ss'Z'
YearMonthPattern                 : MMMM, yyyy
AbbreviatedDayNames              : {Sun, Mon, Tue, Wed...}
ShortestDayNames                 : {Su, Mo, Tu, We...}
DayNames                         : {Sunday, Monday, Tuesday, Wednesday...}
AbbreviatedMonthNames            : {Jan, Feb, Mar, Apr...}
MonthNames                       : {January, February, March, April...}
IsReadOnly                       : True
NativeCalendarName               : Gregorian Calendar
AbbreviatedMonthGenitiveNames    : {Jan, Feb, Mar, Apr...}
MonthGenitiveNames               : {January, February, March, April...}

Notice the ShortDatePattern above. This is exactly what the -Format switch of Get-Date cmdlet expects, so one can use this to format the output of Get-Date in a different locale. Like thus:

PS> get-date
02 June 2013 12:33:36
PS> Get-Date -Format([Globalization.cultureinfo]::GetCultureInfo("en-US").DateTimeFormat.ShortDatePattern)
6/2/2013
PS> Get-Date -Format([Globalization.cultureinfo]::GetCultureInfo("zh-CN").DateTimeFormat.ShortDatePattern)
2013/6/2

So now we have seen to use the [datetime]::Parse() method to parse an input string into a DateTime object while specifying the country format of the input string. We have also seen how to output the result of get-date in the format of a different country. Both methods use the [Globalization.cultureinfo] class.

Lastly, there is an easy way to parse any string – irrespective of whether it matches the format of a country or not – into a DateTime object via the ParseExact() method. This is worth mentioning as without such a method one would have to resort to regular expressions and such!

In the example below we match an input string with time “02:50PM” by specifying the format as “hh:mmtt” to the ParseExact() method.

PS> [datetime]::ParseExact("02:50PM","hh:mmtt",$null)
02 June 2013 14:50:00

I am not sure what the third parameter of this method is supposed to be. Most examples put it as $null. It seems to take a “culture” as input, but I am not sure how it affects the output. Instead of $null the following too seem to work: $(get-culture), [Globalization.Cultureinfo]::GetCultureInfo("en-US") (“en-GB” too works), and [Globalization.Cultureinfo]::InvariantCulture.

The format can contain other text too. Escape the characters of such text with a slash or put the text within quotes ':

PS> [datetime]::ParseExact("At the beep the time will be 02:50PM","'At the beep the time will be' hh:mmtt",[Globalization.Cultureinfo]::GetCultureInfo("en-GB"))
02 June 2013 14:50:00

And just to show what happens if the format does not match:

PS> [datetime]::ParseExact("At the beep the time will be 02:50PM","'At the beep the time will be' hh:mmt",[Globalization.Cultureinfo]::GetCultureInfo("en-GB"))
Exception calling "ParseExact" with "3" argument(s): "String was not recognized as a valid DateTime."
At line:1 char:23
+ [datetime]::ParseExact <<<< ("At the beep the time will be 02:50PM","'At the beep the time will be' hh:mmt",[Globaliz
ation.Cultureinfo]::GetCultureInfo("en-GB"))
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

Thanks to these two posts for showing me ParseExact().


Viewing all articles
Browse latest Browse all 5

Latest Images

Trending Articles





Latest Images