.ToUniversalTime() Is Incorrect? - c#

DateTime dt = new DateTime(1972, 4, 24, 0, 0, 0);
Response.Write("dt: " + dt.ToString("M/d/yyyy h:mm:ss tt") + "<br />");
Response.Write("dt.Kind: " + dt.Kind.ToString() + "<br />");
Response.Write("dt.ToUniversalTime(): " + dt.ToUniversalTime().ToString("M/d/yyyy h:mm:ss tt") + "<br />");
displays
dt: 4/24/1972 12:00:00 AM
dt.Kind: Unspecified
dt.ToUniversalTime(): 4/24/1972 7:00:00 AM
which is incorrect. April 24, 1972 at 12 PM Pacific is actually April 24, 1972 at 8 AM UTC.
I have confirmed the correct UTC conversion with iOS's internal UTC date conversation and www.timeanddate.com and the UTC time should be 8 AM. Am I doing something wrong?
The server is running in "Pacific Time" timezone so ToUniversalTime should be converting from Pacific timezone to UTC since Unspecified is treated like Local.

If you're on a machine that's not on Pacific Standard Time, you can see this behavior using the following code:
DateTime dt = new DateTime(1972, 4, 24, 0, 0, 0);
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
Console.WriteLine (TimeZoneInfo.ConvertTimeToUtc(dt, tz));
// 4/24/1972 7:00:00 AM
If you look at navy.mil's history of daylight saving time, you'll notice the following paragraph:
The Uniform Time Act of 1966 provided standardization in the dates of
beginning and end of daylight time in the U.S. but allowed for local
exemptions from its observance. The act provided that daylight time
begin on the last Sunday in April and end on the last Sunday in
October, with the changeover to occur at 2 a.m. local time.
And then a little later on:
In 1986, a law was passed that shifted the starting date of daylight
time to the first Sunday in April, beginning in 1987
So the DST switchover wasn't the first Sunday in April until 1987, but for some reason .NET is acting as if it was.
Timeanddate.com's history of DST seems to agree, and lists April 30, 1972 (the last Sunday in April) as the date clocks were turned forward one hour (to UTC-7).
Microsoft's DST adjustment rules for times before 1987 appear to be wrong (and I'm not the only one who thinks so).
Here's what TimeZoneInfo lists as the rules for PST:
Basically, Microsoft has ignored the historical rules and chosen to use rules put into effect in 1987 for dates that happened before those rules even existed.
Essentially your date (in 1972) is being handled incorrectly by Microsoft's TimeZoneInfo adjustment rules.
If you're looking for a library that handles these types of time zone rules much better, check out NodaTime, which handles this particular case correctly:
var pacific = DateTimeZoneProviders.Tzdb["America/Los_Angeles"];
LocalDateTime localDateTime = new LocalDateTime(1972, 4, 24, 0, 0);
ZonedDateTime zonedDateTime = pacific.AtStrictly(localDateTime);
DateTime utcDateTime = zonedDateTime.ToDateTimeUtc();
Console.WriteLine(utcDateTime);
// 4/24/1972 8:00:00 AM

Related

Get Datetime Offset for Timezone

