Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NodaTime conversions (Part 2). How to?

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?

like image 957
melancia Avatar asked May 21 '13 15:05

melancia


1 Answers

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...

like image 182
Jon Skeet Avatar answered Nov 08 '22 07:11

Jon Skeet