skip to Main Content

Problem description

in the Microsoft Powershell Documentation ConvertFrom-Json (Microsoft.PowerShell.Utility) – PowerShell – Microsoft Learn is an example using Get-Date

Get-Date | Select-Object -Property * | ConvertTo-Json | ConvertFrom-Json

Running this example in a simply modified way the double conversion shows a weird output:

PS T:> $DateObj = Get-Date | Select-Object -Property *
PS T:> $DateObj | ConvertTo-Json | ConvertFrom-Json


DisplayHint : 2
DateTime    : Freitag, 19. April 2024 23:06:39
Date        : 18.04.2024 22:00:00
Day         : 19
DayOfWeek   : 5
DayOfYear   : 110
Hour        : 23
Kind        : 2
Millisecond : 54
Minute      : 6
Month       : 4
Second      : 39
Ticks       : 638491647990542546
TimeOfDay   : @{Ticks=831990542546; Days=0; Hours=23; Milliseconds=54; Minutes=6; Seconds=39; TotalDays=0.96295201683564813; TotalHours=23.110848404055556; TotalMilliseconds=83199054.2546; TotalMinutes=1386.6509042433333;
              TotalSeconds=83199.054254599992}
Year        : 2024

Note the output of the Date field, which is off by one day for whatever reason.
Is this a bug in the conversion?

DateTime : Freitag, 19. April 2024 23:06:39 – correct local time

Date : 18.04.2024 22:00:00 – time offset of 25 hours – like UTC +25


Looking at the time of the query and language date settings, it could be related to to those settings:

  • Get-Date issued after 23:00 (11PM)
  • local Timezone set is CEST – Central European Summer Time (German MESZ)
  • as summer time is active, CEST is UTC +2 at the moment
  • CET (German MEZ) is 1 hour behind UTC (or GMT) – UTC +1
  • summer time is genrally 1 hour earlier than local time UTC – UTC +1

In a second test after midnight, the difference can be at least explained, as detailed below

But none of the differences explain, why the Date field is off by 25 hours (earlier) – UTC +25

PS T:> $unixTime = 1713477600000 / 1000
PS T:> $origin = New-Object -Type DateTime -ArgumentList 1970, 1, 1, 0, 0, 0, 0
PS T:> $whatIWant = $origin.AddSeconds($unixTime)
PS T:> $whatIWant

Donnerstag, 18. April 2024 22:00:00

It seems somehow in the conversion to JSON with ConvertTo-Json the timestamp seems to get messed up.

Test 1 – onestep conversion at 23:04 CEST – weird offset like UTC +25 hours

PS T:> Get-Date | Select-Object -Property * | ConvertTo-Json | ConvertFrom-Json


DisplayHint : 2
DateTime    : Freitag, 19. April 2024 23:04:54
Date        : 18.04.2024 22:00:00
Day         : 19
DayOfWeek   : 5
DayOfYear   : 110
Hour        : 23
Kind        : 2
Millisecond : 435
Minute      : 4
Month       : 4
Second      : 54
Ticks       : 638491646944355931
              TotalSeconds=83094.4355931}
Year        : 2024

The double conversion to JSON and back ConvertTo-Json and then ConvertFrom-Json shows the weird behaviour between the Datetime (correct local time) and Date property field, which shows a difference of roughly 25 hours less than local time (UTC +25)

Test 2 – at 23:05 CEST – properties in powershell are off by 23 hourslike UTC +23

PS T:> Get-Date | Select-Object -Property *


DisplayHint : DateTime
DateTime    : Freitag, 19. April 2024 23:05:00
Date        : 19.04.2024 00:00:00
Day         : 19
DayOfWeek   : Friday
DayOfYear   : 110
Hour        : 23
Kind        : Local
Millisecond : 487
Minute      : 5
Month       : 4
Second      : 0
Ticks       : 638491647004876481

Test 3 – at 23:06 CEST – 2 steps: create $DateObj with all properties – time offset +23 h

PS T:> $DateObj = Get-Date | Select-Object -Property *
PS T:> $DateObj


DisplayHint : DateTime
DateTime    : Freitag, 19. April 2024 23:06:39
Date        : 19.04.2024 00:00:00
Day         : 19
DayOfWeek   : Friday
DayOfYear   : 110
Hour        : 23
Kind        : Local
Millisecond : 54
Minute      : 6
Month       : 4
Second      : 39
Ticks       : 638491647990542546
TimeOfDay   : 23:06:39.0542546
Year        : 2024

First we create a meta object $DateObj with all properties and display it.
We see that Get-Date is using local time settings and it seems the Date property field should be UTC, but the difference between the Date (UTCish) and Datetime (correct local time) property fields is 25 hours later (UTC +25), a time offset which doesn’t exist.

