TimeZoneInfo.ConvertTime from PST to UTC to AEST - off by one hour - c#

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.

Related

Incorrect UTC Datetime Calculated based on TimeZoneInfo

I have a timezoneinfo Central Europe Standard Time, which is showing UTC +01:00,
when i execute the code it shows 2 hrs before time instead of 1 hr.
Below is the sample code
static void Main(string[] args)
{
var strTimeZoneInfo = "Central Europe Standard Time";
var datetimeDST = Convert.ToDateTime("2019-07-18 18:17:00");
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(strTimeZoneInfo);
var dtUTC= TimeZoneInfo.ConvertTimeToUtc(datetimeDST, timeZoneInfo);
Console.WriteLine(Convert.ToString(dtUTC));
Console.ReadKey();
}
So instead of 2019-07-18 17:17:00 i am getting 2019-07-18 16:17:00.
Can someone explain how to resolve this without using Noda or other external library.
Referred Below links but not clear how to resolve this problem.
UTC converted to Central Europe Standard Time is 2 hours in front not 1
Is there a generic TimeZoneInfo For Central Europe?
Also referred this link's answer but it's not working.
Even though "Central Europe Standard Time" is the Id property for that TimeZoneInfo, but it actually handles both Standard and Daylight time within that time zone depending on the date you're talking about. I understand it is confusing because that TimeZoneInfo includes "UTC+01:00" in the Display Name, even though it's currently in Daylight Savings Time.
But in this case, the date you're giving it is during a Daylight Savings Time period, so the offset should be +02:00, not +01:00. The library is producing the correct results.
var strTimeZoneInfo = "Central Europe Standard Time";
var datetimeDST = Convert.ToDateTime("2019-07-18 18:17:00");
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(strTimeZoneInfo);
Console.WriteLine(timeZoneInfo.DisplayName);
Console.WriteLine(timeZoneInfo.StandardName);
Console.WriteLine(timeZoneInfo.DaylightName);
Console.WriteLine(timeZoneInfo.IsDaylightSavingTime(datetimeDST));
Console.WriteLine(timeZoneInfo.GetUtcOffset(datetimeDST));
var dtUTC = TimeZoneInfo.ConvertTimeToUtc(datetimeDST, timeZoneInfo);
Console.WriteLine(dtUTC.ToString(CultureInfo.InvariantCulture));
Output:
(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
Central Europe Standard Time
Central Europe Daylight Time
True
02:00:00
07/18/2019 16:17:00

Getting the Arizona Standard Time in .net

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.

Incorrect time using TimeZoneInfo.ConvertTime()

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

Converting EST to IST gives error in C#

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.

I want to specify a TimeZone as GMT but create a DateTime that is local (EST);

I want to specify a time in GMT timezone, and then convert it to the local TimeZone which is EST.
This appears to do what I want, but seems a long way to get there!
Is there a simpler way to achieve this:
public static TimeZoneInfo edtZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
public static TimeZoneInfo gmtZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
public static CultureInfo ci = CultureInfo.InvariantCulture;
DateTime edtStartDT = TimeZoneInfo.ConvertTime(DateTime.SpecifyKind(DateTime.Now.Date.Add(new TimeSpan(18, 00, 00)), DateTimeKind.Unspecified), gmtZone, edtZone);
This is probably what you are looking for:
// A timespan isn't really a time-of-day, but we'll let that slide for now.
TimeSpan time = new TimeSpan(18, 00, 00);
// Use the current utc date, at that time.
DateTime utcDateTime = DateTime.UtcNow.Date.Add(time);
// Convert to the US Eastern time zone.
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
DateTime easternDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, tz);
Note that we're pairing the current UTC date with the time you provided. Since US Eastern time is either 5 or 4 hours behind UTC, with 18:00 you will always get the same date. But if you were using a different time, such as 00:00, you would find that the resulting Eastern time was on the prior day. This is normal.
A couple of notes about your previous code:
The Windows Time Zone ID "Eastern Standard Time" refers to both EST and EDT. It really should have been called "Eastern Time". Don't let the name confuse the issue.
GMT and UTC are mostly the same for all modern usage. Unless you are referring to the time zone used in London, you should prefer the term UTC.
The Windows Time Zone ID "GMT Standard Time" is not actually for GMT/UTC. It is specifically the time zone used in London, which alternates between GMT (UTC+00:00) and BST (UTC+01:00). If you want a TimeZoneInfo representing UTC, the ID is just "UTC". (However, you don't really need that in this case.)
Your original code assumed used DateTime.Now.Date, which would assume the date in the computer's local time zone - which might not be either UTC or Eastern.
If you find yourself using DateTime.SpecifyKind, in most situations, you're probably doing something wrong. (The exception would be when loading or deserializing.)
Regarding my note about a TimeSpan not being a true time-of-day, this is how .NET would have you handle that:
DateTime time = DateTime.Parse("18:00:00", CultureInfo.InvariantCulture);
DateTime utcDateTime = DateTime.UtcNow.Date.Add(time.TimeOfDay);
Or even uglier in one line:
DateTime utcDateTime = DateTime.Parse("18:00:00", CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
Personally, I would rather use Noda Time, which has a separate LocalTime type explicitly for a time-of-day that's not bound to a specific date. I'm also working to get System.TimeOfDate and System.Date types added to the CoreCLR.

Categories

Resources