I have a code where I need to find current offset from UTC for Central European Timezone.
My code is deployed in azure app service.
var offset = DateTimeOffset.Now.Offset.Hours + ":" +
DateTimeOffset.Now.Offset.Minutes;
The problem with above code is, it relies on server time, Even though my App Service is in West Europe, the time zone of Azure App Service is always UTC .
One solution is to change Azure App Service TimeZone to Desired TimeZone and it will work, but i am also looking at getting the offset using code.
Remember I cannot use system datetime to get offset as it's always UTC.
Can I get Current Central Europe DatetimeOffset irrespective of system date time?
You can do something like this
TimeZoneInfo cet = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
DateTimeOffset offset = TimeZoneInfo.ConvertTime(DateTime.Now, cet);
As described here.
If you're not sure about a TimeZoneId you can use GetSystemTimeZones() to find it.
An alternative, as described here, would be to do something like this
DateTime nowCet = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.Now,
"Central European Standard Time");
The nice thing about this is you can easily calculate the difference between two time zones like, for example
DateTime newYork = new DateTime(2017, 10, 04, 12, 23, 00);
DateTime berlin = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(newYork,
"Eastern Standard Time", "Central European Standard Time");
TimeSpan diff = berlin - newYork;
You can use TimeZoneInfo:
TimeZoneInfo cetInfo = TimeZoneInfo.FindSystemTimeZoneById("Central Europe Standard Time");
DateTimeOffset cetTime = TimeZoneInfo.ConvertTime(DateTimeOffset.Now, cetInfo);
With correct daylight saving offsets (example in CET):
DateTime utcDT = DateTime.UtcNow;
DateTime cetDT = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(utcDT, "UTC", "Central European Standard Time");
DateTimeOffset utcDTO = new DateTimeOffset(utcDT);
DateTimeOffset cetDTO = new DateTimeOffset(cetDT, cetDT - utcDT);
// Result (with daylight saving)
//
// 2021. 06. 24. 7:42:09
// 2021. 06. 24. 9:42:09
// 2021. 06. 24. 7:42:09 +00:00
// 2021. 06. 24. 9:42:09 +02:00

Noda Time Instant to CET

