I'm trying to get ahold of this timezone issue we are having. We would like to store all DateTime
s 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?
The exception you show:
Is thrown from this code:
It means that
dateTime.Kind
needs to beDateTimeKind.Utc
to be convertible to anInstant
, 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 toConvertUTCtoLocal
.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-basedDateTime
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 passingDateTimeStyles.RoundTripKind
to aDateTime.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 giveDateTime
as output. Instead, useDateTimeOffset
, or use Noda-Time types likeInstant
,LocalDateTime
, etc. as early as possible.