Search code examples
c#asp.netsql-servertimezonedst

Daylight Saving Time not working in TimeZoneInfo, when converting from UTC to Local


I'm working on an ASP.NET/C# application, which supports time zones.

First let me explain the flow of the application, I'm storing an object for purchase order. So it has the datetime field in it.

I'm storing that datetime as UTC in Database, and binding it in grid (according to the client's timezone).

In the Page_Init method of the first page, I had used a javascript code which will detect the client's timezone, and return appropriate offset.

In the Page_Load method, I'm getting that javascript return value (offset) and comparing it with the offset of each zone in TimeZoneInfo.GetSystemTimeZones().

When comparing the offset, I'm getting a TimeZoneInfo object (for example) "(UTC-08:00) Baja California", "(UTC +05:30) Chennai,Kolkata"

Using that particular TimeZoneInfo object, I'm converting the UTC datetime (which is stored in DB) to the client's timezone.

As by the above flow, the application works fine for some timezones.

The problem is, if when I change timezone of client machine to (UTC -8:00), the client machine shows the timezone name as "(UTC-08:00) Pacific Time (US & Canada)" but in application the timezone differs from the client system it shows as "(UTC-08:00) Baja California".

And importantly DST changes are not reflecting when I convert the UTC to Local. TimeZoneInfo.ConvertTimeFromUtc(DateTime, clientTimezone);

Note: I'm not storing Timezone information of client in database or anywhere. So everytime if a user enters the application, the application will recognize the timezone and it will react according to it.

My question is:

  • Whether the TimeZoneInfo class can work automatically work according to the adjusment rule, when we convert from UTC to Local?

  • Do we have to detect the DST for particular datetime using the method TimeZoneInfoObject.IsDaylightSavingTime(DateTime) and do conversion?

  • Is there any other classes in .Net which can sync with windows timezones?


Solution

  • A few things you should understand:

    1. A time zone is not the same as a time zone offset. One cannot just take the number -8 and assume that the time zone should be Pacific time.

    2. Offsets can change within a single time zone. For example, Pacific Time usually uses -8, but switches to -7 when daylight saving time is in effect.

    3. The offsets in the DisplayName property of a TimeZoneInfo are only the standard offset. They match with the BaseOffset property. They do not change to reflect the current offset.

    4. Time zone detection in JavaScript is imperfect. There are only three approaches:

      • Using the getTimezoneOffset function of the Date class, which should return you the offset of the date it was called on. For example new Date().getTimezoneOffset() gives you the current offset. With this approach, you should also be aware that there is a bug in the ES5 spec that can cause the wrong offset to sometimes be returned when called on older dates.

      • Using a library such as jsTimezoneDetect, which makes several calls to the getTimezoneOffset to attempt to guess at an IANA time zone identifier. The guess is suitable to set a default time zone when a list of time zones is presented to the user. It is just a guess, and could be wrong. If you want to use it on the back end with .NET, you'll need Noda Time, since TimeZoneInfo doesn't currently support IANA time zones. (You can optionally convert to Windows time zones if desired).

      • Some newer browsers support the ECMAScript Internationalization API, which has an optionally implemented function to return the time zone. It may work in some browsers, but is not guaranteed to return a valid result everywhere.

        Intl.DateTimeFormat().resolvedOptions().timeZone

        Again, you'll need Noda Time on the back end.

    5. You said:

      The problem is, if when I change timezone of client machine to (UTC -8:00), the client machine shows the timezone name as "(UTC-08:00) Pacific Time (US & Canada)" but in application the timezone differs from the client system it shows as "(UTC-08:00) Baja California".

      This is probably related to how you are choosing a time zone in your application code. It sounds to me like you're scanning the list of server time zones and choosing the first one that matches some criteria. Since both of these time zones have the same base offset, you're probably just picking the wrong one, and you shouldn't be doing that anyway. But since you didn't show that part of your code, I can't help you much there.

    To answer your specific questions:

    • Whether the TimeZoneInfo class can work automatically work according to the adjusment rule, when we convert from UTC to Local?

      Yes, it can. There's nothing wrong with TimeZoneInfo, it's all about how you're using it. You probably are selecting the wrong time zone.

    • Do we have to detect the DST for particular datetime using the method TimeZoneInfoObject.IsDaylightSavingTime(DateTime) and do conversion?

      No, you should not have to do that just to convert from UTC to a specific time zone. The ConvertTimeFromUtc function will handle that for you.

    • Is there any other classes in .Net which can sync with windows timezones?

      TimeZoneInfo is the only one built in to the .NET Framework. Noda Time is a great alternative that can work with either Windows time zone or IANA time zones.

    Lastly, I'll re-iterate what Jon said in comments. If all you're doing is displaying a particular instant in time to an end-user, then forget about time zone detection or working with local time on the server at all. Just send the UTC time to the client, and use either the UTC functions on the JavaScript Date object, or use a library like moment.js. Either can work in both UTC and local, and can convert between them. For example (using moment.js):

    var valueFromServer = "2015-07-26T12:00:00Z";     // the Z means UTC
    var localTime = moment(valueFromServer).format(); // "2015-07-26T05:00:00-07:00"  (Pacific)