Similar to the recent PR that added the -AsUTC switch to Get-Date, we should also have the ability to set Local DateTimeKind as well.
Examples of how this would work:
Get-Date "1/12/2020 4:00pm" -Format "MM/dd/yyyy HH:mm K"
# The DateTimeKind here is Unspecified, so result is 01/12/2020 16:00
Get-Date ((Get-Date "1/12/2020 4:00pm").ToUniversalTime()) -Format "MM/dd/yyyy HH:mm K"
Get-Date "1/12/2020 4:00pm" -AsUTC -Format "MM/dd/yyyy HH:mm K"
# The DateTimeKind here is Utc, so result is 01/12/2020 22:00 Z
Get-Date ((Get-Date "1/12/2020 4:00pm").ToLocalTime()) -Format "MM/dd/yyyy HH:mm K"
Get-Date "1/12/2020 4:00pm" -AsLocal -Format "MM/dd/yyyy HH:mm K"
# The DateTimeKind here is Local, so result is 01/12/2020 10:00 -06:00
Great idea.
For the sake of completeness, -AsUnspecified
would be helpful too, if you're expressly looking for an abstract point in time.
I assume the intent of the -As*
switches is to either _directly output_ a [datetime]
instance with the indicated .Kind
value (UTC
, Local
, Unspecified
), or to make such an instance _the basis for applying the -Format
/ -UFormat
format_.
We have to be careful about the logic of interpreting the _input_, however:
From what I can tell, only Get-Date -Date
with either a _string_ argument (except strings explicitly indicating UTC or containing a time-zone offset) or a [datetime]
instance whose .Kind
is Unspecified
currently results in the _output_ [datetime]
instance having .Kind
Unspecified
; otherwise, it is Local
(or will be Utc
with -AsUtc
).
The tricky part is that Unspecified
instances are treated _situationally_ either as representing UTC _or_ as representing LOCAL time:
.ToUniversalTime()
interprets an Unspecified
instance as Local
.ToLocalTime()
interprets an Unspecified
instance as Utc
(!)Therefore, as also implied by your last example command:
# NOTE: Interprets the given date *as UTC*
PS> (Get-Date "1/12/2020 4:00pm").ToLocalTime()
Sunday, January 12, 2020 11:00:00 AM # e.g., in the US Eastern time zone
This is .NET's behavior, over which we have no control, but I wonder if surfacing this behavior via
-AsLocal
would cause confusion:
# !! If we just call .ToLocalTime() behind the scenes, we get the same behavior as above:
# !! The given date is interpreted *as UTC* and then *converted to a local date*.
Get-Date "1/12/2020 4:00pm" -AsLocal
My expectation would be that the nominal date given (in text form) would be interpreted _as-is_ as a local date; which is the equivalent of:
PS> [datetime]::new((Get-Date "1/12/2020 4:00pm").Ticks, 'Local') | Select DateTime, Kind
DateTime Kind
-------- ----
Sunday, January 12, 2020 4:00:00 PM Local
Note how the unspecified input date string was now interpreted _as_ a local time, as reflected in the output [datetime]
instance, which has .Kind
Local
, as requested.
+1 @mklement0. I would expect it to use:
[datetime]::Parse(
'4pm',
[cultureinfo]::CurrentCulture,
[Globalization.DateTimeStyles]::AssumeLocal)
Thanks, @SeeminglyScience, but note that since the -Date
parameter is [datetime]
-typed, I think it is the _parameter binder_ that performs the parsing up front (culture-_sensitively_, due to being a _cmdlet_ argument, as opposed to the culture-_invariant_ parsing that happens with _casts_ or _advanced functions_, which is a won't-fix inconsistency; see #6989)
Therefore, I think Get-Date
already sees the parsed result, which is a [datetime]
instance with a .Kind
value of Unspecified
- hence my suggestion to use [datetime]::new(<ticks>, <kind>)
.
@mklement0 and @SeeminglyScience Thanks for joining in!
If we are including all three DateTimeKind options, do you think we need a parameter more like
-AsDateTimeKind Utc
-AsDateTimeKind Local
-AsDateTimeKind Unspecified
?
-AsUTC hasn't been part of a formal release yet, so now would be the time to change it if the PowerShell team was going to?
I'd say, given that there's only _3_ enumeration values whose number is unlikely to grow, it's easier on users to use distinct switches, -AsUtc
, -AsLocal
, -AsUnspecified
.
@mklement0 Fair point. I can accept that ;-)
Then again, for invocation with programmatically constructed parameters something like -AsKind Utc|Local|Unspecified
is preferable...
Having our cake and eating it too (providing -AsUtc
as an effective alias of -AsKind Utc
, ...) is also an option, but might get confusing / be a pain to implement (many parameter sets).
;-) I have only started posting suggestions for PowerShell in the last few days and I hate to ask for the moon, so I am trying to be semi-conservative in my asks. I will take it either way ;-)
Since we're adding multiple options here for the kind it should be an enum not switch parameter. Right now if we add -AsUtc
and -AsLocal
and -AsUnspecified
those three switch parameters will need to be in separate parameter sets since you can't have multiple passed at the same time.
As stated before, for _programmatic_ construction of arguments -AsKind <kind>
is preferable; that doesn't necessary preclude _also_ providing _convenience aliases_ (-AsUtc
for -AsKind Utc
, ...; they wouldn't be the first of their kind), but I get that this (a) could be a paint to _implement_ (proliferation of parameter sets) and (b) the duplication could cause _conceptual confusion_.
In short: I'm personally fine with _only_ implementing -AsKind <kind>
, if that's the consensus.
However, we should then act quickly, before 7.0 GA ships with the recently implemented -AsUtc
switch.
Do you have any examples of a alias switch parameter mapping to another parameter?
The one that readily comes to mind is Get-Location -Stack
for Get-Location -StackName ''
, but, if memory serves, there are more; they're not easy to search for.
More relatable examples:
In Get-ChildItem
: -Directory
/ for Attributes -Directory
, -File
for -Attributes !Directory
, -Hidden
for -Attributes Hidden
, -ReadOnly
for -Attributes ReadOnly
, -System
for Attributes System
.
In Import-Module
: -Global
for -Scope Global
("The Global
parameter is equivalent to the Scope
parameter with a value of Global
." - help).
I'm curious, though, @ThomasNieto: Are you asking not just because you were curious / weren't convinced that there is such a thing, or are you philosophically opposed to the concept?
@mklement0 I was unaware of them or had used them without knowing. The concern I have with them is that you can get into scenarios like this where you can pass conflicting parameters. Normally tab completion will prevent a user from passing parameters that cannot be used together. In the first example -Global
and -Scope
can't be used together but is allowed by tab completion since they're in the same parameter set. The second example is where both can be used at the same time if all attributes are met.
To sum it up, I think convenience parameters have a place where they can be used in conjunction with the other parameter. PowerShell has done a good job with parameter sets protecting the user from passing parameters that cannot be used together through tab completion and help.
C:\> Import-Module AssignedAccess -Global -Scope Local
Import-Module: The 'Global' and 'Scope' parameters cannot be specified together. Remove one of these parameters, and then try running the command again.
C:\> Get-ChildItem -Attributes Directory -Hidden
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d--hs 12/4/2019 10:42 PM $Recycle.Bin
l--hs 12/4/2019 11:35 PM Documents and Settings -> C:\Users
d--h- 2/3/2020 10:09 AM ProgramData
d--hs 12/4/2019 11:35 PM Recovery
d--hs 2/5/2020 6:09 PM System Volume Information
or had used them without knowing.
That's a testament to their usefulness.
To sum it up, I think convenience parameters have a place where they can be used in conjunction with the other parameter
While you _can_ squeeze combinations out of -File
/ -Directory
, ... vs. -Attributes
, it's awkward and potentially self-defeating:
# Allowed, but nonsensical
Get-ChildItem -File -Attributes directory
# The more intuitive way to write your example, using shortcut aliases only
Get-ChildItem -Directory -Hidden
The far more typical case is to use one _or_ the other; if there's something you cannot model with combining the shortcut aliases alone, you're better off using _just_ -Attributes
.
With _single-choice_ enumerations - as in the case of -Global
vs. -Scope Global
and in our case, -AsUtc
, ... vs. -AsKind Utc
- conceptual confusion doesn't even arise, because the shortcut aliases _mustn't_ be combined with the verbose parameter.
As such, these single-choice scenarios strike me as the _ideal_ use case for shortcut aliases.
That Import-Module
currently implements this exclusion awkwardly is not an argument against the concept.
Similarly, the fact that _implementing_ such mutual exclusion is currently cumbersome is not an argument - it just means that we need to make implementing this easier, as has been attempted before, in this since-withdrawn RFC: https://github.com/PowerShell/PowerShell-RFC/blob/master/X-Withdrawn/RFC0001-Mutual-Exclusion-Parameters-and-Properties.md
GitHubRFC (Request for Comments) documents for community feedback on design changes and improvements to PowerShell ecosystem - PowerShell/PowerShell-RFC
To continue the discussion from https://github.com/PowerShell/PowerShell/pull/13084#discussion_r454955330, where @iSazonov wrote:
I guess the DateTimeKind was added to mitigate Datetime type limitations (today we could use DateTimeOffset). I do not think that we should use DateTimeKind broadly.
Agreed that [datetimeoffset]
is always the better choice, butGet-Date
is currently a wrapper for [datetime]
only and for reasons of backward compatibility it will have to remain that way, at least by default.
Therefore, it makes sense to support it more fully, and to also offer -AsLocal
and -AsUnspecified
/ -AsKind <kind>
.
A possible alternative is to eliminate the Unspecified
kind from Get-Date
as follows, in which case _only_ implementing -AsUtc
would be sufficient:
-AsUtc
is passed, _always_ return a Local
date.This would mean that commands such as Get-Date 2020-1-1
, which currently return an Unspecified
date, would then return a Local
date, which may be more in line with what users expect.
Technically, though, it is a breaking change.
AsUTC was added in Preview only (#11611) and we can replace it with general parameter like -AsKind <kind>
or -DateTimeKind <kind>
. No needs exist for other parameters.
/cc @brendandburns
I think it would be complete to take TimeZoneInfo (or its Id
) instead of DateTimeKind (in which case the output would be DateTimeOffset).
As a special case of TimeZoneInfo
, it supports UTC
and Local
.
For example, I may want to know what time '10:00 AM PDT' is in Japanese time (this format is often used as the start time for some event).
So, I think it would be useful if I could convert the time zone as follows.
Get-Date -Date '2020-07-25 10:00' -TimeZone PDT -AsTimeZone JST
But, time zone abbreviations such as 'PDT' and 'JST' are not specified in ISO 8601 and are not official.
Also, it is unclear whether 'JST' refers to 'Japan Standard Time' or 'Jerusalem Standard Time'.
Thus, I am not sure if we should accept these notations as arguments.
This command will accept the following formats.
But, if the command only accepts values in the following form, I don't think this specification is so useful.
Because before I can use the command, I have to find out the official name of the time zone or its offset.
Get-Date -Date '2020-07-25 10:00' -TimeZone 'America/Los_Angeles' -AsTimeZone 'Asia/Tokyo'
Get-Date -Date '2020-07-25 10:00' -Offset -7 -AsOffset +9
I think we should be cautious about using SwitchParameter
and ParameterSetName
together, as they don't work well together.
For example, not setting -AsUTC
is equivalent to specifying -AsUTC:$false
.
However, if -AsUTC
and -AsLocal
are mutually exclusive by parameter set, then if -AsUTC
is not set, -AsLocal
can be set, but if -AsUTC:$false
is specified, -AsLocal
cannot be set.
@aetos382 RE: parameter sets - yes, that's how it's designed, you're not supposed to be able to use them together. Switches supporting a -Switch:$false
option doesn't change that, really, I don't think that allowing both for that reason is very clear or sensible. It's much clearer to show users via parameter sets (which are directly reflected in the syntax diagrams in the Get-Help
output) which switches can and can't be used in tandem. It would be pointless and confusing to display a syntax diagram that _seems_ to allow both, when in practice only one can be used.
As for the time zone suggestion, it would be best if you requested that in a new issue as it is an entirely separate request IMO.