I'm retrieving the Media Created column field from file explorer this way:
$mCreated = $shellfolder.GetDetailsOf($shellfile, 208);And that works fine. It gives me the string "5/7/2017 4:09 PM"
The Problem:
When I try to convert the string to formatted date I get this error:
Exception calling "ParseExact" with "3" argument(s): "String was not recognized as a valid DateTime."
At C:\Client\testVideos\anotherTest.ps1:28 char:9
+ $formattedDate = [Datetime]::ParseExact("$mCreated".Trim(), ' ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : FormatExceptionWhat I've tried:
Tried these individually and all give me the same error:
$formattedDate = [Datetime]::ParseExact("$mCreated".Trim(), 'yyyyMMddTHHmmss', $null)
$formattedDate = [Datetime]::ParseExact("$mCreated".Trim(), 'yyyyMMddTHHmmss', [Globalization.CultureInfo]::InvariantCulture)
# This line actually gives me a different error
$formattedDate = Get-Date "$mCreated".Trim() -Format "yyyyMMddTHHmmss"
# ERROR: Get-Date : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline inputWhat has worked:
I've also been getting the Created Date in the same code and formatting it the same way, and it works!
The Created Date is "2/12/2021 1:10 PM", "2/4/2021 3:39 PM", "2/2/2021 9:00 AM", or "2/4/2021 3:54 PM"
# This formats it successfully with no error
$createdDate = $shellfolder.GetDetailsOf($shellfile, 4);
$formattedDate = $createdDate | Get-Date -Format "yyyyMMddTHHmmss"Related but ultimately unhelpful:
62 Answers
I had the same issue in the past with various dates returned by the Folder.GetDetailsOf() method. I found the issue to be Unicode formatting characters
PS C:\> $TestFile = 'C:\Users\keith\Music\ABBA\Hits\03 Dancing Queen.wma'
>> $ofolder = $Shell.NameSpace((Split-Path $TestFIle))
>> $oFile = $oFolder.ParseName((Split-Path $TestFile -Leaf))
>>
>> $MediaCreated = $oFolder.GetDetailsOf($oFile, 208)
>> $MediaCreated
>> $MediaCreated.Length
>> [Int[]][Char[]]$MediaCreated -join '-'
>>
6/19/2010 11:54 PM
23
8206-54-47-8206-49-57-47-8206-50-48-49-48-32-8207-8206-49-49-58-53-52-32-80-77
PS C:\>>[DateTime]$MediaCreated
Cannot convert value "6/19/2010 11:54 PM" to type "System.DateTime". Error:
"String was not recognized as a valid DateTime."
At line:1 char:1 + [DateTime]$MediaCreated + ~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidArgument: (:) [], RuntimeException + FullyQualifiedErrorId : InvalidCastParseTargetInvocationWithFormatProviderMy solution was to "whitelist" valid DateTime characters in a regualr expression. So the regex matches for "good" characters are:
\wAny word character (0..9, 'A', 'P', and 'M')\:Colons (Time separator)/Slashes (Date separator)- Spaces
Separators may vary based on region. I'm hard-coding for U.S. for simplicity, brevity, and laziness.
These can be combined & enclosed in brackets to create a character class that will match valid characters.:
[\w\:/ ]
To invert the class, & match invalid characters, prepend a caret to the class definition:
[^\w\:/ ]
Then we can use the -Replace operator to remove them:
[DateTime]($MediaCreated -replace '[^\w\:/ ]','')
PS C:\> $MediaCreated = [DateTime]($MediaCreated -replace '[^\w\:/ ]','')
PS C:\>>$MediaCreated
Saturday, June 19, 2010 11:54:00 PM
PS C:\>>$MediaCreated.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True DateTime System.ValueTypeJust recently learned of the alternative: ShellFolderItem.ExtendedProperty()
Gets the value of a property from an item's property set. The property can be specified either by name or by the property set's format identifier (FMTID) and property identifier (PID).
Return value Type: Variant*
When this method returns, contains the value of the property, if it exists for the specified item. The value will have full typing—for example, dates are returned as dates, not strings.
This method returns a zero-length string if the property is valid but does not exist for the specified item, or an error code otherwise.
Remarks There are two ways to specify a property. The first is to assign the property's well-known name, such as "Author" or "Date", to sPropName. However, each property is a member of a Component Object Model (COM) property set and can also be identified by specifying its format ID (FMTID) and property ID (PID). An FMTID is a GUID that identifies the property set, and a PID is an integer that identifies a particular property within the property set.
Specifying a property by its FMTID/PID values is usually more efficient than using its name. To use a property's FMTID/PID values with ExtendedProperty, they must be combined into an SCID. An SCID is a string that contains the FMTID/PID values in the form "FMTID**PID", where the FMTID is the string form of the property set's GUID. For example, the SCID of the summary information property set's author property is "{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 4".
I couldn't find the proper name for "Media Created", but the * FMTID/PID* pair is "{2E4B640D-5019-46D8-8881-55414CC5CAA0}100":
PS C:\>>$oFile.ExtendedProperty("MediaCreated")
PS C:\> $oFile.ExtendedProperty("Media Created")
PS C:\> $oFile.ExtendedProperty("MediaEncoded")
PS C:\> $oFile.ExtendedProperty("MediaCreationDate")
PS C:\> $oFile.ExtendedProperty("System.MediaEncoded")
PS C:\>>$oFile.ExtendedProperty("{2E4B640D-5019-46D8-8881-55414CC5CAA0}100")
Sunday, June 20, 2010 04:54:26 AM
PS C:\>>($oFile.ExtendedProperty("{2E4B640D-5019-46D8-8881-55414CC5CAA0}100")).GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True DateTime System.ValueType 3 Continuing from my comment, btw, you could have just done this as well for formatting.
($mCreated = (Get-Date -Format g).ToString())
# Results
<#
2/16/2021 11:10 AM
#>
($mCreated).GetType() |
Format-Table -AutoSize
# Results
<#
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
#>
$mCreated |
Get-Member
# Results
<#
TypeName: System.String
Name MemberType Definition
---- ---------- ----------
...
Chars ParameterizedProperty char Chars(int index) {get;}
Length Property int Length {get;}
#> Cast a date string as datetime object
([DateTime]$mCreated = ((Get-Date -Format g).ToString()))
# Results
<#
Tuesday, February 16, 2021 11:12:00 AM
#>
($mCreated).GetType() |
Format-Table -AutoSize
# Results
<#
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True DateTime System.ValueType
#>
$mCreated |
Get-Member
# Results
<#
TypeName: System.DateTime
Name MemberType Definition
---- ---------- ----------
...
ToUniversalTime Method datetime ToUniversalTime()
Date Property datetime Date {get;}
Day Property int Day {get;}
DayOfWeek Property System.DayOfWeek DayOfWeek {get;}
DayOfYear Property int DayOfYear {get;}
Hour Property int Hour {get;}
Kind Property System.DateTimeKind Kind {get;}
Millisecond Property int Millisecond {get;}
Minute Property int Minute {get;}
Month Property int Month {get;}
Second Property int Second {get;}
Ticks Property long Ticks {get;}
TimeOfDay Property timespan TimeOfDay {get;}
Year Property int Year {get;}
DateTime ScriptProperty System.Object DateTime {get=if ((& { Set-StrictMode -Version 1; $this.DisplayHint }) -ieq "Date")...
#>Details and more here:
As far as a string with parseexact. It is impacted by how your system date is set up and that yeat must be at the beginning or end of the timestamp. See the details here:
[datetime]::ParseExact("20181010134412",'yyyyMMddHHmmss',$null)
# Results
<#
Wednesday, October 10, 2018 1:44:12 PM
#>
($mCreated).GetType() |
Format-Table -AutoSize
# Results
<#
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
#>
[datetime]::ParseExact($mCreated,'yyyyMMddHHmmss',$null)
[datetime]::ParseExact($($mCreated),'yyyyMMddHHmmss',$null)
# Results
<#
Exception calling "ParseExact" with "3" argument(s): "String was not recognized as a valid DateTime."
#>
$mCreated
# Results
<#
2/16/2021 11:24 AM
#>
[datetime]::ParseExact('20210216112400','yyyyMMddHHmmss',$null)
[datetime]::ParseExact('02161124002021','MMddHHmmssyyyy',$null)
# Results
<#
Tuesday, February 16, 2021 11:24:00 AM
Tuesday, February 16, 2021 11:24:00 AM
#>Or you end up here:
$mCreated = '16/02/2021 9:11 AM'
([datetime]::ParseExact($mCreated,'dd/MM/yyyy h:mm tt',$null))
$mCreated = '02/16/2021 9:11 AM'
([datetime]::ParseExact($mCreated,'MM/dd/yyyy h:mm tt',$null))
# Results
<#
Tuesday, February 16, 2021 9:11:00 AM
Tuesday, February 16, 2021 9:11:00 AM
#>See the details here:
Unprintable/invisible characters is the issue when trying to use the date string from the call.
$FileInformation = Get-ItemProperty -Path 'F:\Videos\2020 03 19 Competitive Intel.mp4'
$ShellApplication = New-Object -ComObject Shell.Application
$ShellFolder = $ShellApplication.Namespace($FileInformation.Directory.FullName)
$ShellFile = $ShellFolder.ParseName($FileInformation.Name)
$MetaDataProperties = [ordered] @{}
0..400 |
ForEach-Object -Process { $DataValue = $ShellFolder.GetDetailsOf($null, $_) $PropertyValue = (Get-Culture).TextInfo.ToTitleCase($DataValue.Trim()).Replace(' ', '') if ($PropertyValue -ne '') {$MetaDataProperties["$_"] = $PropertyValue}
}
$MetaDataProperties.Values -Match 'media'
# Results
<#
MediaCreated
#>
###
($MediaCreated = $ShellFolder.GetDetailsOf($ShellFile , 208))
($DateCreated = $ShellFolder.GetDetailsOf($ShellFile , 4))
($MediaCreated).GetType()
($DateCreated).GetType()
# Results
<#
19-Mar-20 17:30
03-Dec-20 13:05
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
True True String System.Object
#>
($TestDate = Get-Date(Date) -Format g)
($TestDate1 = Get-Date(Date) -Format g) -replace '-|\s|:'
# Results
<#
17-Feb-21 16:41
17Feb211641
#>
([datetime]::ParseExact($TestDate,'dd-MMM-yy HH:mm',$null))
([datetime]::ParseExact($TestDate1,'dd-MMM-yy HH:mm',$null))
# Results
<#
Wednesday, 17 February, 2021 16:54:00
Wednesday, 17 February, 2021 16:54:00
#>
(('17-Feb-21 16:41').ToCharArray()).Count
# Results
<#
15
#>
([datetime]::ParseExact($MediaCreated,'dd-MMM-yy HH:mm',$null))
# Results
<#
Exception calling "ParseExact" with "3" argument(s): "String was not recognized
as a valid DateTime."
#>In regular cmd.exe (not cmd.exe from PowerShell (ISE/consolehost) or via Windows Terminal), there are unprintable/invisible characters that do not appear in Notepad/consolehost/ISE/VSCode/Windows Terminal.
(($MediaCreated).ToCharArray()).Count
# Results
<#
20 # multiple hidden/non-printable characters
#>
($MediaCreated).ToCharArray()
# Results
<#
1
9
-
M
a
r
-
2
0
1
7
:
3
0
#>
(($MediaCreated).ToCharArray())[0]
(($MediaCreated).ToCharArray())[1]
(($MediaCreated).ToCharArray())[2]
(($MediaCreated).ToCharArray())[3]
# Results
<#
1
9
-
#>
($MediaCreated).ToCharArray() |
ForEach{ [int][char]$PSItem }
# Results
<#
8206
49
57
45
8206
77
97
114
45
8206
50
48
32
8207
8206
49
55
58
51
48
#>Hence why Keiths' regex works...
([datetime]::ParseExact(($MediaCreated -replace '[^\w\:/ ]'),'ddMMMyy HH:mm',$null))
# Results
<#
Thursday, 19 March, 2020 17:30:00
#>... and thus we can go get this more directly, to allow parseexact to work, and not have to change the data type:
$FileInformation = Get-ItemProperty -Path 'F:\Videos\2020 03 19 Competitive Intel.mp4'
$ShellApplication = New-Object -ComObject Shell.Application
$ShellFolder = $ShellApplication.Namespace($FileInformation.Directory.FullName)
$ShellFile = $ShellFolder.ParseName($FileInformation.Name)
($MediaCreated = $ShellFolder.GetDetailsOf($ShellFile , 208))
($DateCreated = $ShellFolder.GetDetailsOf($ShellFile , 4))
# Results
<#
19-Mar-20 17:30
03-Dec-20 13:05
#>
($MediaCreated).GetType()
($DateCreated).GetType()
# Results
<#
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
True True String System.Object
#>
([datetime]::ParseExact(($MediaCreated -replace '[^\w\:/ ]'),'ddMMMyy HH:mm',$null))
([datetime]::ParseExact(($DateCreated -replace '[^\w\:/ ]'),'ddMMMyy HH:mm',$null))
# Results
<#
Thursday, 19 March, 2020 17:30:00
Thursday, 3 December, 2020 13:05:00
#>
($MediaCreated).GetType()
($DateCreated).GetType()
# Results
<#
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
True True String System.Object
#> 1