1

I'm getting the following error message:

The variable '$dateTime' cannot be retrieved because it has not been set.

The error is generated by the line [DateTime]$dateTime in this code:

  [string]$registryEntry = Read-Registry -path $SCRIPT_KEY -name $key

   [DateTime]$dateTime 
   if ($null -ne $registryEntry) {
      $dateTime = [DateTime]$registryEntry
   }
   return $dateTime

But the error message makes no sense: I’m not trying to retrieve the $dateTime variable, I’m just declaring it.

I have to return a DateTime object, or $null if it’s undefined. FYI, the registry entry is the string “19 Jul 2023 05:52”

How do I make this work?

$psVersionTable:

Name                           Value
----                           -----
PSVersion                      7.3.6
PSEdition                      Core
GitCommitId                    7.3.6
OS                             Microsoft Windows 10.0.19045
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
mklement0
  • 382,024
  • 64
  • 607
  • 775
aksarben
  • 588
  • 3
  • 7

2 Answers2

2

As the error very clearly states:

because it has not been set.

PowerShell doesn't really have a concept of "variable declaration" - if you want to strongly type a variable, you'll have to do it as part of an assignment operation - which in turn will solve your problem:

[string]$registryEntry = Read-Registry -path $SCRIPT_KEY -name $key

[datetime]$dateTime = [datetime]::MinValue
if ($null -ne $registryEntry) {
    $dateTime = [DateTime]$registryEntry
}

return $dateTime
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Wow. Never would have guessed I couldn't declare a variable. Glad someone has more PSH experience than me. Thanks.Hopefully the folks who maintain PSH will come up with a better error message for this scenario. – aksarben Jul 19 '23 at 18:45
  • 1
    It’s worth noting for the OP’s benefit that ```[DateTime]$dateTime``` is valid Powershell but its meaning is equivalent to ```write-output ([DateTime]$dateTime)``` which sends the result of evaluating ```[DateTime]$dateTime``` to the output stream, so it *is* trying to retrieve the variable ```$dateTime``` (which hasn’t been assigned a value yet so doesn’t exist) to calculate that result, hence the error message… – mclayton Jul 19 '23 at 18:48
0

To add to Mathias R. Jessen's helpful answer:

I’m not trying to retrieve the $dateTime variable, I’m just declaring it.

PowerShell has no concept of mere variable declarations (the only exceptions are parameter variables - see the relevant section of about_Functions).

You can only "declare" a variable by implicitly creating it via assigning a value to it. (If the variable already exists, it will be updated.)

Since you want to use a type constraint, i.e. to limit the values the variable can hold to a given type, the value you assign must be of that type or convertible to it; in the simplest case:

[datetime] $dateTime = 0

Using [int] value 0 has the same effect as [datetime]::MinValue, as shown in Mathias' answer, given that 0 can be converted to the latter, due to PowerShell's flexible automatic type conversions (see this answer).

If you either use no type constraint or you use a type constraint with a .NET reference type, you can use $null for initialization; e.g.:

# No type constraint, same as [object]
$newVar = $null

# .NET reference-type type constraint
[regex] $newVar = $null

if $null is allowed as a value in a given situation, you can also use the following single-statement idiom for initializing a variable with varying values, so as to guarantee the variable's existence, based on using an if statement as an expression; it wouldn't work with [datetime], so I'm omitting the type constraint here:

# Assigns the output from the `if` statement.
$dateTime =
  if ($null -ne $registryEntry) {
    [datetime] $registryEntry
  }

That is, the if statement's output becomes the value of $datetime, which means that if $null -ne $registryEntry is true, [datetime] $registryEntry is assigned, and $null otherwise.[1]

Note that a PowerShell variable without a type constraint is free to not only be initialized with a value of an type (including $null), but to also later be updated with a value of any other type.

As an aside:

  • Another surprising aspect of variable assignments in PowerShell is that they implicitly create a local variable with the given name, even if a variable by that name already exists, but was defined in an ancestral scope - see this answer.

As for what you tried:

In isolation, [DateTime]$dateTime does the following:

  • It is a type cast, i.e. it tries to convert an existing $dateTime variable to type [datetime] and outputs the result:

    • That is, you are trying to retrieve the value of variable $dateTime

    • If $dateTime does not exist yet...

      • ... and you have Set-StrictMode -Version 1 or higher in effect, you'll get the error you saw.

      • ... otherwise, given that referencing non-existent variables defaults to $null, your cast will be the equivalent of [datetime] $null, which fails with Cannot convert null to type "System.DateTime"

  • In the event that such a cast succeeds its result is sent to the success output stream, which - in the absence of assigning it to a variable or passing it to a command - prints to the display by default, due to PowerShell's implicit output behavior, explained in the bottom section of this answer.


[1] Strictly speaking, it is the "Automation null" value that is assigned, which is an "enumerable null" that behaves like $null in expression contexts, and like an empty enumerable in enumeration contexts - see this answer for details.

mklement0
  • 382,024
  • 64
  • 607
  • 775