How to Use Timezoneinfo to Get Local Time During Daylight Saving Time

How to use TimeZoneInfo to get local time during Daylight Saving Time?

You need to get the UtcOffset from the TimeZoneInfo, then pass that to the ToOffset() method:

var dt = DateTime.UtcNow;
Console.WriteLine(dt.ToLocalTime());

var tz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
var utcOffset = new DateTimeOffset(dt, TimeSpan.Zero);
Console.WriteLine(utcOffset.ToOffset(tz.GetUtcOffset(utcOffset)));

How to use TimeZoneInfo Class with daylight savings To CONVERT TO UTC

The code is already doing the right thing and adjusting for daylight savings. "AUS Eastern Standard Time" is the Windows equivalent of the IANA Australia/Sydney zone. The transitions for Australia/Sydney are shown in my Noda Time site, and they indicate that there's a transition on April 6th 4pm UTC (so April 7th 3am local time) from UTC+11 to UTC+10.

That means by April 10th - the date of the local time you're asking to be converted - the offset is indeed UTC+10, and 10/04/2019 02:00:00 UTC (am) is the correct result. If it gave a result of 1am (so subtracting a UTC offset of +11) that would be incorrect because that wouldn't be adjusting from the daylight saving transition on April 6th.

Handling timezones with daylight saving in C#

From the discussion in the question's comments, I understand that you are working with flight time schedules - that is, the time a future flight is intended to depart. This is indeed a case where the local time is more important than the UTC time.

Since you have the local time and location of departure (ex: 5:00 PM in Salt Lake City), then you should be storing in your database of scheduled departure times two values:

  • 17:00 - The relevant local time of the departure
  • SLC - The location where the time is relevant

If this is a specific occurrence of this flight, then you should store the date as well:

  • 2018-06-01T17:00 - The specific relevant local time of the departure
  • SLC - The location where the local time is relevent

These are the details that are contextually relevant to your business use case. Do not lose sight of them by converting them to UTC.

That said, you might consider storing them as a DateTimeOffset (2018-06-01T17:00-06:00), which makes converting to UTC trivial for a given instance. However there are two problems with this approach:

  • It cannot work with recurrences, because the offset may change.
  • Even for a single instance, the offset might change - if the government controlling the time zone decides to change their standard offset or daylight saving time rules before that occurrence takes effect. If you do take a DateTimeOffset approach, or a UTC-based approach, you must be prepared to recalculate future events in the face of such changes. (For more on this, see my blog articles: On the Timing of Time Zone Changes and Time Zone Chaos Inevitable in Egypt. There are countless other examples.)

With regards to the location - because you are working with data that is contextually applicable to the airline industry, I recommend using IATA airport codes like the SLC that I showed above. In other contexts, one might store an IANA time zones identifier like America/Denver, or a Windows time zone identifier like Mountain Standard Time.

You may find my "Airport Time Zones" gist (code and output table) useful for working with IATA airport codes. You'll have to decide how that data will flow through your system. If you are running on Windows and want to use the TimeZoneInfo class to convert times to different time zones, then use the Windows time zone IDs shown there. If you want to use the IANA time zone IDs instead, consider using Noda Time, or you can use my TimeZoneConverter library. There are several different options here, so explore them all carefully and pick ones that make sense to you.

Noda Time would be a great choice, IMHO. Not only would you get excellent time zone support, but you'd also be able to use types like LocalTime or LocalDateTime which align well with the scenarios described.

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

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)

