Powershell Registry String to Workable Date - powershell

Cannot convert string format to date using parse exact.
Registry contains the following string value:
2022-10-18T12:40:25
I need to convert this string to a date field in order to count number of days since (compared to today).
$startdate =
Get-ItemProperty -Path 'HKLM:SOFTWARE\Adobe\Acrobat Distiller\DC\Installer\' |
select-object 'Dummy'
[datetime]::parseexact($startdate,'dd/MM/yyyy HH:mm',$null)
$today = (([datetime]::Now))
$x = New-TimeSpan -Start $startdate -End $today
"$($x.days) $("days have passed since") $($startdate)"
Cannot find an overload for "parseexact" and the argument count: "3".
At line:2 char:1
+ [datetime]::parseexact($startdate,'dd/MM/yyyy HH:mm',$null)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
New-TimeSpan : Cannot bind parameter 'Start'. Cannot convert value "#{dummy=2022-10-18T12:40:25}" to type "System.DateTime". Error: "Cannot convert the "#{dummy=2022-10-18T12:40:25}" value of type
"Selected.System.Management.Automation.PSCustomObject" to type "System.DateTime"."
At line:5 char:26
+ $x = New-TimeSpan -Start $startdate -End $today
+ ~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [New-TimeSpan], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.NewTimeSpanCommand
days have passed since #{dummy=2022-10-18T12:40:25}

Your primary problem is that you need to use -ExpandProperty, if you use Select-Object, or you can use simple property access:
Select-Object (select) by default returns a [pscustomobject] instance that has the requested properties - even when you're only asking for a single property. To get only that property's value, use the -ExpandProperty parameter instead of the (possibly positionally implied) -Property parameter - see this answer for details and alternatives, notably the ability to simply use (...).SomeProperty
Therefore, the simplest solution is:
$startdate = (
Get-ItemProperty 'HKLM:SOFTWARE\Adobe\Acrobat Distiller\DC\Installer'
).Dummy
Or, in PSv5+, using Get-ItemPropertyValue:
$startdate =
Get-ItemPropertyValue 'HKLM:SOFTWARE\Adobe\Acrobat Distiller\DC\Installer' Dummy
As for then parsing the resulting string into a [datetime] instance:
The format string you're passing to ::ParseExact(), doesn't match your input string, and TheMadTechnician's answer shows how to fix that (see this answer's bottom section for further considerations).
However, you can more simply use a [datetime] cast, which recognizes your string format as-is, irrespective of what culture is currently in effect:
[datetime] '2022-10-18T12:40:25'
In essence, PowerShell translates the above into the following ::Parse() call:
[datetime]::Parse('2022-10-18T12:40:25', [cultureinfo]::InvariantCulture)
In general, for full robustness when using format strings, it is best to escape :, /, and . (if present) with \, so as if they are to be interpreted literally, i.e by only matching literally during parsing and being included literally when formatting output (by default, they're considered placeholders for the target culture's separators):
[datetime]::ParseExact(
'2022-10-18T12:40:25',
'yyyy-MM-ddTHH\:mm\:ss', # \-escaping ensures literal matching
[cultureinfo]::InvariantCulture
)
Note: In this particular case, [cultureinfo]::InvariantCulture alone would be sufficient for robustness, given that this culture uses : as the time separator.

You get an error because your second argument doesn't match the format of the datetime.
[datetime]::parseexact($startdate,'yyyy-MM-ddTHH:mm:ss',$null)

Related

PowerShell Function doesn't work consistently [duplicate]

