objectprogr
objectprogr

Reputation: 89

Parse string to datetime format

I'm getting all installed software on my computer, and print result to a HTML table. But a lot of dates have yyyyMMdd format, but I would like yyyy-MM-dd format.

Windows 10
Powershell version - 5.1.17

$Soft = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*;
#$Soft64 =  Get-ItemProperty HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*;
$InstallDate = $Soft | Select-Object InstallDate;

for ($i = 0; $i -lt $InstallDate.Length; $i++) {
    if ($InstallDate[$i] -match '-') {
        $InstallDate[$i]
    } else {
        $InstallDate = [DateTime]::ParseExact($InstallDate[$i], 'yyyyMMdd', $null)
        $InstallDate.ToString('yyyy-MM-dd')
    }
}

One date on list is with a good format (2019-04-01) or they are empty/null, and after execute script I'm getting this result:

InstallDate
-----------
2019-04-01

But dates which don't contain -, e.g. 20190319, throw errors:

Cannot find an overload for "ParseExact" and the argument count: "3".
At C:\Users\xxx\Desktop\xxx\ForLoopTest.ps1:12 char:9
+         $InstallDate = [datetime]::ParseExact($InstallDate[$i], 'yyyy ...
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodCountCouldNotFindBest

Cannot find an overload for "ToString" and the argument count: "1".
At C:\Users\xxx\Desktop\xxx\ForLoopTest.ps1:13 char:9
+         $InstallDate.ToString('yyyy-MM-dd')
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodCountCouldNotFindBest

Upvotes: 0

Views: 1162

Answers (2)

Ansgar Wiechers
Ansgar Wiechers

Reputation: 200293

You need to remove empty InstallDate results from the data you're parsing, and then expand the property, as @arco444 pointed out. Also, your loop currently assigns a parsed date back to the array variable over which your loop iterates, thus destroying all other values in that array.

Using InvariantCulture instead of $null, as @Moerwald suggested, is recommended, but not the cause of the issue. Another (performance-)optimization would be using the Contains() string method instead of the -match operator.

Something like this should do what you want:

$reg = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
$culture = [Globalization.CultureInfo]::InvariantCulture

$Soft = Get-ItemProperty $reg
$InstallDate = $Soft |
               Where-Object { $_.InstallDate } |
               Select-Object -Expand InstallDate

for ($i = 0; $i -lt $InstallDate.Length; $i++) {
    if ($InstallDate[$i].Contains('-')) {
        $InstallDate[$i]
    } else {
        $d = [DateTime]::ParseExact($InstallDate[$i], 'yyyyMMdd', $culture)
        $d.ToString('yyyy-MM-dd')
    }
}

Pipelining the entire processing is also a possibility:

$reg = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
$culture = [Globalization.CultureInfo]::InvariantCulture

Get-ItemProperty $reg | Where-Object {
    $_.InstallDate
} | Select-Object -Expand InstallDate | ForEach-Object {
    if ($_.Contains('-')) {
        $_
    } else {
        [DateTime]::ParseExact($_, 'yyyyMMdd', $culture).ToString('yyyy-MM-dd')
    }
}

Upvotes: 3

Moerwald
Moerwald

Reputation: 11254

You need to add a FormatProvider to specify culture when using ParseExact:

$dt = [datetime]::ParseExact("20190319", "yyyyMMdd", [System.Globalization.CultureInfo]::InvariantCulture)
$dt.ToString("yyyy-MM-dd")
2019-03-19

ParseExact throws an exception, therefore $InstallDate is invalid and you call ToString on it. You can add an additional $null check:

 $dt = [datetime]::ParseExact("20190319", "yyyyMMdd", [System.Globalization.CultureInfo]::InvariantCulture)
 if ($dt) {
   $dt.ToString("yyyy-MM-dd")
 }

Hope that helps

Upvotes: 0

Related Questions