How to get convert server time to local time and deal with daylight saving time

  1. You should never rely on the time zone settings of a server. Therefore DateTime.Now should not be ever used in an ASP.NET application.

    There are many other reasons to avoid DateTime.Now. Read:The case against DateTime.Now.

  2. The local time of the server is never guaranteed to be the local time of the user of your website anyway. If it is, it's just coincidental.

  3. The easiest way to get the user's current time in their time zone is via JavaScript:

    var now = new Date();

    Though this is based on the user's clock, which may or may not be correctly set. To have any guarantees about the time, you'd have to use the UTC time of the server's clock, with the user's time zone applied.

    One approach you might consider is to send the UTC time of the server down to the client, then load that into JavaScript in the client to project it to their time zone:

    // C#
    string serverUtcTime = DateTime.UtcNow.ToString("o"); // "2015-09-18T17:53:15.6988370Z"

    // JS
    var now = new Date("2015-09-18T17:53:15.6988370Z");
  4. Actually detecting the user's time zone is a hard problem that does not currently have a solution. Some may recommend new Date().getTimezoneOffset(), but that only gives you the current numeric offset, not the time zone. Offsets can change for DST, and many time zones use similar offsets. There are also complications for historical dates near DST transitions that will work against you.

    Scripts like jsTimeZoneDetect can guess your IANA time zone ID, such as America/New_York for Eastern time, but they are not 100% accurate. If you need the user's time zone on your server, then ultimately should ask the user for their time zone somewhere in your application.

  5. In .NET, you can use Noda Time to work with IANA time zones. Without Noda Time, .NET has the TimeZoneInfo class, but it can only work with Windows time zones.

  6. If you know for certain that the users are in Atlanta, GA (which is in the US Eastern time zone), then you can do this:

    DateTime utc = DateTime.UtcNow;
    TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
    DateTime eastern = TimeZoneInfo.ConvertTimeFromUtc(utc, tz);

    Or, with Noda Time and IANA time zone IDs:

    Instant now = SystemClock.Instance.Now;
    DateTimeZone tz = DateTimeZoneProviders.Tzdb["America/New_York"];
    ZonedDateTime eastern = now.InZone(tz);

TimeZoneInfo.ConvertTimeFromUtc is not converting old dates to then daylight saving time

You're using the wrong timezone. "E. Australia Standard Time" corresponds to Brisbane time, and that timezone does not observe DST so your winter and summer times come back with the same UTC offset.

For Sydney, you need to use "AUS Eastern Standard Time". ConvertTimeFromUtc will apply the UTC offset as appropriate for that timezone and give you the times you're expecting.

Here is your snippet including the use of the appropriate time zone, and a bit of editing around the Timezone initialisation so it compiled:

    public void ConvertTimes()
{
string Timezone = "AUS Eastern Standard Time";

TimeZoneInfo ESTZone = TimeZoneInfo.FindSystemTimeZoneById(Timezone);
DateTime date = Convert.ToDateTime("2019-01-23 13:15:23.6090752");
DateTime SydneyDateTime = TimeZoneInfo.ConvertTimeFromUtc(date, TimeZoneInfo.FindSystemTimeZoneById(Timezone));
string EST = SydneyDateTime.ToString() + " / " + SydneyDateTime.IsDaylightSavingTime().ToString() + " / " + ESTZone.IsDaylightSavingTime(SydneyDateTime) + " / " + ESTZone.DaylightName + " / " + ESTZone.StandardName;
DateTime IndiaDateTime = TimeZoneInfo.ConvertTimeFromUtc(date, TimeZoneInfo.FindSystemTimeZoneById("India Standard Time"));

DateTime date2 = Convert.ToDateTime("2019-05-23 13:15:23.6090752");
DateTime SydneyDateTime2 = TimeZoneInfo.ConvertTimeFromUtc(date2, TimeZoneInfo.FindSystemTimeZoneById(Timezone));
string EST2 = SydneyDateTime2.ToString() + " / " + SydneyDateTime2.IsDaylightSavingTime().ToString() + " / " + ESTZone.IsDaylightSavingTime(SydneyDateTime2) + " / " + ESTZone.DaylightName + " / " + ESTZone.StandardName;
}

This gives the output:

EST = "24/01/2019 00:15:23 / False / True / AUS Eastern Daylight Time / AUS Eastern Standard Time"

EST2 = "23/05/2019 23:15:23 / True / False / AUS Eastern Daylight Time / AUS Eastern Standard Time"

which I think is what you were expecting.

Incidentally, DateTime.IsDaylightSavingTime() uses your local timezone to determine whether the time is within DST. It might be misleading looking at it here.



Related Topics



Leave a reply



Submit