Noda Time Instant to CET - c#

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"]

Related

Getting wrong UTC offset hours for exact daylight date

I am trying to get the offset hours of a particular time for EST timezone like this
var est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
est.GetUtcOffset(new DateTime(2020, 3, 9)).Hours;
This works fine and returns me -4, which is right for 9th March 2020 because the daylight saving changes on 8th March 2020 02:00:00, but when I try to run the same code for date 8th March 2020 02:00:00, it returns me -5, while my understanding is that from 2am of 8th march it should start returning me -4.
To get the -4, I have to run it for 3am, that is one hour after the daylight changing.
But why is it so? Why do I have to add one hour to make daylight change take effect?
From the Remarks section of the TimeZoneInfo.GetUtcOffset docs:
... If dateTime is ambiguous, or if the converted time is ambiguous, this method interprets the ambiguous time as a standard time. If dateTime is invalid, this method returns a TimeSpan object that reflects the difference between UTC and the time zone's standard time.
The datetime 2020-03-08 02:00:00, is invalid in the US Eastern time zone, because the clocks advance from 01:59:59 to 03:00:00 on that day. Thus, the GetUtcOffset uses its described behavior of returning the standard time offset. You can detect this if you first call TimeZoneInfo.IsInvalidTime.
You might want to also use other methods from TimeZoneInfo to apply specific behavior. For example, it's commonly desired in appointment scheduling scenarios to use the behavior I presented in the ToDateTimeOffset extension method in this other answer.

Check if TimeSpan falls in Daylight Saving transition

I have flight arrival time and the flight departure time. I have to check if the flight time falls in the daylight saving transition (Equinox Transition). If the hour is forward I need to add an hour in flight time else if the hour is reverted I need to deduct an hour from flight time.
In TimeZoneInfo class we do have IsDaylightSavingTime but it only says either if the time is in Daylight saving or not.
I need to check that my timespan is either effected by DayLight Saving Transition or not.
Update1: The transition is observed in March and November but the date changes every year so I can't hard code any date. I need to get the specific date of the year at which the EQUINOX will occur.
Update2: Datetime is local not UTC, as the flight arrival and departure are from the same airport.
Data:
Flight Departure Time : 19 March 2019 23:00
Flight Arrival Time : 20 March 2019 08:00
Flight Time : 7 Hours but due to EQUINOX its 8 Hours as the hour was forward at
20 March 2019 05:58
The GetAdjustmentRules provide you the info you are looking for:
Provides information about a time zone adjustment, such as the transition to and from daylight saving time.
Example of the output of the sample code in the above link:
W. Europe Standard Time Adjustment rules
Start Date: Monday, January 01, 0001
End Date: Friday, December 31, 9999
Time Change: 1:00 hours
Annual Start: The Last Sunday of March at 2:00 AM
Annual End: The Last Sunday of October at 3:00 AM
Also the IsInvalidTime say you if the specific value is invalid due to daylight transition.
You can convert it to a valid DateTime by using:
TimeZoneInfo timeZone = TimeZoneInfo.FindSystemTimeZoneById(id);
if (timeZone.IsInvalidTime(dateTime))
dateTime = TimeZoneInfo.ConvertTime(dateTime.ToUniversalTime(), timeZone);
Finally, my suggestion is to store and evaluate DateTime as UTC in order to avoid ambiguity. You can convert it to local time for GUI purpose only.
EDIT: here is there an example of how to use GetAdjustmentRules.

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

.ToUniversalTime() Is Incorrect?

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

Convert same time to different time zone

I am trying to convert times to different time zones, but not the way you're thinking. I need to convert a DateTime that is 9am EST to 9am CST on the UTC for example. The timezones are variable so just adding/subtracting hours doesn't seem correct way to do it with NodaTime
Fri, 21 Feb 2014 21:00:00 EST = 1393034400 Epoch Timestamp
convert to
Fri, 21 Feb 2014 21:00:00 CST = 1393030800 Epoch Timestamp
If I understand the question correctly, it sounds like you're trying to convert a date/time in one time zone to another one that has the same local time and a different time zone; that is, a different point in time.
You can do this with Noda Time by combining the LocalDateTime with the new zone. For example, given something like the following:
Instant now = SystemClock.Instance.Now;
DateTimeZone eastern = DateTimeZoneProviders.Tzdb["America/New_York"];
ZonedDateTime nowEastern = now.InZone(eastern);
nowEastern is the time now in the America/New_York time zone. If we print nowEastern directly to the console, we'll see something like 2014-02-22T05:18:50 America/New_York (-05).
As an aside, "EST" and "CST" aren't time zones: they're non-unique abbreviations for a particular offset within a time zone; America/New_York and America/Chicago are probably representative of what we think of as "Eastern" and "Central", though (or you could use something like UTC-05:00 if you really wanted EST even when daylight savings time was in effect).
Given a ZonedDateTime in any time zone, we can convert it to a ZonedDateTime with the same local time and a specified time zone as follows:
DateTimeZone central = DateTimeZoneProviders.Tzdb["America/Chicago"];
ZonedDateTime sameLocalTimeCentral = nowEastern.LocalDateTime.InZoneStrictly(central);
This gives us a ZonedDateTime with the same local time, but a different time zone. With the input above, the result would be 2014-02-22T05:18:50 America/Chicago (-06).
Note that I'm using InZoneStrictly. This will throw an exception if the local time is ambiguous or invalid (for example, during daylight savings transitions). If that's unacceptable, you could use InZoneLeniently, which picks the earliest valid ZonedDateTime on or after the given local time, or InZone, which allows you to specify your own rules in those cases.
On Msdn website you can find all you need.
Small example:
DateTime dateNow = DateTime.Now;
Console.WriteLine("The date and time are {0} UTC.",
TimeZoneInfo.ConvertTimeToUtc(dateNow));
Go to the link for more details on what you want, I can't give you more with that small description

Categories