17

Following my first post:

DateTime conversions using NodaTime on ASP.Net MVC 3 Razor website. How to?

I'm struggling to find an easy way to convert date/time between local and UTC (both ways), using NodaTime.

The current picture is:

  • I have the date/time saved as UTC in the database.
  • When displaying it to the user, I should consider the local time zone and convert it accordingly.
  • When the user provides date/time as a filter, I need to convert it back to UTC before sending to the SQL query.

What I have so far:

Extension to convert from UTC to local (this part is working fine):

    public static DateTime UTCtoLocal(this DateTime dateTime)
    {
        IDateTimeZoneProvider timeZoneProvider = DateTimeZoneProviders.Tzdb;

        var utcTimeZone = timeZoneProvider["UTC"];
        var dateTimeFromDb = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond);

        var zonedDbDateTime = utcTimeZone.AtLeniently(LocalDateTime.FromDateTime(dateTimeFromDb));

        var usersTimezoneId = "Europe/London"; //just an example
        var usersTimezone = timeZoneProvider[usersTimezoneId];

        var usersZonedDateTime = zonedDbDateTime.WithZone(usersTimezone);

        return usersZonedDateTime.ToDateTimeUnspecified();
    }

Extension to convert from local back to UTC (this part is the problem):

    public static DateTime LocaltoUTC(this DateTime dateTime)
    {
        IDateTimeZoneProvider timeZoneProvider = DateTimeZoneProviders.Tzdb;
        var usersTimezoneId = "Europe/London";
        var usersTimezone = timeZoneProvider[usersTimezoneId];

        var dateTimeFromDb = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond);
        var zonedDbDateTime = usersTimezone.AtLeniently(LocalDateTime.FromDateTime(dateTimeFromDb));

        var utcTimezoneId = "UTC";
        var utcTimezone = timeZoneProvider[utcTimezoneId];

        var utcZonedDateTime = zonedDbDateTime.WithZone(utcTimezone);

        return utcZonedDateTime.ToDateTimeUtc();
    }

What am I doing wrong here?

Community
  • 1
  • 1
melancia
  • 9,119
  • 2
  • 26
  • 45

1 Answers1

31

Your UTCToLocal looks like it's doing more work than it needs to, to be honest.

It should just be:

// Note: the DateTime here must have a "Kind" of Utc.
public static DateTime UTCtoLocal(this DateTime dateTime)
{
    Instant instant = Instant.FromDateTimeUtc(dateTime);
    IDateTimeZoneProvider timeZoneProvider = DateTimeZoneProviders.Tzdb;
    var usersTimezoneId = "Europe/London"; //just an example
    var usersTimezone = timeZoneProvider[usersTimezoneId];
    var usersZonedDateTime = instant.InZone(usersTimezone);
    return usersZonedDateTime.ToDateTimeUnspecified();
}

Similarly your LocalToUTC should be along these lines:

// The DateTime here should have a "Kind" of Unspecified
public static DateTime LocaltoUTC(this DateTime dateTime)
{
    LocalDateTime localDateTime = LocalDateTime.FromDateTime(dateTime);

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

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

You don't need to convert it to a different time zone: ZonedDateTime knows what the instant is, and ToDateTimeUtc will do the right thing. Note that there's no real dateTimeFromDb here, because if you're converting from an unspecified DateTime, that's presumably from the user...

Jon Skeet
  • 1,261,211
  • 792
  • 8,724
  • 8,929
  • Thanks again, @Jon. Will try this one out. Keep up with the good work on NodaTime. – melancia May 22 '13 at 08:16
  • what is the use of LocaltoUTC, shouldn't DateTime.ToUniversalTime() work just fine? thanks – pajics Nov 19 '13 at 13:54
  • 2
    @pajics: No, because that will always use the system time zone - the OP wants to use the user's time zone. – Jon Skeet Nov 19 '13 at 13:56
  • When converting from a LocalDateTime to Utc, does NodaTime take into account future timezone daylight savings? Ex: You convert LocalDateTime of July 4, 2020 at 4:00PM EST to Utc, but the conversion is done in February, so at the time of the conversion EST offset is -5, but at the time of the date being converted, EST offset is -4. – ScubaSteve Jan 08 '20 at 17:56
  • @ScubaSteve: The date on which you do the conversion is completely irrelevant. The date being converted is all that's relevant. – Jon Skeet Jan 08 '20 at 18:04
  • Correction: not AtLieniently, I meant to make sure ToDateTimeUtc was taking account of what the DLS would be at the time specified by LocalDateTime/DateTimeZone. – ScubaSteve Jan 09 '20 at 18:56