Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I convert a local DateTime to an Instant?

Tags:

.net

nodatime

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 of Local - this would effectively be for the system default time zone, which you should generally be explicit about to start with.

like image 780
Sam Avatar asked Oct 17 '13 03:10

Sam


People also ask

Should I use LocalDateTime or instant?

Instant and LocalDateTime are two entirely different animals: One represents a moment, the other does not. Instant represents a moment, a specific point in the timeline. LocalDateTime represents a date and a time-of-day. But lacking a time zone or offset-from-UTC, this class cannot represent a moment.

How do I get LocalDate from instant?

Get Instant object which you want to convert to LocalDate. Create ZoneId instance based on Location. Pass ZoneId to atZone() method to get ZoneDateTime . Call toLocalDate() on ZoneDateTime object to get LocalDate .

How do I convert local date to local date?

There are two methods that we can use to convert LocalDate to LocalDateTime. Method atStartOfDay(): This method adds midnight time to the LocalDate. LocalDateTime atTime(int hour, int minutes): This method adds given hour and minutes time to the LocalDate.


1 Answers

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.

like image 177
Matt Johnson-Pint Avatar answered Oct 19 '22 09:10

Matt Johnson-Pint