I have a script which is using the EXIF data from a JPG file to find the DateTaken value. Once found, place the data in $Year and $Month variables.
$objShell = New-Object -ComObject Shell.Application
$folders = (Get-ChildItem G:\ServerFolders\Photos\UnSorted\ -Directory -Recurse -force).FullName
foreach ($Folder in $folders) {
$objfolder = $objShell.Namespace($folder)
foreach ($file in $objFolder.Items()) {
if ($objfolder.GetDetailsOf($file, 156) -eq ".jpg") {
$yeartaken = ($objFolder.GetDetailsOf($File, 12)).Split("/")[2].Split(" ")[0]
$month = $objFolder.GetDetailsOf($File, 12).Split("/")[1]
$monthname = (Get-Culture).DateTimeFormat.GetMonthName($month)
Write-Host $file.Name
}
}
}
So, if the file has a DateTaken as 06/10/2016, $yeartaken is 2016 and $month is 10
I then to Get-Culture to convert the 10 into October. This doesn't work because it's seeing $month as a string.
Cannot convert argument "month", with value: "‎10", for "GetMonthName" to
type "System.Int32": "Cannot convert value "‎10" to type "System.Int32".
Error: "Input string was not in a correct format.""
At line:1 char:3
+ (Get-Culture).DateTimeFormat.GetMonthName($month)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
I've tried to convert it to a Integer value using casting or converting, but for some reason it won't convert.
PS> [int]$Test = $month
Cannot convert value "‎10" to type "System.Int32". Error: "Input string was
not in a correct format."
At line:1 char:1
+ [int]$Test = $month
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException
+ FullyQualifiedErrorId : RuntimeException
A better approach to working with dates is to convert the date string to an actual DateTime object, which provides all the information you're looking for:
$culture = [Globalization.CultureInfo]::InvariantCulture
$pattern = 'dd\/MM\/yyyy'
$datestring = $objFolder.GetDetailsOf($File, 12).Split(' ')[0]
$datetaken = [DateTime]::ParseExact($datestring, $pattern, $culture)
$year = $datetaken.Year
$month = $datetaken.Month # month (numeric)
$monthname = $datetaken.ToString('MMMM') # month name
Assuming that the date is followed by a time in the format HH:mm:ss you could extend the code to handle the time as well:
$culture = [Globalization.CultureInfo]::InvariantCulture
$pattern = 'dd\/MM\/yyyy HH:mm:ss'
$datestring = $objFolder.GetDetailsOf($File, 12)
$timestamp = [DateTime]::ParseExact($datestring, $pattern, $culture)
$year = $timestamp.Year
$month = $timestamp.Month # month (numeric)
$monthname = $timestamp.ToString('MMMM') # month name
$hour = $timestamp.Hour
...
The date-and-time string returned by $objFolder.GetDetailsOf($File, 12) contains invisible control characters.
You can strip them as follows, after which your code should work:
# Remove the formatting control characters from the string.
$dateTimeStr = $objFolder.GetDetailsOf($File, 12) -replace '\p{Cf}'
# Note: The date format shown is directly recognized by
# PowerShell's [datetime] casts, which use the invariant culture.
$dateTaken = [datetime] $dateTimeStr
$monthname = (Get-Culture).DateTimeFormat.GetMonthName($dateTaken.Month)
\p{Cf} matches characters in the Format Unicode category, which comprises any character that "affects the layout of text or the operation of text processes, but is not normally rendered."
Specifically, in my test file's metadata I found multiple instances of Unicode control characters U+200E (LEFT-TO-RIGHT MARK) and U+200F (RIGHT-TO-LEFT MARK).
As for why these invisible control characters are present (information from this forum post, courtesy of mclayton):
The characters that you are seeing (along with some others, such as nulls) are embedded in BSTRs to allow the calling function to correctly display the string for any locale. It includes such things as the left to right marker that you saw, so that the calling application knows that the characters must be grouped and displayed left to right.
However, it's not quite clear why an explicit direction marker is needed for the default direction, left-to-right, and why seemingly each date component is preceded by one, and the time component even by two; here's an example of what should just be date string 9/5/2015 11:32 AM, with the invisible control characters visualized as PowerShell (Core) 7+ Unicode escape sequences (via Debug-String):
`u{200e}9/`u{200e}5/`u{200e}2015·`u{200f}`u{200e}11:32·AM
As an aside, on a general note: Ansgar Wiecher's answer shows more robust date-time string parsing techniques.
Self-contained sample:
The following example extracts the "Date Taken" field from file test.jpg located in the current folder and converts it to a [datetime] instance - note the need to use .ParseName() in order to pass an object representing the target file that the .GetDetailsOf() method understands:
[datetime] (
($folder = (New-Object -ComObject Shell.Application).
Namespace($PWD.ProviderPath)).
GetDetailsOf($folder.ParseName('test.jpg'), 12) -replace '\p{Cf}'
)

Powershell Source Code Encoding / hidden characters [duplicate]

I have a script which is using the EXIF data from a JPG file to find the DateTaken value. Once found, place the data in $Year and $Month variables.
$objShell = New-Object -ComObject Shell.Application
$folders = (Get-ChildItem G:\ServerFolders\Photos\UnSorted\ -Directory -Recurse -force).FullName
foreach ($Folder in $folders) {
$objfolder = $objShell.Namespace($folder)
foreach ($file in $objFolder.Items()) {
if ($objfolder.GetDetailsOf($file, 156) -eq ".jpg") {
$yeartaken = ($objFolder.GetDetailsOf($File, 12)).Split("/")[2].Split(" ")[0]
$month = $objFolder.GetDetailsOf($File, 12).Split("/")[1]
$monthname = (Get-Culture).DateTimeFormat.GetMonthName($month)
Write-Host $file.Name
}
}
}
So, if the file has a DateTaken as 06/10/2016, $yeartaken is 2016 and $month is 10
I then to Get-Culture to convert the 10 into October. This doesn't work because it's seeing $month as a string.
Cannot convert argument "month", with value: "‎10", for "GetMonthName" to
type "System.Int32": "Cannot convert value "‎10" to type "System.Int32".
Error: "Input string was not in a correct format.""
At line:1 char:3
+ (Get-Culture).DateTimeFormat.GetMonthName($month)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
I've tried to convert it to a Integer value using casting or converting, but for some reason it won't convert.
PS> [int]$Test = $month
Cannot convert value "‎10" to type "System.Int32". Error: "Input string was
not in a correct format."
At line:1 char:1
+ [int]$Test = $month
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException
+ FullyQualifiedErrorId : RuntimeException
A better approach to working with dates is to convert the date string to an actual DateTime object, which provides all the information you're looking for:
$culture = [Globalization.CultureInfo]::InvariantCulture
$pattern = 'dd\/MM\/yyyy'
$datestring = $objFolder.GetDetailsOf($File, 12).Split(' ')[0]
$datetaken = [DateTime]::ParseExact($datestring, $pattern, $culture)
$year = $datetaken.Year
$month = $datetaken.Month # month (numeric)
$monthname = $datetaken.ToString('MMMM') # month name
Assuming that the date is followed by a time in the format HH:mm:ss you could extend the code to handle the time as well:
$culture = [Globalization.CultureInfo]::InvariantCulture
$pattern = 'dd\/MM\/yyyy HH:mm:ss'
$datestring = $objFolder.GetDetailsOf($File, 12)
$timestamp = [DateTime]::ParseExact($datestring, $pattern, $culture)
$year = $timestamp.Year
$month = $timestamp.Month # month (numeric)
$monthname = $timestamp.ToString('MMMM') # month name
$hour = $timestamp.Hour
...
The date-and-time string returned by $objFolder.GetDetailsOf($File, 12) contains invisible control characters.
You can strip them as follows, after which your code should work:
# Remove the formatting control characters from the string.
$dateTimeStr = $objFolder.GetDetailsOf($File, 12) -replace '\p{Cf}'
# Note: The date format shown is directly recognized by
# PowerShell's [datetime] casts, which use the invariant culture.
$dateTaken = [datetime] $dateTimeStr
$monthname = (Get-Culture).DateTimeFormat.GetMonthName($dateTaken.Month)
\p{Cf} matches characters in the Format Unicode category, which comprises any character that "affects the layout of text or the operation of text processes, but is not normally rendered."
Specifically, in my test file's metadata I found multiple instances of Unicode control characters U+200E (LEFT-TO-RIGHT MARK) and U+200F (RIGHT-TO-LEFT MARK).
As for why these invisible control characters are present (information from this forum post, courtesy of mclayton):
The characters that you are seeing (along with some others, such as nulls) are embedded in BSTRs to allow the calling function to correctly display the string for any locale. It includes such things as the left to right marker that you saw, so that the calling application knows that the characters must be grouped and displayed left to right.
However, it's not quite clear why an explicit direction marker is needed for the default direction, left-to-right, and why seemingly each date component is preceded by one, and the time component even by two; here's an example of what should just be date string 9/5/2015 11:32 AM, with the invisible control characters visualized as PowerShell (Core) 7+ Unicode escape sequences (via Debug-String):
`u{200e}9/`u{200e}5/`u{200e}2015·`u{200f}`u{200e}11:32·AM
As an aside, on a general note: Ansgar Wiecher's answer shows more robust date-time string parsing techniques.
Self-contained sample:
The following example extracts the "Date Taken" field from file test.jpg located in the current folder and converts it to a [datetime] instance - note the need to use .ParseName() in order to pass an object representing the target file that the .GetDetailsOf() method understands:
[datetime] (
($folder = (New-Object -ComObject Shell.Application).
Namespace($PWD.ProviderPath)).
GetDetailsOf($folder.ParseName('test.jpg'), 12) -replace '\p{Cf}'
)

