How I troubleshooted incorrect output from Get-Date while using both AddDays() and -Format on it.
In one of my scripts I name my files using yyyyMMdd format. Every few days I'd like to cleanup the old files.
In that particular case I don't want to rely on the date of creation nor modification date.
NOTE: There's no solid reasoning behind that, I simply have chosen that design. Thanks to that I notice something worth sharing and there it is 😉
My expected output is the date from 30 days ago in the desired format. Format is saved in variable $dateFormat.
If you try to run any of these cmdlet's, they won't work:
$dateFormat = 'yyyyMMdd'
(Get-Date).AddDays(-30) -Format $dateFormat
(Get-Date -Format $dateFormat).AddDays(-30)Let's break them down and see, why.
First option does the following, going step by step:
# Prerequisite - define date format
$dateFormat = 'yyyyMMdd'
# Gets current date
Get-Date
# Subtracts 30 days from the date
(Get-Date).AddDays(-30)
# Tries to specify the formatting
(Get-Date).AddDays(-30) -Format $dateFormatIf we run them step-by-step, only the last line fails. The error is:
At line:1 char:25
+ (Get-Date).AddDays(-30) -Format $dateFormat
+ ~~~~~~~
Unexpected token '-Format' in expression or statement.
At line:1 char:33
+ (Get-Date).AddDays(-30) -Format $dateFormat
+ ~~~~~~~~~~~
Unexpected token '$dateFormat' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedTokenIn simple English it says: you tried to specified -Format for something which doesn't accept that parameter.
If we check it carefully, we might notice that we're trying to specify format to the output of AddDays(), not to Get-Date. If we change the order, we get our second solution. Let's analyse step-by-step:
# We already have $dateFormat, no need to specify again
# $dateFormat = 'yyyyMMdd'
# Gets current date
Get-Date
# Convert it to the format specified
Get-Date -Format $dateFormat
# Tries to subtract 30 days
(Get-Date -Format $dateFormat).AddDays(-30)As previously, the last line fails. This time with:
Method invocation failed because [System.String] does not contain a method named 'AddDays'.
At line:1 char:1
+ (Get-Date -Format $dateFormat).AddDays(-30)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFoundError message gives us pretty clear explanation why it fails, but where on earth did we use [System.String]?
The key is the -Format parameter for Get-Date. When used, the cmdlet outputs the value as a string. We can confirm it by invoking GetType() function:
PS> (Get-Date).GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True DateTime System.ValueType
PS> (Get-Date -Format $dateFormat).GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.ObjectString type doesn't have AddDays method. It's quite obvoius but let's confirm it using Get-Member:
Get-Date -Format $dateFormat | Get-Member -Name AddDaysNo suprise, nothing in output.
We already know what to do - use AddDays() on DateTime object and remember about conversion to String forced by -Format.
Let's do it in two steps
# We already have $dateFormat, no need to specify again
# $dateFormat = 'yyyyMMdd'
# Calculate desired date
$30DaysAgo = (Get-Date).AddDays(-30)
# Convert to DateTime and apply formatting
Get-Date -Date $30DaysAgo -Format $dateFormatBy having a quick look at the docs of Get-Date (emphasis mine):
-Date
Type: DateTime
Aliases: LastWriteTime
Position: 0
Default value: None
Accept pipeline input: True
Accept wildcard characters: False
We discover that we might skip -Date (due to numbered position) or we can use pipeline (as it accepts pipeline input). In that case our last line might be:
# Numbered position
Get-Date $30DaysAgo -Format $dateFormat
# Pipeline input
$30DaysAgo | Get-Date -Format $dateFormat-Format on Get-Date changes type of the output from DateTime to StringDateTime object to specific format running Get-Date with -Format parameter on itDate parameter on Get-Date is the default one so we can supply the data by using position or pipeline