PS T:> $DateObj | ConvertTo-Json | ConvertFrom-Json


DisplayHint : 2
DateTime    : Freitag, 19. April 2024 23:06:39
Date        : 18.04.2024 22:00:00
Day         : 19
DayOfWeek   : 5
DayOfYear   : 110
Hour        : 23
Kind        : 2
Millisecond : 54
Minute      : 6
Month       : 4
Second      : 39
Ticks       : 638491647990542546
TimeOfDay   : @{Ticks=831990542546; Days=0; Hours=23; Milliseconds=54; Minutes=6; Seconds=39; TotalDays=0.96295201683564813; TotalHours=23.110848404055556; TotalMilliseconds=83199054.2546; TotalMinutes=1386.6509042433333; TotalSeconds=83199.054254599992}
Year        : 2024


The double conversion from the PS object $DateObj with ConvertTo-Json to JSON and then back with ConvertFrom-Json shows the difference between DateTime and Date of 23h in the past (UTC +25)

PS T:> $DateObj | ConvertTo-Json
{
    "DisplayHint":  2,
    "DateTime":  "Freitag, 19. April 2024 23:06:39",
    "Date":  "/Date(1713477600000)/",
    "Day":  19,
    "DayOfWeek":  5,
    "DayOfYear":  110,
    "Hour":  23,
    "Kind":  2,
    "Millisecond":  54,
    "Minute":  6,
    "Month":  4,
    "Second":  39,
    "Ticks":  638491647990542546,
    "TimeOfDay":  {
                      "Ticks":  831990542546,
                      "Days":  0,
                      "Hours":  23,
                      "Milliseconds":  54,
                      "Minutes":  6,
                      "Seconds":  39,
                      "TotalDays":  0.96295201683564813,
                      "TotalHours":  23.110848404055556,
                      "TotalMilliseconds":  83199054.2546,
                      "TotalMinutes":  1386.6509042433333,
                      "TotalSeconds":  83199.054254599992
                  },
    "Year":  2024
}

In the conversion to JSON with ConvertTo-Json we see the Date field with the wrong timestamp 1713477600000.
It’s the Unix timestamp with milliseconds added somehow rounded, but the offset of UTC/GMT and local time are wrong.

PS T:> $unixTime = 1713477600000 / 1000
PS T:> $origin = New-Object -Type DateTime -ArgumentList 1970, 1, 1, 0, 0, 0, 0
PS T:> $whatIWant = $origin.AddSeconds($unixTime)
PS T:> $whatIWant

Donnerstag, 18. April 2024 22:00:00

Test 4 – at 00:07 CEST – offset is in the same hour

PS T:> $DateObj2 = Get-Date | Select-Object -Property *
PS T:> $DateObj2


DisplayHint : DateTime
DateTime    : Samstag, 20. April 2024 00:07:32
Date        : 20.04.2024 00:00:00
Day         : 20
DayOfWeek   : Saturday
DayOfYear   : 111
Hour        : 0
Kind        : Local
Millisecond : 18
Minute      : 7
Month       : 4
Second      : 32
Ticks       : 638491684520183150
TimeOfDay   : 00:07:32.0183150
Year        : 2024

Here the offset between the DateTime and Date fields is merely 7 minutes.

PS T:> $DateObj2 | ConvertTo-Json | ConvertFrom-Json


DisplayHint : 2
DateTime    : Samstag, 20. April 2024 00:07:32
Date        : 19.04.2024 22:00:00
Day         : 20
DayOfWeek   : 6
DayOfYear   : 111
Hour        : 0
Kind        : 2
Millisecond : 18
Minute      : 7
Month       : 4
Second      : 32
Ticks       : 638491684520183150
TimeOfDay   : @{Ticks=4520183150; Days=0; Hours=0; Milliseconds=18; Minutes=7; Seconds=32; TotalDays=0.005231693460648148; TotalHours=0.12556064305555556; TotalMilliseconds=452018.315; TotalMinutes=7.5336385833333335; TotalSeconds=452.018315}
Year        : 2024

Using the double conversion to JSON and back to a PS object, the time difference between DateTime and Date is now two hours (UTC +2)

PS T:> $DateObj2 | ConvertTo-Json
{
    "DisplayHint":  2,
    "DateTime":  "Samstag, 20. April 2024 00:07:32",
    "Date":  "/Date(1713564000000)/",
    "Day":  20,
    "DayOfWeek":  6,
    "DayOfYear":  111,
    "Hour":  0,
    "Kind":  2,
    "Millisecond":  18,
    "Minute":  7,
    "Month":  4,
    "Second":  32,
    "Ticks":  638491684520183150,
    "TimeOfDay":  {
                      "Ticks":  4520183150,
                      "Days":  0,
                      "Hours":  0,
                      "Milliseconds":  18,
                      "Minutes":  7,
                      "Seconds":  32,
                      "TotalDays":  0.005231693460648148,
                      "TotalHours":  0.12556064305555556,
                      "TotalMilliseconds":  452018.315,
                      "TotalMinutes":  7.5336385833333335,
                      "TotalSeconds":  452.018315
                  },
    "Year":  2024
}