Unable to convert a string to an integer variable from DateTaken attribute on a JPG file

I have a script which is using the EXIF data from a JPG file to find the DateTaken value. Once found, place the data in $Year and $Month variables.
$objShell = New-Object -ComObject Shell.Application
$folders = (Get-ChildItem G:\ServerFolders\Photos\UnSorted\ -Directory -Recurse -force).FullName
foreach ($Folder in $folders) {
$objfolder = $objShell.Namespace($folder)
foreach ($file in $objFolder.Items()) {
if ($objfolder.GetDetailsOf($file, 156) -eq ".jpg") {
$yeartaken = ($objFolder.GetDetailsOf($File, 12)).Split("/")[2].Split(" ")[0]
$month = $objFolder.GetDetailsOf($File, 12).Split("/")[1]
$monthname = (Get-Culture).DateTimeFormat.GetMonthName($month)
Write-Host $file.Name
}
}
}
So, if the file has a DateTaken as 06/10/2016, $yeartaken is 2016 and $month is 10
I then to Get-Culture to convert the 10 into October. This doesn't work because it's seeing $month as a string.
Cannot convert argument "month", with value: "‎10", for "GetMonthName" to
type "System.Int32": "Cannot convert value "‎10" to type "System.Int32".
Error: "Input string was not in a correct format.""
At line:1 char:3
+ (Get-Culture).DateTimeFormat.GetMonthName($month)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
I've tried to convert it to a Integer value using casting or converting, but for some reason it won't convert.
PS> [int]$Test = $month
Cannot convert value "‎10" to type "System.Int32". Error: "Input string was
not in a correct format."
At line:1 char:1
+ [int]$Test = $month
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException
+ FullyQualifiedErrorId : RuntimeException
A better approach to working with dates is to convert the date string to an actual DateTime object, which provides all the information you're looking for:
$culture = [Globalization.CultureInfo]::InvariantCulture
$pattern = 'dd\/MM\/yyyy'
$datestring = $objFolder.GetDetailsOf($File, 12).Split(' ')[0]
$datetaken = [DateTime]::ParseExact($datestring, $pattern, $culture)
$year = $datetaken.Year
$month = $datetaken.Month # month (numeric)
$monthname = $datetaken.ToString('MMMM') # month name
Assuming that the date is followed by a time in the format HH:mm:ss you could extend the code to handle the time as well:
$culture = [Globalization.CultureInfo]::InvariantCulture
$pattern = 'dd\/MM\/yyyy HH:mm:ss'
$datestring = $objFolder.GetDetailsOf($File, 12)
$timestamp = [DateTime]::ParseExact($datestring, $pattern, $culture)
$year = $timestamp.Year
$month = $timestamp.Month # month (numeric)
$monthname = $timestamp.ToString('MMMM') # month name
$hour = $timestamp.Hour
...
The date-and-time string returned by $objFolder.GetDetailsOf($File, 12) contains invisible control characters.
You can strip them as follows, after which your code should work:
# Remove the formatting control characters from the string.
$dateTimeStr = $objFolder.GetDetailsOf($File, 12) -replace '\p{Cf}'
# Note: The date format shown is directly recognized by
# PowerShell's [datetime] casts, which use the invariant culture.
$dateTaken = [datetime] $dateTimeStr
$monthname = (Get-Culture).DateTimeFormat.GetMonthName($dateTaken.Month)
\p{Cf} matches characters in the Format Unicode category, which comprises any character that "affects the layout of text or the operation of text processes, but is not normally rendered."
Specifically, in my test file's metadata I found multiple instances of Unicode control characters U+200E (LEFT-TO-RIGHT MARK) and U+200F (RIGHT-TO-LEFT MARK).
As for why these invisible control characters are present (information from this forum post, courtesy of mclayton):
The characters that you are seeing (along with some others, such as nulls) are embedded in BSTRs to allow the calling function to correctly display the string for any locale. It includes such things as the left to right marker that you saw, so that the calling application knows that the characters must be grouped and displayed left to right.
However, it's not quite clear why an explicit direction marker is needed for the default direction, left-to-right, and why seemingly each date component is preceded by one, and the time component even by two; here's an example of what should just be date string 9/5/2015 11:32 AM, with the invisible control characters visualized as PowerShell (Core) 7+ Unicode escape sequences (via Debug-String):
`u{200e}9/`u{200e}5/`u{200e}2015·`u{200f}`u{200e}11:32·AM
As an aside, on a general note: Ansgar Wiecher's answer shows more robust date-time string parsing techniques.
Self-contained sample:
The following example extracts the "Date Taken" field from file test.jpg located in the current folder and converts it to a [datetime] instance - note the need to use .ParseName() in order to pass an object representing the target file that the .GetDetailsOf() method understands:
[datetime] (
($folder = (New-Object -ComObject Shell.Application).
Namespace($PWD.ProviderPath)).
GetDetailsOf($folder.ParseName('test.jpg'), 12) -replace '\p{Cf}'
)

PowerShell - Add-Content- Unable to add multiple vars to a file

I'm trying to add an expression to a log file which contains Date,Time some data separated by ";". Unfortunately I get an error every time I change the position of the items in the -value brackets.
Whats seems to be wrong?
This is the code :
Add-Content -path C:\...\outlog.txt -Value($Date + ';' + $Time + ';Checked;' + $strFileName)
This is the error :
Cannot convert argument "1", with value: ";", for "op_Addition" to type "System.TimeSpan": "Cannot convert
value ";" to type "System.TimeSpan". Error: "String was not recognized as a valid TimeSpan.""
At C:\...\Untitled1.ps1:8 char:64
+ ... \outlog.txt -Value($($Date + ';' + $Time + ';'+ $str))
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
Try this -
Add-Content -path C:\...\outlog.txt -Value("$Date; $Time; Checked; $strFileName")
If you look at get-help Add-Content -full, and look at the -value parameter, you will see -
-Value <Object[]>
Specifies the content to be added. Type a quoted string, such as "This data is for internal use only", or
specify an object that contains content, such as the DateTime object that Get-Date generates.
You cannot specify the contents of a file by typing its path, because the path is just a string, but you can
use a Get-Content command to get the content and pass it to the Value parameter.
Required? true
Position? 1
Default value None
Accept pipeline input? True (ByPropertyName, ByValue)
Accept wildcard characters? false
It says that it expects a quoted string or an object that contains content. It was missing in your case and hence the + operator was trying to add $date and time.

How to extract a certain part of a string in powershell

I want to extract the last 4 digits before ".txt" from this string:
09/14/2017 12:00:27 - mtbill_post_201709141058.txt 7577_Delivered: OK
Those represent the time at which that log was created and I want to display it as 10:58. I read from a file that has multiple lines similar to the one displayed.
Get-Content file.txt | ForEach-Object {
$splitUp = $_ -split "_"
$SC=$splitUp[2] -split "_"
Write-Host $SC
$len = $SC.Length
$folder2 = $SC.Substring($len - 12, 42)
}
I tried separating the string by "_" and then counting the characters in the obtained string and tried separating by the "Substring" command, but I receive the following error.
Exception calling "Substring" with "2" argument(s): "StartIndex cannot
be less than zero. Parameter name: startIndex"
At line:6 char:5
+ $folder2 = $SC.Substring($len - 12, 42)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : NotSpecified: (:) [], MethodInvocationException
FullyQualifiedErrorId : ArgumentOutOfRangeException
You can use a regex "lookahead".
What you are searching for is a set of four digits followed by ".txt":
$string = "09/14/2017 12:00:27 - mtbill_post_201709141058.txt 7577_Delivered: OK"
$regex = "\d{4}(?=\.txt)"
[regex]::matches($string, $regex).value
There's probably a more elegant solution:
$String = '09/14/2017 12:00:27 - mtbill_post_201709141058.txt 7577_Delivered: OK'
$String -Match '.*(?=\.txt)' | Out-Null
$Match = $Matches[0][-4..-1] -Join ''
$Time = [DateTime]::ParseExact($Match, 'HHmm',[CultureInfo]::InvariantCulture)
$Time.ToShortTimeString()
Uses RegEx to get all of the string before the .txt
Uses the Array Index to get the characters from 4th to last to the last character and joins them together as a single string.
Casts the value as a DateTime object using ParseExact to interpret it as 24 hour time code
Outputs the Short Date value of that DateTime object.
Just do it with Substring and IndexOf:
$string="09/14/2017 12:00:27 - mtbill_post_201709141058.txt 7577_Delivered: OK"
$string.Substring($string.IndexOf('.txt')-4, 4)