I'm using the Noda Time libarary (v 2.0.3) for date time handling in a .net core project. However; I'm having some issues converting an instant to a CET date-time.
I'm fairly new to Noda Time, possibly using it wrong (is not CET tz-db entry referring to CET at all)?
Example code
// get current system instant
var systemInstant = SystemClock.Instance.GetCurrentInstant();
// get oslo zoneddatetime from instant
var osloDateTime = systemInstant.InZone(DateTimeZoneProviders.Tzdb["Europe/Oslo"]);
// get CET(?) zoneddatetime from instant
var cetDateTime = systemInstant.InZone(DateTimeZoneProviders.Tzdb["CET"]);
// output
Debug.WriteLine(osloDateTime.ToString());
Debug.WriteLine(cetDateTime.ToString());
The output from the example code above gives me:
2017-06-16T22:28:16 Europe/Oslo (+02)
2017-06-16T22:28:16 CET (+02)
Acutally I was expecting the CET zoned time to be 21:28:16 (UTC+1) and not 22:28:16 (UTC+2). UTC+1 is also what the Time and date website displaying.
The Zone line of the europe file in the IANA time zone database contains this single line for the CET zone ID:
Zone CET 1:00 C-Eur CE%sT
Then the end recurrence of the C-Eur rule is this pair of lines:
Rule C-Eur 1981 max - Mar lastSun 2:00s 1:00 S
Rule C-Eur 1996 max - Oct lastSun 2:00s 0 -
So it goes into UTC+2 at 2am on the last Sunday of March each year, and back to UTC+1 at 2am on the last Sunday of October each year.
Note that the "abbreviation" in the CET time zone will vary between "CET" and "CEST" - and that may be what's misleading you. But Noda Time is following the definition of the ID "CET" as per the IANA database.
This is just another reason to avoid using the abbreviations, and instead to use full zone IDs such as Europe/Oslo which are unambiguous. I would suggest avoid trying to use the concept of "a CET date-time" entirely.
If you look at the result of DateTimeZoneProviders.Tzdb["CET"], it has a minimum offset of +1 and a maximum offset of +2, so I think it is refering to the actual central European timezone (including the Central European Summer Time [CEST]).
As the date you selected falls into the summer time range, it is UTC+2.
If you try the following you get UTC+1 for CET:
Instant.FromDateTimeUtc(new DateTime(2017, 01, 01, 12, 0, 0, DateTimeKind.Utc)).InZone(DateTimeZoneProviders.Tzdb["CET"]

UTC converted to Central Europe Standard Time is 2 hours in front not 1

I'm trying to understand why my date is wrong:
DateTime databaseUtcTime = new DateTime(2016, 8, 15, 10, 20, 0, DateTimeKind.Utc);
var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Central Europe Standard Time");
var testDateTime = TimeZoneInfo.ConvertTimeFromUtc(databaseUtcTime, timeZone);
testDateTime outputs 15/08/2016 12:20:00 rather than 15/08/2016 11:20:00
why is this? Shouldn't it be 1 hour forward from UTC, not 2 ?
EDIT----
Thanks Jon Skeet,
If this helps anyone, i resorted to using:
if(testDateTime.IsDaylightSavingTime())
{
testDateTime = testDateTime.AddHours(-1);
}
Albeit you not knowing the context, this could be helpful to know how to get rid of daylight saving time when running certain explicit testing on time.
The time zone with an ID of "Central Europe Standard Time" is just the one used by central Europe... it doesn't really mean standard time.
As central Europe is observing daylight savings at the moment, the offset really is UTC+2.
It's very unfortunate that the IDs used in Windows time zones are misleading like this... but the TimeZoneInfo implementation itself is correct.
(This isn't all that's wrong with Windows time zone names... see Matt Johnson's post on the matter for more...)

Different time output for ToUniversalTime

I cannot understand or find any information that could explain why there are two different time component output (12p.m and 11 a.m) for the following. Can somebody please explain.
DateTime d1 = new DateTime(2015, 05, 15).ToUniversalTime();
DateTime d2 = new DateTime(2015, 02, 02).ToUniversalTime();
Console.WriteLine(d1.ToString()); //OUTPUTS - 1/05/2015 12:00:00 p.m.
Console.WriteLine(d2.ToString()); //OUTPUTS - 1/02/2015 11:00:00 a.m.
The ToUniveralTime method converts from the local time zone where the code is running, to UTC.
Since time zones can change their offsets from UTC at different times of the year, the value can easily be different between two different dates - especially since one date is in the winter, and the other is in the summer, due to daylight saving time.
See also, the DST tag wiki, and "time zone != offset" in the timezone tag wiki.

Getting local hour with NodaTime

New to NodaTime and I chose to use it instead of the BCL libraries because it removes a lot of ambiguity when dealing with dates. However, I can't seem to get it to do what I want. I have a date and time specified by year, month, day, hours, and minutes. I also have two timezones for which I need to display the "clock time". For example, if my input is December 15, 2015 at 3:30 PM and my two timezones are central standard and eastern standard (one hour apart), I expect my output to be
12/15/2015 3:30 PM Central
12/15/2015 4:30 PM Eastern
But I can only seem to get the central (local to me, if that matters) timezone. Here's my code:
var localDateTime = new LocalDateTime(
year: 2015,
month: 12,
day: 15,
hour: 15,
minute: 30,
second: 0
);
var centralTimeZone = DateTimeZoneProviders.Bcl.GetZoneOrNull("Central Standard Time");
var easternTimeZone = DateTimeZoneProviders.Bcl.GetZoneOrNull("Eastern Standard Time");
var centralTime = centralTimeZone.AtLeniently(localDateTime);
var easternTime = easternTimeZone.AtLeniently(localDateTime);
It seems that centralTime and easternTime are both ZonedDateTime objects whose times are 2015-12-10T15:30 with the correct offset i.e. centralTime is -6 and easternTime is -5.)
I just can't figure out how to get the output I want.
It sounds like your initial date/time is actually in Central time - so once you've performed the initial conversion, you can just say "Hey, I want the same instant in time, but in a different time zone" - which isn't the same as "I want a different instant in time for the same local time". You want:
var centralTime = centralTimeZone.AtLeniently(localDateTime);
var easternTime = centralTime.InZone(easternTimeZone);

Categories