The conversion of the Date field to JSON gets a different timestamp (looks again like unix timestamp * 1000 ms)

PS T:> $unixTime = 1713477600000 / 1000
PS T:> $origin = New-Object -Type DateTime -ArgumentList 1970, 1, 1, 0, 0, 0, 0
PS T:> $whatIWant = $origin.AddSeconds($unixTime)
PS T:> $whatIWant

Donnerstag, 18. April 2024 22:00:00

Using the JSON timestamp in the Date field an converting it to a timedate string, it shows the same time as when the date field gets converted back to a Powershell Object.
Here the time difference to local time is UTC +2

Summary: Powershell datetime values and or conversion to and from JSON seem to be wrong

It seems the Date Property of the Powershell seems to be returning really odd and impossible values.

PS T:> Get-TimeZone


Id                         : W. Europe Standard Time
DisplayName                : (UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
StandardName               : W. Europe Standard Time
DaylightName               : W. Europe Daylight Time
BaseUtcOffset              : 01:00:00
SupportsDaylightSavingTime : True

PS T:> (Get-Date).IsDaylightSavingTime()
True

Above is the information about the timezone and summer time setting of the local host, where the commands were issued.

Links

2

Answers


  1. Yeah, it’s a bug. It has been fixed in 7.x, not sure if they are ever going to fix it in 5.1.x.

    Explanation:

    A [DateTime] object has a Date property which is a [DateTime] object its value with time set to Midnight.
    This is WANTED as it’s supposed to be the value of JUST the date without the time but there was no way to have only the day-month-year values.
    So they made a "convert to midnight and let the dev know it’s supposed to check only the date part"
    Ex:

    PS C:Usersxxx> $DateTime=Get-date
    PS C:Usersxxx> $DateTime
        sabato 20 aprile 2024 07:41:08                                                                                                                                                                                                                                                                                                                                          
    PS C:Usersxxx> $DateTime.Date
       sabato 20 aprile 2024 00:00:00                                                                                                                                                                                                                                                
    

    ConvertTo-Json takes the property Date, which is set to Midnight, CONVERTS IT TO UTC and then converts it to to UnixTime.

    Which means it applies the TimeZone Offset to MIDNIGHT.
    Which, in your specific case, means it sets it back by 2 hour, thus turning it into 19 April 2024 22:00:00
    Then it’s converted into UnixTime and then to JSON

    Then ConvertFrom-Json gets it… but it has no way to know its supposed to apply some offset, so it converts it straight back to to 19 April 2024 22:00:00

    Powewrshell 7.x fixed it by having ConvertTo-Json converting [Datetime] properties in a full date string with offset included :

    PS C:Usersxxx> Get-Date | Select-Object -Property Date | convertto-json
        {
          "Date": "2024-04-20T00:00:00+02:00"
        }
    
    Login or Signup to reply.
  2. It’s not so much a bug as it is a quirk of how the .NET Framework < 7 handles JSON dates.

    Most applications follow the ISO 8601 datetime format. When converting date objects to JSON, Microsoft must have decided that it was better to be able to identify where a value is a date – possibly for purposes of simplicity when parsing/deserialising JSON – by using a /date(epoch_timestamp)/ format.

    The issue with double conversion for a date object is that it does not contain the timezone, or time offset; ISO 8601 does.

    When converting to JSON, the Unix time generated is the number of seconds elapsed since 1970 according to your local time, including your time offset. When it is converted back to datetime, there is no way to determine an offset or timezone; It’s been lost. So, when It’s converted back it is assumed that the JSON value was generated inside the UTC timezone. And therefore, in order to display the correct time as intended by the party who generated it, your local time offset is applied.

    In your case, you are West Europe (UTC+1) and daylight saving offset gives you another (+1), so that’s (UTC+2). To get back to UTC time (the time .NET thinks is represented by the JSON value), 2 hours must be deducted.

    You can verify this behaviour by changing your timezone to UTC. You will find that the date is always the correct date.

    You can actually instruct Get-Date to output the date in ISO 8601 format, by using the o round-trip format parameter, if it helps.

    Get-Date -Format o
    

    Otherwise, you could negate the offset behaviour by adding your time offset to the datetime object after conversion. Just make sure the conversion to and back from JSON are being done on the same machine, or on machines in the same time zone.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search