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:
1 2 3 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
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:
1 |
... | 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:
1 2 3 4 5 6 7 |
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:
1 2 3 4 5 6 7 8 |
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:
1 2 3 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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):
1 2 3 4 |
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:
1 2 3 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
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:
1 2 3 4 5 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
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:
1 2 3 4 5 6 7 8 9 |
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.
1 2 3 |
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
'
:
1 2 3 |
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:
1 2 3 4 5 6 7 |
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 |