I'm using the Invoke-RestMethod to get the data from REST API. One of the attributes in response is the date. When using Postman or other tools to get the data the date is returned correctly but when I'm using PowerShell (version 5.1.19041.906) and its Invoke-RestMethod like this:
$response = Invoke-RestMethod -Method Get -Uri $url -Headers $requestHeaders
All values from the date attribute are automatically converted to UTC. Is there any way how to disable this shift? I need the original values returned from the API.
CodePudding user response:
$response = Invoke-RestMethod -Method Get -Uri $url -Headers $requestHeaders
$changeddate = $response.fields.'System.ChangedDate'
$datetime = ([DateTime]$changeddate).ToLocalTime()
CodePudding user response:
Invoke-RestMethod
, when given a JSON response, automatically parses it into a [pscustomobject]
graph; in a manner of speaking, it has ConvertFrom-Json
built in.
When ConvertFrom-Json
does recognize what are invariably string representation of dates in the input JSON, it converts them to [datetime]
instances.
In Windows PowerShell (v5.1, the latest and final version) and as of PowerShell (Core) 7.2, you get NO control over what kind of [datetime]
instances are constructed, as reflected in their .Kind
property:
In Windows PowerShell, which requires a custom date-string format (e.g.
"\/Date(1633984531266)\/"
), you invariably getUtc
instances.In PowerShell (Core) 7 , which additionally automatically recognizes string values that are (variations of) ISO 8601 date-time strings (e.g.
"2021-10-11T13:27:12.3318432-04:00"
), the.Kind
value depends on the specifics of the string value:- If the string ends in
Z
, denoting UTC, you get aUtc
instance. - If the string ends in a UTC offset, e.g.
-04:00
you get aLocal
instance (even if the offset value is00:00
) - Otherwise you get an
Unspecified
instance.
- If the string ends in
While Windows PowerShell will see no new features, there is a hope for PowerShell (Core): GitHub issue #13598 proposes adding a -DateTimeKind
parameter to ConvertFrom-Json
, so as to allow explicitly requesting the kind of interest, and to alternatively construct [datetimeoffset]
instances, which are preferable.
Workaround:
- Note: In the event that you need access to the raw string values, exactly as defined, the solution below wont' work. You'll have to retrieve the raw JSON text and perform your own parsing, using
Invoke-WebRequest
and the response's.Content
property, as Mathias R. Jessen notes.
The following snippet walks a [pscustomobject]
graph, as returned from Invoke-RestMethod
and explicitly converts any [datetime]
instances encountered to Local
instances in place (Unspecified
instances are treated as Local
):
# Call Invoke-RestMethod to retrieve and parse a web service's JSON response.
$fromJson = Invoke-RestMethod ...
# Convert any [datetime] instances in the object graph that aren't already
# local dates (whose .Kind value isn't already 'Local') to local ones.
& {
# Helper script block that walks the object graph.
$sb = {
foreach ($el in $args[0]) { # iterate over elements (if an array)
foreach ($prop in $el.psobject.Properties) {
# iterate over properties
if ($dt = $prop.Value -as [datetime]) {
switch ($dt.Kind) {
'Utc' { $prop.Value = $dt.ToLocalTime() }
# Note: calling .ToLocalTime() is not an option, because it interprets
# an 'Unspecified' [datetime] as UTC.
'Unspecified' { $prop.Value = [datetime]::new($dt.Ticks, 'Local') }
}
}
elseif ($prop.Value -is [Array] -or $prop.Value -is [System.Management.Automation.PSCustomObject]) {
& $sb $prop.Value # recurse
}
}
}
}
# Start walking.
& $sb $args[0]
} $fromJson
# Output the transformed-in-place object graph
# that now contains only Local [datetime] instances.
$fromJson