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.
Related
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"]
so I know the function DateTimeOffset.Now.Offset returns the offset from UTC and from wikipedia it states that GMT and UTC are the same. Therefore I would guess that DateTimeOffset.Now.Offset.ToString() would always return 00:00:00 if your timezone is (UTC) London. Am I correct in thinking this or would it return 01:00:00 when in daylight savings time?
DateTimeOffset.Now.Offset.ToString() would always return 00:00:00 if
your timezone is UTC.
Right. From documentation of DateTimeOffset.Offset property;
The difference between the current DateTimeOffset object's time value
and Coordinated Universal Time (UTC).
As you can see, it is normal to get 00:00:00 since London is UTC±00:00 which is what we are expected.
Am I correct in thinking this or would it return 01:00:00 when in
daylight savings time?
Exactly. From Wikipedia page of Daylight saving time;
Typically, users of DST adjust clocks forward one hour near the start
of spring and adjust them backward in the autumn to "normal" or
regular time.
By the way, you can't change your offset value with creating a Datetime which it is in your daylight saving time. OffSet value can be change only with changing your time zone manually or with daylight saving time. Your DateTimeOffset still has the same offset value even if you create it when times that daylight saving time.
Yes, UTC and GMT are the same. Just "GMT" term are not using most of computer science community.
I know there are bunch of solutions on converting timezone to timezone, but what I'd like to know whether we can get eastern time without making conversion from our local time.
Well yes and no, if you represent using GMT always then you don't need to convert until you need to show locality.
Time is always in GMT and services should run under the GMT Timezone because of this reason among others.
Receiving and Storing a time in anything other than GMT / UTC requires conversions.
Please note the Timezone of the server should also be set to GMT for things to work as I indicated.
When you set things up like this it thus becomes much easier to reference and compare times and dates in different calendar date formats also.
This is the reason why DateTime offset made its way into the framework and sql server. if not careful when the Timezone on a server changes so does all stored and loaded dates in local format.
A date comparison is a date comparison. DateTime just wraps a memory structure which is defined in ticks and makes methods to access commonly used parts of the memory e.g. day or year or Time or TimeOfDay etc.
Furthermore conversion is only possible if you know both the source and destination offsets and then the calculation is always as given is given by -1 * (sourceOffset - destOffset)
Where the part in parenthesis represents the time zone difference.
Where the DateTime you want to convert is in UTC and called theDate
DateTime eastern = TimeZoneInfo
.ConvertTimeFromUtc(
theDate,
TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"))
If theDate has a Kind of Local this will throw, only accepting Kind values of Utc or Unspecified. Of course, moving from local to UTC is easy and we could even check and do this conversion when required, but since you say you want this conversion "without making conversion from our local time" I'm assuming you have your times in UTC and hence having the exception would be better as it would warn you that times that should be UTC are being treat as local and a bug could pop up elsewhere.
This will use Eastern Daylight Time when theDate is a time when EDT is in effect, as that is the normal rule for EST. To use EST even when it is the summer you can create your own TimeZoneInfo:
TimeZoneInfo
.ConvertTimeFromUtc(
theDate,
TimeZoneInfo.CreateCustomTimeZone(
"Eastern Standard Time No Daylight Savings",
new TimeSpan(-5, 0, 0),
"Eastern Standard Time No Daylight Savings",
"Eastern Standard Time No Daylight Savings"))
Below is the method I use that takes in three inputs:
dateTimeInput which is a string that represents a date.
inputFormat are my format strings like this format: yyyy-MM-dd'T'HH:mm:sszzz.
timeZoneStandardName are unique timezone identifiers retrieved from var TimeZoneList = TimeZoneInfo.GetSystemTimeZones(); where the ID is retrieved via timeZoneList.Id.
I mainly used TimeZoneInfo to get my hours and minute offsets because it's very explicit as to what city/timezone it is, e.g. UTC is the input string.
public string unixTimeToDateTime(int unixInput, string outputFormat, string timeZoneStandardName)
{
// output format is "yyyy-MM-dd'T'HH:mm:sszzz"
TimeZoneInfo objTimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZoneStandardName);
if (outputFormat == "yyyy-MM-dd'T'HH:mm:sszzz")
{
DateTime UTCDate = Epoch.AddSeconds(unixInput);
DateTime localDate = TimeZoneInfo.ConvertTimeFromUtc(UTCDate, objTimeZoneInfo);
return localDate.ToString("yyyy-MM-dd'T'HH:mm:ss") + toTimeSpan(objTimeZoneInfo.GetUtcOffset(localDate).Hours, objTimeZoneInfo.GetUtcOffset(localDate).Minutes);
}
// output format is anything else
else
{
DateTime UTCDate = Epoch.AddSeconds(unixInput);
DateTime localDate = TimeZoneInfo.ConvertTimeFromUtc(UTCDate, objTimeZoneInfo);
return localDate.ToString(outputFormat);
}
}
Using this method on a component in Grasshopper 3D I am going to show you three examples:
One of a DST change on Mar 9th 2014
One of a DST change on Nov 2nd 2014 (which is the date I am having issues with)
And another date on Nov 2nd 2015 to demonstrate the weird discrepancy.
With an input of a list of UTC times (strategically place to force a DST offset on March 9th 2014), and outputting them to EST time, this example successfully outputs with the correct DST offset, as denoted on the most right panel (hint: 2014-03-09T01:00:00-05:00 to 2014-03-09T03:00:00-04:00).
Now here is the weird part.
If I do the November 2nd 2014 DST change, this is the output:
As you can tell on the most right panel, 01:00:00-05:00 exists TWICE. The desired output should go from 01:00:00-04:00 to 01:00:00-05:00!
As a comparison, here's November 2nd 2015:
November 2nd 2015 (which has the same DAYS though not years) has no problem switching DST without repeating an hour.
So my conclusion here is that for 2014, November 2nd, the DST increments an hour too early.
Is this a bug of DateTimeInfo? Why is it incrementing an hour too early? Is it my code? What gives?!
EDIT:
Using the Unixtimestamps and converting them to DateTime:
1414904400 is Sun, 02 Nov 2014 05:00:00 GMT
1414908000 is Sun, 02 Nov 2014 06:00:00 GMT
In your code, you have:
objTimeZoneInfo.GetUtcOffset(localDate)
Try instead:
objTimeZoneInfo.GetUtcOffset(UTCDate)
From the remarks on MSDN (emphisis mine):
If the dateTime parameter's Kind property does not correspond to the time zone object, this method performs the necessary conversion before returning a result. For example, this can occur if the Kind property is DateTimeKind.Local but the time zone object is not the local time zone. 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.
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