I'm parsing the dnsRecord attribute from a CSVDE dump of DNS records.
The attribute stores the timestamp as the number of hours that have elapsed since midnight (00:00:00), January 1, 1601 UTC in a string of hex digits.
So far, I have (with hard-coded sample value):
$dnsRecordTimestampBytes = '0038a293'
(Get-Date '01/01/1601Z').AddHours([int]('0x' + $dnsRecordTimestampBytes))
which results in 03 June 2024 11:00:00
but it should be 03 June 2024 12:00:00
.
When I parse a value which is not in Summer Time, it gives the correct value, e.g.
$dnsRecordTimestampBytes = '00389c04'
(Get-Date '01/01/1601Z').AddHours([int]('0x' + $dnsRecordTimestampBytes))
gives 25 March 2024 12:00:00
which is correct.
Is there a way to fix this without me having to do bounds-checks on the start and end of summer time?
To get the correct result, you must convert your timestamp from UTC to local time only after adding the hours, and the problem is that your Get-Date
call - despite being passed a UTC timestamp string as an argument - returns a local timestamp (of type [datetime]
):
(Get-Date '01/01/1601Z').Kind # -> Local
The immediate fix is to call .ToUniversalTime()
first, then add the hours, and then call .ToLocalTime()
:
(Get-Date '1601-01-01Z'). # better: ([datetime] '1601-01-01Z').
ToUniversalTime().
AddHours([int]('0x' + $dnsRecordTimestampBytes)).
ToLocalTime()
A more concise solution is to use the [datetimeoffset]
class:
([datetimeoffset] '1601-01-01Z').
AddHours([int]('0x' + $dnsRecordTimestampBytes)).
LocalDateTime
As an aside:
Note that I've reformatted your timestamp string from 01/01/1601Z
to 1601-01-01Z
so as to use an unambiguous format (though in this particular case, with both the month and day being 01
, this isn't strictly necessary).
Either way, in PowerShell it is generally better to work with casts (e.g.
[datetime] '01/25/2025'
), as they are culture-invariant (they use [cultureinfo]::InvariantCulture
), which means that the result is not dependent on what .NET culture happens to be in effect (note, however, that the invariant culture is based on the US one, so that, as implied by the example above, the first two-digit number is interpreted as the month).
By contrast, passing a timestamp string as an argument to a binary cmdlet such as Get-Date
is subject to interpretation by the effective culture.
This is a known inconsistency that - unfortunately - won't be fixed so as not to break backward compatibility; see GitHub issue #6989.