NodaTime Invalid DateTime.Kind for Instant.FromDateTimeUtc

4.7k views Asked by At

I'm trying to get ahold of this timezone issue we are having. We would like to store all DateTimes in UTC, and then convert the DateTime to the user's timezone.

We decided to use NodaTime for this, as it seems like the right approach. However, we are experiencing an issue with it.

This is how we convert the DateTime to UTC (note - I hardcoded the usersTimeZone for now):

public static DateTime ConvertLocaltoUTC(this DateTime dateTime)
{
    LocalDateTime localDateTime = LocalDateTime.FromDateTime(dateTime);

    IDateTimeZoneProvider timeZoneProvider = DateTimeZoneProviders.Tzdb;
    var usersTimezoneId = "Europe/Copenhagen";
    var usersTimezone = timeZoneProvider[usersTimezoneId];

    var zonedDbDateTime = usersTimezone.AtLeniently(localDateTime);
    var returnThis = zonedDbDateTime.ToDateTimeUtc();
    return zonedDbDateTime.ToDateTimeUtc();
}

And here is how we convert it back:

public static DateTime ConvertUTCtoLocal(this DateTime dateTime)
{
    Instant instant = Instant.FromDateTimeUtc(dateTime);
    IDateTimeZoneProvider timeZoneProvider = DateTimeZoneProviders.Tzdb;
    var usersTimezoneId = "Europe/Copenhagen"; //just an example
    var usersTimezone = timeZoneProvider[usersTimezoneId];
    var usersZonedDateTime = instant.InZone(usersTimezone);
    return usersZonedDateTime.ToDateTimeUnspecified();
}

However, when we convert it back to local time, it throws an exception:

Argument Exception: Invalid DateTime.Kind for Instant.FromDateTimeUtc

at the first line of ConvertUTCtoLocal().

An example of the DateTime could be: "9/18/2017 5:28:46 PM" - yes this has been through the ConvertLocalToUTC method.

Am I providing an incorrect format? What am I doing wrong here?

2

There are 2 answers

0
Matt Johnson-Pint On

The exception you show:

Argument Exception: Invalid DateTime.Kind for Instant.FromDateTimeUtc

Is thrown from this code:

Instant instant = Instant.FromDateTimeUtc(dateTime);

It means that dateTime.Kind needs to be DateTimeKind.Utc to be convertible to an Instant, and for whatever reason it is not.

If you look at the result of your ConvertLocaltoUTC method, you'll find that it does have .Kind == DateTimeKind.Utc.

So, the problem lies elsewhere in your code, wherever you created the dateTime you're passing in to ConvertUTCtoLocal.

You may find the solution could be any of the following:

  • You might need to call DateTime.SpecifyKind to set the kind to UTC, but be careful to only do this when your values are actually UTC and it's just not setting the kind. For example, use this when loading a UTC-based DateTime from a database.

  • You might need to call .ToUniversalTime(), but be careful to only do this if the machine's local time zone is relevant to your situation. For example, do this in desktop or mobile apps where a UI control is picking a date, but you meant it to mean UTC instead of local time.

  • You might need to change how you parse strings into DateTime values, such as by passing DateTimeStyles.RoundTripKind to a DateTime.Parse call (or any of its variants. For example, do this if you are reading data from text, csv, etc.

If you want to avoid having to decide, don't write functions that take DateTime as input or give DateTime as output. Instead, use DateTimeOffset, or use Noda-Time types like Instant, LocalDateTime, etc. as early as possible.

0
Chris Halcrow On

This is what worked for me:

Instant instant = Instant.FromDateTimeUtc(DateTime.SpecifyKind(datetime, DateTimeKind.Utc));