I started a little, pet project recently and it’s all based around how I store my pictures and video files. I have a base directory – C:\Users\tommymaynard\Documents\Media – that contains a subdirectory for each year: 2012, 2013, etc. Inside each of those, I have 12 other directories – one for each month. These each use the naming convention 01 – January, 02 – February, etc. Inside each of those directories, I have at least two other directories: pictures and videos. Here’s an image that may help this structure make more sense.
I don’t always sort my photos and videos right away. Instead, I’ll often procrastinate and just drop them into a ‘Pics to Sort’ folder on my desktop – that’s literally the directory’s name. I wondered if I would procrastinate any less, if I had Windows PowerShell build out the desired directory structure for me each year. As an aside, if you consider that I always add my pictures and videos to the same I’ll-sort-you-later directory, then I could easily build a solution to do the sorting, based on file extension and creation date, from this directory into the proper subdirectories in the media directory. I’ll save this one for another day, but thanks for thinking of it.
For this Quick Learn, let’s only concern ourselves with capturing the month’s names. While I could have created a hard-coded array of the month names, I decided to see what I could get return using Get-Date – not what I wanted. I could return the current month’s name using Get-Date -Format MMMM, or the current month’s numeric representation using Get-Date | Select-Object Month, but neither of these return all of the months. The system knows the names of the months and so I opted to look for a .NET way to get them.
So, here it is, the System.Globalization.DateTimeFormatInfo .NET class. When you enter this as the value of the New-Object’s -TypeName parameter, it will return a great deal of information, to include a property called MonthNames. In the example below, you can see the code used to store the month names in a variable. You would think I would be done here, but I wasn’t.
PS C:\> $MonthNames = (New-Object System.Globalization.DateTimeFormatInfo).MonthNames PS C:\> $MonthNames January February March April May June July August September October November December PS C:\>
Instead of creating directories, like my project does, the example below simply echos the month’s numeric value, a space-dash-space, and then the month’s name. I’ve added a for statement example as well, just in case someone were to wonder why I didn’t include one, since I was incrementing a counter variable and knew how many looping iterations I had to do.
PS C:\> $i = 1 PS C:\> $MonthNames | foreach {Write-Output "$i - $_";$i++} 1 - January 2 - February 3 - March 4 - April 5 - May 6 - June 7 - July 8 - August 9 - September 10 - October 11 - November 12 - December 13 - PS C:\> PS C:\> for ($i=1; $i -le $MonthNames.Count; $i++) {echo "$i - $($MonthNames[$i-1])"} 1 - January 2 - February 3 - March 4 - April 5 - May 6 - June 7 - July 8 - August 9 - September 10 - October 11 - November 12 - December 13 - PS C:\>
Based on these results, the MonthNames property has a thirteenth entry, and that’s a problem, since there’s no thirteenth month. I proved to myself that there was a problem a couple different ways, as seen below. Remember that the first index, or element, of an array is index zero, making January $MonthNames[0] and December $MonthNames[11].
PS C:\> $MonthNames.Count 13 PS C:\> $MonthNames[0] January PS C:\> $MonthNames[11] December PS C:\> $MonthNames[12] PS C:\> $MonthNames[13] PS C:\>
I needed to ensure this extra, blank month wasn’t making its way into my code. While I could have done my filtering when I was actually creating new directories, it is a better practice to only store what I need, and to only store what’s correct. As can be seen below, I only returned twelve months by adding a pipeline and some filtering.
PS C:\> $MonthNames = (New-Object System.Globalization.DateTimeFormatInfo).MonthNames | Where-Object -FilterScript {$_ -notlike ''} PS C:\> $MonthNames January February March April May June July August September October November December PS C:\> $MonthNames.Count 12
Since I was already here, I checked out another property. DayNames didn’t have a blank entry, and had I needed to use it, I wouldn’t have had to do any filtering on the returned results – something I incorrectly assumed about MonthNames. Hope this is helpful to someone, and thank you for reading this post.
PS C:\> $DayNames = (New-Object System.Globalization.DateTimeFormatInfo).DayNames PS C:\> $DayNames Sunday Monday Tuesday Wednesday Thursday Friday Saturday PS C:\> $DayNames.Count 7 PS C:\>
Hi Tommy,
thanks for sharing, I never came across that one but it will definitely same me some time in case I do. “Googling” a bit around I found that this is done for the purpose of consistency accounting for the fact that in some calendars (like Hebrew) there are years with 13 months (like in Gregorian 2014):
$cal = New-Object Globalization.HebrewCalendar
$cal.GetMonthsInYear($cal.GetYear((Get-Date -Year 2014)))