I have a situation where I would like to convert a DateTime
to an Instant
. I believe the DateTime
's Kind
will be Local
.
Given that the DateTime
is in a variable called time
, the closest I could find in the library is:
Instant.FromDateTimeUtc(time.ToUniversalTime())
This seems reasonable, but I want to be certain that this is completely reliable and doesn't have the risk of data loss or corruption. I'm not certain if this is the best way to do the conversion or if there's a more reliable means to do so.
I looked at NodaTime's BCL Conversions page, and it said the following about this scenario:
Note that there are no conversions to a
DateTime
with a kind ofLocal
- this would effectively be for the system default time zone, which you should generally be explicit about to start with.
You're missing one critical point: A DateTime
whose kind is Local
doesn't always fully represent a unique moment in time. That is why there is no direct mapping to an Instant
.
During a fall-back DST transition, a local DateTime
can represent either of two possible moments in time. If you're going to convert it to an Instant
, then somewhere you need to decide which moment in time you should choose.
In the answer you gave, I'm assuming you obtained the timezone
from one of the following:
var timezone = DateTimeZoneProviders.Tzdb.GetSystemDefault();
or
var timezone = DateTimeZoneProviders.Bcl.GetSystemDefault();
Either are ok for this task. Then the code you gave:
var localTime = LocalDateTime.FromDateTime(time);
var zonedTime = localTime.InZoneStrictly(timeZone);
return zonedTime.ToInstant();
This is exactly correct, but since you used InZoneStrictly
, you will get an AmbiguousTimeException
during the fall-back transition.
You can avoid this by using InZoneLeniently
, which will pick the latter of the two possibilities (usually the "Standard" time). But more importantly, you can instead use InZone
and provide either a standard or custom resolver to control the behavior more precisely.
Regarding your original approach of:
Instant.FromDateTimeUtc(time.ToUniversalTime())
This is ok and will not corrupt your data, but understand that it will rely on the BCL's behavior of local to universal conversion. It is identical to InZoneLeniently
, in that an ambiguous value will be treated as the "standard" time.
This is a great example of how NodaTime offers an API that is more precise. Instead of making assumptions, you have the opportunity to be specific and provide custom behavior. In the end you achieved the same result, but it brought this issue to the foreground instead of hiding it.