KMOR87
KMOR87

Reputation: 37

Powershell - compare date with CSV date

I am trying to compare a date entered with a date from a CSV and display a message if the date entered is before the date in the CSV.

Am having difficulty, unsure if I need to convert the date in the CSV from string for it to be compared correctly.

Have got this far with the code:

$CSV=Import-Csv C:\Users\person\Desktop\date.csv 
$InputDate = Get-Date -Format "MM/dd/yyyy" (Read-Host -Prompt 'Enter the date')
$CreatedDate = Get-Date -Format "MM/dd/yyyy HH:mm" $CSV.updated

if($InputDate -gt $CreatedDate) {
  write-host "Input Newer"
}
else {
  write-host "Input Older"
}

The comparison doesn't seem to work correctly.

CSV formatted as below (contains a single row, so $CSV.updated can be assumed to contain a single date string):

updated
11/07/2016 16:14

Upvotes: 1

Views: 751

Answers (2)

mklement0
mklement0

Reputation: 437548

Kiran Patnayakuni's answer provides an effective solution; let me make it clearer what the problem was:

  • You seemingly expected -Format "MM/dd/yyyy" to specify an input parsing format; in fact, it formats the output, which then becomes a [string].

    • For meaningful date comparison you need a [datetime] instance, however.
  • Your arguments to the (implied) -Date parameter ((Read-Host ...), $csv.updated) were implicitly parsed, based on the standard formats recognized by the current culture (System.Globalization.CultureInfo.CurrentCulture).

Therefore:

  • If your arguments are already properly recognized by the implicit parsing (as your acceptance of Kiran's answer suggests), the solution is to simply remove the -Format arguments from your code:

      # Parse each input string implicitly, based on the current culture,
      # and output a [datetime] instance, which will compare as expected
      # with `-gt`
      $InputDate = Get-Date (Read-Host -Prompt 'Enter the date')
      $CreatedDate = Get-Date $CSV.updated
    
  • If, by contrast, you do need to parse by the "MM/dd/yyyy" format string explicitly in order for your arguments to be recognized, call .NET method [datetime]::ParseExact(<string>, <format>[, <culture>]) (System.DateTime.ParseExact); e.g.:

      # Note: The input string must match the format *exactly*
      $InputDate = [datetime]::ParseExact(
        (Read-Host -Prompt 'Enter the date'), # input string
        "MM/dd/yyyy", # format string
        $null  # default to the current culture's rules
      ) 
    

Read on for background information.


Get-Date input and output formats an data types:

-Format "MM/dd/yyyy" doesn't specify an input parsing format, it specifies the output string-formatting format, which has two implications:

  • String input passed to input parameter -Date (which the first positional argument is bound to) is implicitly parsed as [datetime], based on the standard string formats recognized by the current culture[1]; in effect, the following is called behind the scenes:

      [datetime]::Parse((Read-Host -Prompt 'Enter the date'), [cultureinfo]::CurrentCulture)
    
    • (Omitting [cultureinfo]::CurrentCulture in the call above has the same effect.)
    • Caveat: When you cast a string to [datetime], it is the invariant culture ([cultureinfo]::InvariantCulture, based on US-English) that is applied, which PowerShell often does for stability of code across cultures; that a cmdlet such as Get-Date is not culture-invariant is a historical accident that won't be corrected for fear of breaking old code; see this GitHub issue for background. For instance, with culture fr-FR (French) in effect, [datetime] '12/1] yields December 1st (month first), whereas Get-Date -Date '12/1' yields January 12 (day first).
  • The Get-Date command will return a (formatted) string rather than a [datetime] instance; that is, the [datetime] instance implicitly parsed on input is formatted based on the format string passed to -Format, based on the rules of the current culture; if $dt contains a [datetime] instance, -Format "MM/dd/yyyy" is the equivalent of (note how a direct call to System.DateTime.ToString() gives you the option to specify a different culture, which -Format doesn't):

      $dt.ToString("MM/dd/yyyy", [cultureinfo]::CurrentCulture)
    

(As of PowerShell v7.0), the Get-Date cmdlet has no support for specifying a format string for input parsing.

  • As stated, passing a string to -Date causes it to be implicitly parsed, based on the standard date/time formats recognized by the current culture.

  • You'll have to call .NET method [datetime]::ParseExact(<string>, <format>[, <culture>]) (System.DateTime.ParseExact) directly, as demonstrated in the top section.

  • Once you have a [datetime] instance, you may pass it (via -Date) to Get-Date -Format to get a formatted string representation for the current culture; alternatively, you can call .ToString(<format>[, <culture>]) (System.DateTime.ToString) directly on the instance, which also gives you the option to format for a different culture.

As for Get-Date input and output data types:

Fundamentally, how the input is passed (specific arguments) determines the how the resulting [datetime] instance is interpreted, based on how the cmdlet chooses the instance's .Kind property value (Utc, Local, or Unspecified).

The .Kind property value determines what specific global point in time, if any, a [datetime] instance represents. The .Kind value can also affect an instance's string representation, depending on what specific formatting is applied (notably, inclusion of time-zone information).

The resulting [datetime] instance is:

  • either: output directly (by default)
  • or: if a -Format argument was passed, is the basis for deriving the requested string representation ([string]).

Get-Date determines the .Kind property value as follows:

  • With no -Date argument, you get a Local instance (representing the current point in time)

    • (Unless a string is also passed to -Date), using the various offset parameters, such as -Year and -Day, produces a Local instance as well.
  • With a -Date argument that already is of type [datetime], that instance is used as-is, which means the existing .Kind property value is retained.

  • With a string -Date argument (which gets implicitly parsed), the .Kind value will be Unspecified (representing an abstract point in time, without reference to a specific time zone), including if combined with parameters such as -Year.

  • A numeric -Date argument (e.g, 637165787436900010) is interpreted as a System.DateTime.Ticks value, which also produces an Unspecified instance.

Note: PowerShell [Core] 7.1 will introduce an -AsUTC switch that makes the output / to-be-string-formatted instance be of kind Utc; -AsLocal and -AsUnspecified switches (and/or an -AsKind <kind> parameter) are also being discussed - see this GitHub issue.


[1] If the input already is a [datetime] instance, it is used as-is.

Upvotes: 1

Kiran Patnayakuni
Kiran Patnayakuni

Reputation: 46

You need to parse the date in string to datetime type to do the date comparisons.

$CSV = Import-Csv C:\Users\person\Desktop\date.csv 
$InputDate = [datetime]::Parse( $(Read-Host -Prompt 'Enter the date (MM/dd/yyyy)') )
$CreatedDate = [datetime]::Parse( $CSV.updated )
if($InputDate -gt $CreatedDate)
{write-host "Input Newer"}
else
{write-host "Input Older"}

Upvotes: 2

Related Questions