I'm getting an unexpected DateTime when converting from:
(UTC) Dublin, Edinburgh, Lisbon, London to (UTC-08:00) Baja California
Below is the code I have been using. I'm expecting serverDateTime to be 29/03/2016 00:00:01 (-8 hours), but instead I get 28/03/2016 23:00:01 - which is a 9 hour difference.
private static void Main(string[] args)
{
ReadOnlyCollection<TimeZoneInfo> timeZones = TimeZoneInfo.GetSystemTimeZones();
TimeZoneInfo localTimeZone = timeZones.FirstOrDefault(tz => tz.DisplayName.Contains("London"));
TimeZoneInfo serverTimeZone = timeZones.FirstOrDefault(tz => tz.DisplayName.Contains("California"));
DateTime clientDateTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 29, 8, 0, 1);
DateTime serverDateTime = TimeZoneInfo.ConvertTime(clientDateTime, localTimeZone, serverTimeZone);
}
My local machine is in the UK, which is currently UTC+1, and Baja - California is currently UTC-7, so I'm expecting to get an 8 hour difference, instead of 9. What am I doing wrong?
Thanks in advance.
A few things:
"(UTC-08:00) Baja California" refers to Baja California, Mexico - not California, USA.
This zone entry follows Mexican DST rules, which doesn't start DST until April 3rd this year. Thus this entry is on UTC-8 still, explaining the time difference you observed.
This particular entry is actually wrong in Windows, as it turns out that the entire state of Baja California follows USA rules for daylight saving time, not Mexican rules.
There is an extensive multi-thread discussion of this in the tz mailing list archives in October and November 2015. This resulted in the corresponding IANA zone America/Santa_Isabel being deprecated with release 2016a. It now links to America/Tijuana instead, which already had been following USA DST rules.
Microsoft has not yet made a similar change to its data, but it is likely in a future update to the Windows time zone data. (I have already communicated this to the appropriate personnel.) In the meantime, use "(UTC-08:00) Pacific Time (US & Canada)" - even if you're talking about Baja California, Mexico.
Don't try to look up a time zone by its DisplayName. Those values will be different depending on operating system language. Instead, use TimeZoneInfo.FindSystemTimeZoneById. The Id properties are not localized. Additionally, Microsoft considers the Id values to be stable identifiers, and thus they will not change with future updates. The DisplayName values have been modified in the past, and could indeed change in the future.
Use the ID "GMT Standard Time" for "(UTC) Dublin, Edinburgh, Lisbon, London"
Use the ID "Pacific Standard Time" for "(UTC-08:00) Pacific Time (US & Canada)"
The ID "Pacific Standard Time (Mexico)" is for "(UTC-08:00) Baja California" - but for the reasons I described, don't use this entry.
Really, you shouldn't be concerned with the time zone on the server at all. The server should only be concerned with UTC. (note that London is not UTC, since it switches for BST in the summer)
I'm not sure what you're intention was with this line:
DateTime clientDateTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 29, 8, 0, 1);
Pulling the month and year from DateTime.Now is going to use the server's time zone. Then you combine with fixed day of 29 and fixed time at 8:00:01. This will fail in February of a non-leap year (when there are only 28 days in the month), and also could give bad dates when the server's date is not in the same month and year as the client's (such as near the transition from one month to another).
Related
This question already has answers here:
Why doesn't C# detect that 1970/1/1 was under BST?
(3 answers)
Closed 4 years ago.
British Summer time adjusts clocks forward in March and back in October every year. During 1968 to 1971 the UK trialed BST as a permanent option, such that the clocks where put forward 1 hour in March 1968 and not reverted back until October 1971.
I am creating dates in Javascript, serializing them as JSON and posting to a WebApi.
Currently using windows 7 as a development environment, Windows is NOT recognizing that period as BST. For example 01/01/1970 should be Daylight Saving time, however
new System.DateTime(1970, 01, 01, 00, 00, 00).IsDaylightSavingTime();
returns false.
also...
System.TimeZone.CurrentTimeZone.GetDaylightChanges(1970)
{System.Globalization.DaylightTime}
Delta: {01:00:00}
End: {25/10/1970 02:00:00}
Start: {29/03/1970 01:00:00}
1970 should have a rule covering the entire year as the whole year was BST.
Is there a patch to correct the flaw in Windows?
Update
There is a bug after all, or rather GMT Standard Time doesn't contain UK-specific rules. Between 1968 and 1970 the offset for the UK changed to +1:00 and there was no DST.
The real problem is that the offset is wrong for the UK for that period :
var date= new DateTime(1970,1,1,0,0,0,DateTimeKind.Local);
var tzi = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
var offset=tzi.GetUtcOffset(date);
offset is 00:00:00. Oooops!
For 1970-08-01 the offset is 01:00:00 and IsDaylightSavingTime() returns True.
PS:
SQL Server's AT TIME ZONE uses the Windows time zone names. This could be an even bigger source of problems with historical data.
Original
There's no bug. When the entire year has a single offset it doesn't make sense to talk about Daylight Saving Time.
The IANA timezone database shows that 1970-01-01 did not use DST. The offset was +1:00. Using NodaTime :
var london = DateTimeZoneProviders.Tzdb["Europe/London"];
// Time zone conversions
var localDate = new LocalDateTime(1970, 1, 1, 0, 0, 00);
var before = london.AtStrictly(localDate);
Console.WriteLine($"{before} {before.IsDaylightSavingTime()} {before.Offset}");
This returns :
1970-01-01T00:00:00 Europe/London (+01) DST:False +01
For 1971-11-01 the result is :
1971-11-01T00:00:00 Europe/London (+00) DST:False +00
At that point the offset changed from +1:00 to +00:00 and the DST rule was reintroduced.
The results are more interesting for summer dates.
1971-07-30 returns :
1971-07-30T00:00:00 Europe/London (+01) DST:False +01
Which is correct - there were no DST rules in effect on that date. The offset was fixed at +1.
1972-07-30 returns :
1972-07-30T00:00:00 Europe/London (+01) DST:True +01
The offset is the same, because the DST rules were in effect on that date.
Windows does not have a separate time zone for the United Kingdom. The time zone used by Windows in the United Kingdom ("GMT Standard Time") is shared with Ireland and Portugal.
That's why historic deviations that only applied to the United Kingdom are not reflected.
Borders of countries have changed a lot even in the last 200 years and until quite recently (only a few decades ago) very small regions in Europe had their own definition of time zones and they changed frequently. Windows cannot reflect that complex information. If you need that information you need to use a dedicated database.
As has been stated in the comments, the code you presented is using the current timezone on the computer you are running that code. This might be the UK but it's not a good idea to assume. The below code takes into account the comments above:
var tzi = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
var dt = new DateTime(1970, 1, 2, 0, 0, 0, DateTimeKind.Utc);
var isDlt = tzi.IsDaylightSavingTime(dt);
This also returns false though so the bug as you state does indeed exist. I very much doubt there is a patch but if you were so inclined you could quite easily write an extension method that uses the IANA database to determine if a given date is during daylight saving.
You might also want to look at the documentation for TimeZoneInfo.GetAdjustmentRules - https://msdn.microsoft.com/en-us/library/system.timezoneinfo.getadjustmentrules(v=vs.110).aspx
I have an application in which time zones are treated as string, by using the system name so we can make an actual System.TimeZoneInfo object by doing:
var tz = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
Such values are persisted to DB, now we are facing an issue where one such object is requested to be on Arizona Time which is not a standard timezone. From what I have investigated the Arizona Time changes Time Zones due to the fact that it doesn't observes "Day Light Savings".
I am looking for a way to set one value in DB so that there is no need to change it according to day light savings changes.
Is there a way to do this?
Even if I have to change a bit the code to get the TimeZoneInfo object. What really matters to me is a way to determine the actual timezone corresponding to Arizona Time
About Arizona time zones
From timeanddate.com:
There is a common misconception that Arizona is on Pacific Daylight
Time (PDT) during the summer and on Mountain Standard Time (MST)
during the winter. Because MST and PDT have the same UTC offset of
minus 7 hours (UTC-7), Arizona has the same local time as neighboring
states California and Nevada during the summer season. Although the
time is the same, Arizona uses standard time (MST) all year.
“Daylight” time zones, such as MDT, are mostly used for areas that
switch to DST every year
IANA (tz database) time zone database contains two time zones for Arizona:
America/Phoenix (Mountain Standard Time - Arizona, except Navajo), which does not observe daylight saving changes (DST), and
America/Shiprock, which observes DST.
Arizona time zones in .NET
Depending on your users' exact location in Arizona, you should use either America/Phoenix or America/Shiprock time zone, so you will need two values in the database. However, if you try to get time zones with TimeZoneInfo.FindSystemTimeZoneById using tz database names, you will get System.TimeZoneNotFoundException.
In order to get Arizona time zone that does not observe DST (America/Phoenix), you can use:
TimeZoneInfo.FindSystemTimeZoneById("US Mountain Standard Time")
In order to get Arizona time zone that does observe DST (America/Shiprock), you can use:
TimeZoneInfo.FindSystemTimeZoneById("Mountain Standard Time")
So, you would have both ids in your database, US Mountain Standard Time and Mountain Standard Time, or alternatively some other strings that you would later map to these .NET time zone ids.
Check out NodaTime, it can help you a lot when it comes to dealing with date, time and time zones.
And finally, here is a sample program (with NodaTime) that demonstrates the difference between .NET US Mountain Standard Time (America/Phoenix, Arizona without DST) and Mountain Standard Time (America/Shiprock, Arizona with DST).
using System;
using NodaTime;
using NodaTime.TimeZones;
namespace TimeZoneExample
{
class Program
{
static void Main(string[] args)
{
// Arizona without daylight saving time (TZ: America/Phoenix)
var mstWithoutDstTz = TimeZoneInfo.FindSystemTimeZoneById("US Mountain Standard Time");
// Arizona with daylight saving time (TZ: America/Shiprock)
var mstWithDstTz = TimeZoneInfo.FindSystemTimeZoneById("Mountain Standard Time");
// NodaTime BclDateTimeZone for Arizona without daylight saving time
var mstWithoutDstNodaTz = BclDateTimeZone.FromTimeZoneInfo(mstWithoutDstTz);
// NodaTime BclDateTimeZone for Arizona with daylight saving time
var mstWithDstNodaTz = BclDateTimeZone.FromTimeZoneInfo(mstWithDstTz);
// January 1, 2017, 15:00, local winter date
var localWinterDate = new LocalDateTime(2017, 01, 01, 15, 00);
// NodaTime ZonedDateTime for Arizona without daylight saving time: January 1, 2017, 15:00
var winterTimeWithoutDst = mstWithoutDstNodaTz.AtStrictly(localWinterDate);
// NodaTime ZonedDateTime for Arizona with daylight saving time: January 1, 2017, 15:00
var winterTimeWithDst = mstWithDstNodaTz.AtStrictly(localWinterDate);
// Both time zones have the same time during winter
Console.WriteLine($"Winter w/o DST: {winterTimeWithoutDst}"); // 2017-01-01T15:00:00 US Mountain Standard Time (-07)
Console.WriteLine($"Winter w/ DST: {winterTimeWithDst}"); // 2017-01-01T15:00:00 Mountain Standard Time (-07)
// add 180 days to get June 30, 2017
var sixMonthsToSummer = Duration.FromTimeSpan(new TimeSpan(180, 0, 0, 0));
// During summer, e.g. on June 30, Arizona without daylight saving time is 1 hour behind.
Console.WriteLine($"Summer w/o DST: {winterTimeWithoutDst + sixMonthsToSummer}"); // 2017-06-30T15:00:00 US Mountain Standard Time (-07)
Console.WriteLine($"Summer w/ DST: {winterTimeWithDst + sixMonthsToSummer}"); // 2017-06-30T16:00:00 Mountain Standard Time (-06)
}
}
}
If I understand your problem correctly, you want to create a custom time zone representing "Arizona Time" which has a constant offset from UTC regardless the date of the year.
If so, you should be able to use the static method
TimeZoneInfo.CreateCustomTimeZone
Just set the TimeSpan to the number of hours from UTC that you need it to be (-7 hours from what I can tell).
https://msdn.microsoft.com/en-us/library/bb309898(v=vs.110).aspx
EDIT: You might also have some success by simply using the named timezone
"US Mountain Standard Time"
which should represent the same.
I convert a string that represents a time in Pacific Time Zone that I am using to create a DateTime object:
var pacificDateTime = new DateTime(2016, 11, 16, 15, 0, 0) // 11/16/2016 3:00:00 PM
Using that, I create a DateTimeOffset because ultimately it becomes a bit easier to work with.
var pacificTimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
var dateTimeNoKind = new DateTime(pacificDateTime.Ticks, DateTimeKind.Unspecified)
var DateTimeOffsetValue = TimeZoneInfo.ConverTimeToUtc(dateTimeNoKind, pacificTimeZoneInfo) // 11/16/2016 11:00:00 PM
So far so good. The difference between UTC and Pacific is that UTC is ahead by 8 hours (the given time is within daylight savings).
Then I want to convert from UTC to AEST—but this is where the problem appears:
var australianEasternTimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time");
TimeZoneInfo.ConvertTime(DateTimeOffsetValue, australianEasternTimeZoneInfo) // 11/17/2016 10:00:00 AM
AEST is ahead of UTC by 10 hours. I had expected the value to be 11/17/2016 09:00:00 AM but instead I am getting an extra hour added to my result.
As a test, when I convert to PST or GMT or EST from the UTC time, they appear to convert back correctly.
I feel like I am missing something obvious or overlooking something simple?
From Wikipedia's Time in Australia article:
Australia has two eastern time zones. In the northeast, UTC+10 applies all year. In the southeast, UTC+10 applies during standard time, and UTC+11 applies during daylight time (aka summer time).
The northeast region (Queensland) uses the IANA time zone identifier "Australia/Brisbane", while the southeast region (New South Wales) uses "Australia/Sydney". These correspond to the Windows time zone identifiers: "E. Australia Standard Time" and "AUS Eastern Standard Time" respectively.
If you are converting for Queensland, use "E. Australia Standard Time".
If you are converting for New South Wales, use "AUS Eastern Standard Time".
As to the confusing nature of these identifiers, see the section about Windows time zones in the timezone tag wiki.
If you want to use the standard IANA identifiers instead, use Noda Time.
I am trying to convert EST( Eastern Standard Time) to IST (Indian Standard Time) but the conversion is showing incorrect results.
Can anyone help me on that??
I searched on net and found that using Noda time we can solve that.
But I want to solve it using conventional DateTime class.
Here is my code and its output:
DateTime time= new DateTime(1899,12,30, 23, 30 ,0); //some random date and 11:30 PM in EST
TimeZoneInfo estZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"); // Eastern Time Zone
TimeZoneInfo istZone = TimeZoneInfo.FindSystemTimeZoneById("India Standard Time"); // Indian Time Zone
DateTime localTime = TimeZoneInfo.ConvertTime(time, estZone, istZone); // result is 10:00 am while it should be 09:00 am.
A few things:
The TimeZoneInfo identifier "Eastern Standard Time" refers to the North American Eastern Time zone, covering both Eastern Standard Time and Eastern Daylight Time. EST is UTC-5, while EDT is UTC-4. In general, you should not infer too much from the names of these identifiers. See more examples in the timezone tag wiki.
The TimeZoneInfo.ConvertTime method will use whichever offset is appropriate for the supplied date and time, correctly taking the daylight saving time rules into account.
The underlying time zone data from Windows does not go back to 1899. There are actually no sources of data that guarantee historical dates from that time period. Even the IANA time zone database used with Noda Time makes an educated guess. See History of DST in the United States.
Windows will just use the earliest data it has, which for this zone uses the daylight saving time rules that were in effect from 1986 to 2007. These are not the current rules, so it would make better sense to use a modern year, such as DateTime.Today.Year.
Even if you supplied a modern year, the correct converted time would indeed be 10:00 for a date in December. If you want 9:00, try a date in the summer.
Problem:
I Need to execute a task on a server that is on UTC at specific time in different time zones. Say for example I want to execute at 9:00AM Pacific Time, irrespective of Daylight Savings changes.
What do I have?
I checked the enumeration of time zones by doing
var infos = TimeZoneInfo.GetSystemTimeZones();
foreach (var info in infos)
{
Console.WriteLine(info.Id);
}
I could see only "Pacific Standard Time" for the pacific time for example and If I do the following,
TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time").GetUtcOffset(DateTime.UtcNow)
I get -07:00:00 as output but as of now the offset is -08. That means it doesn't consider the daylight changes.
I was planing to to create a DateTime instance based on the offset I get above, which doesn't seem to work as I expected.
Also, I can't use frameworks like NodaTime
Any idea how I can get it working?
You've already got your answer, using TimeZoneInfo.GetUtcOffset and passing a DateTime with DateTimeKind.Utc will work.
I get -07:00:00 as output but as of now the offset is -08. That means it doesn't consider the daylight changes.
Actually, -7 is indeed the current offset, as Pacific time is currently using daylight saving time. In the winter, the offset reverts to -8, which is the standard offset. I think you just have them backwards.
Also, keep in mind that the Id property of a TimeZoneInfo object is the identifier for the entire time zone. Some of them are misleading, like "Pacific Standard Time" - which would make you believe that it only uses Pacific Standard Time (PST), but actually it represents the entire North American Pacific Time zone - including Pacific Standard Time and Pacific Daylight Time. It will switch offsets accordingly at the appropriate transitions.
You are probably looking for something like this:
var chinaTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, TimeZoneInfo.FindSystemTimeZoneById("China Standard Time"));
var pacificTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time"));
The string passed to FindSystemTimeZoneById is picked form TimeZoneInfo.GetSystemTimeZones()
Update: cleansed the code