We are now in the summer time (UTC+01:00) but I need always winter time whereever I am.
For example now time is 08:05 and winter time is 07:05
I can find whether it is saving time or not by using this function
DateTime.Now.IsDaylightSavingTime()
and here is the delta (1 hour change)
TimeZoneInfo.Local.GetAdjustmentRules()[0].DaylightDelta;
so if I do something like that, is it correct?
var winterTime= DateTime.Now;
if (DateTime.Now.IsDaylightSavingTime())
{
winterTime = DateTime.Now.AddHours(-1 * delta.Hours);
}
or is there another way to do? (without using any 3rd solution)
Edit: The reason why I am asking this is that we are flashing a firmware to a nfc device and some dates in the devices should be in winter time. Thats why our tool should write winter time as paramter to the device.
In case DaylightDelta is not whole hours better use the TimeSpan directly. Also you need to find the rule in GetAdjustmentRules() that matches the current date. GetAdjustmentRules() returns both future and historical rules.
var now = DateTime.Today;
var rule = TimeZoneInfo.Local.GetAdjustmentRules().Where(x => now >= x.DateStart && now <= x.DateEnd).First();
winterTime = DateTime.Now - rule.DaylightDelta;
We are now in the summer time (UTC+01:00) but I need always winter time whereever I am.
...
... The reason why I am asking this is that we are flashing a firmware to a nfc device and some dates in the devices should be in winter time. Thats why our tool should write winter time as paramter to the device.
It sounds like you simply want to obtain the UTC time to set on the device. If so, just use DateTime.UtcNow. There is no need to concern yourself with time zones.
However, if you actually wanted to take the standard time (or winter time) for whatever local time zone you're in (that would seem to be a bit silly because that's not how local times work), but if you really are sure you want that, you can do it without querying adjustment rules. Instead, try this:
DateTime dt = DateTimeOffset.UtcNow.ToOffset(TimeZoneInfo.Local.BaseUtcOffset).DateTime;
This takes the current UTC time, applies the base offset of the local time zone, and then gives you back a DateTime with .Kind == DateTimeKind.Unspecified. The base offset is the offset from UTC that applies during standard time without any adjustment rules applied for DST.
Related
I've read a few posts about similar subjects but nothing seems to answer this question. My database has the following information about a time
Day of the week (a number between 0-6)
Time (a number of milliseconds since midnight in the users local time)
UTC offset ( number of hours different to UTC )
DST Observed (boolean stating if DST is observed in that time zone)
This data represents opening hours. So there is a time for each day. I want to display that time in the users local time making the assumption that each day is in the future
int dayOffset = availability.Day - (int)now.DayOfWeek
if (dayOffset < 0)
dayOffset += 7;
I'm really struggling to get my head around time zones and handling when one time zone might be observing DST while another maybe DOES observe DST but hasn't yet.
My main issue at the moment is I think I need to create a DateTimeOffset object for the non-local time but I'm not sure how to do that as I don't know if DST is in effect or not.
I hope I'm making myself clear. It really is a mind-bending experience working with dates and time!
As indicated by other answers, the usual solution to handling DateTime across time zones would be to store UTC times.
However, considering that you are not referencing an absolute time at a specific date, but instead are referring to a time at an infinite number of days in a specific time zone; storing the time as an UTC time doesn't make sense anymore, since the UTC time (even if we discard the date) would be different depending on the date, due to DST.
The best way to store the time is fairly close to what you have done already.
Your problem is that the time zone information you are storing at the moment is ambiguous, as it does not refer to a specific time zone, but instead refers to properties of the time zone.
To solve this problem, simply store the time zone identifier instead of the UTC offset and DST boolean.
It is now possible for us to construct the DateTime object and convert it to any time zone by using the TimeZoneInfo class:
int dayOffset = availability.Day - (int)DateTime.Today.DayOfWeek;
if (dayOffset < 0)
{
dayOffset += 7;
}
var openingHourStart = DateTime
.SpecifyKind(DateTime.Today, DateTimeKind.Unspecified)
.AddDays(dayOffset)
.AddMilliseconds(availability.Time);
var sourceTimeZone = TimeZoneInfo.FindSystemTimeZoneById(availability.TimeZoneId);
var userTimeZone = TimeZoneInfo.Local;
var convertedOpeningHourStart = TimeZoneInfo.ConvertTime(openingHourStart,
sourceTimeZone,
userTimeZone);
Give a try to Quartz.NET.
It implements evaluation of CronExpressions, and even triggers to fire events at the given time. It can evaluate the next time an event will occur. This may help you out calculating the opening times.
Also, take a look at the cronmaker website: there you can understand the full potential of CronExpressions.
The CronExpressionDescriptor is a DotNET library for transforming CronExpressions into human readable strings.
Another library which I haven't tried yet is [HangFire].(https://www.hangfire.io/)
In this forum post you can find some discussion on how HangFire implements evaluation of RecurringJobs in local timezone with DST, which I believe is a solution for what you are looking for.
A comment to another answer made the problem a little bit more clear.
So, first and foremost, do store only UTC in your database. Really.
Now, since you are not interested in the actual dates, since you are storing working schedules that repeat weekly, the date only becomes relevant once you want to present your times - and when you put them in your database.
So let's first see how you get your times into your database correctly. I'm assuming a user will enter times in their own locale.
Make sure you first create a (localised) DateTime consisting of the current date and the given time (from the user), and transform that to a UTC DateTime (you can keep the current date, it doesn't matter):
var utcDateTime = DateTime.Now.Date
.AddHours(userHours)
.AddMinutes(userMinutes)
.ToUniversalTime();
Now when you are presenting these times to the user, simply go the other way:
var userDateTime = DateTime.Now.Date
.AddHours(utcDateTime.Hour)
.AddMinutes(utcDateTime.Minute)
.ToLocalTime();
And then you can use the userDateTime.Hour and .Minute for display purposes.
You should be leveraging DateTime.ToLocalTime() and TimeZoneInfo.ConvertTimeToUtc() in C# - see https://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime(v=vs.110).aspx.
If you want to store only times that you're open from Monday to Sunday, fine. Have a simple data table to describe only the time for each day (0 = Sunday through 7 = Saturday -- this is .Net's DayOfWeek enumeration). Your lookup table might look like:
0 null
1 08:00:00
2 08:00:00
3 08:00:00
4 08:30:00
5 08:30:00
6 10:30:00
(Use whatever data type works for you--SQL Server 2008+ has a TIME data type, for example. Null can be used for Closed on that day--i.e., no open time.)
When it comes time to display YOUR time to any other user, use must create your UTC time on-the-fly at the moment you are displaying information to the local user.
Conyc provided one approach. My approach uses simple date/time strings. To use my approach, just store time values per day in your database. Then you can look up the open time for any given day. To express that time for another user in any locale, use this code to convert your time to UTC (you can substitute the "08:00:00 AM" string value with a string variable that you populated after looking up the open time in your database):
var StoreOpenTimeInUtc = TimeZoneInfo.ConvertTimeToUtc(Convert.ToDateTime("08:00:00 AM"));
To look up the open time in your database for a particular day in the future, you will need to concatenate the date to your time value, like this:
var StoreOpenTimeInUtc = TimeZoneInfo.ConvertTimeToUtc(Convert.ToDateTime("04/28/2018 08:00:00 AM"));
Once you have an accurate StoreOpenTimeInUtc variable, you can use that as the UTC value on someone else's machine who is anywhere else on planet earth. To convert that UTC value to their local time, use the .NET ToLocalTime() method:
var OpenTimeForLocalUser = StoreOpenTimeInUtc.ToLocalTime();
Note that this approach requires you to store only the open times as shown above. You don't have to worry about dates, local offsets from UTC, or anything else. Just leverage ConvertTimeToUtc() and ToLocalTime() as shown.
I'm storing a product in db. All dates (sql server datetime) are UTC and along with the dates I store the time zone id for that product. User enters the dates when product is available "from" and "until" in the listing.
So I do something like:
// Convert user's datetime to UTC
var userEnteredDateTime = DateTime.Parse("11/11/2014 9:00:00");
// TimeZoneInfo id will be stored along with the UTC datetime
var tz = TimeZoneInfo.FindSystemTimeZoneById("FLE Standard Time");
// following produces: 9/11/2014 7:00:00 AM (winter time - 1h back)
var utcDateTime = TimeZoneInfo.ConvertTimeToUtc(userEnteredDateTime, tz);
and save the record. Let's assume user did this on the 1st of August, while his time zone offset to UTC is still +03:00, nevertheless the saved date for the future listing has the correct +02:00 value because conversion took into consideration the "winter" time for that period.
Question is what datetime value will I get if I will attempt to convert that product's "from" and "until" date to product's local time zone on 11/11/2014 if, for example, due to some new rules the transition to winter time was abandoned, thus the time zone is still +03:00 instead of +02:00?
// Convert back
var userLocalTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, tz);
will I get 10AM or correct 9AM because OS/.NET patch will handle this?
Thank you!
P.S.: TimeZoneInfo has ToSerializedString() method, if I rather store this value instead of timezone id, will this guarantee that via UTC datetime + serialized timezoneinfo I will always be able to convert to the user's original datetime input?
In the scenario you describe, you would get 10:00 AM. The time zone conversion function would not have any idea that the value was originally entered as 9:00 AM, because you only saved the UTC time of 7:00 AM.
This illustrates one of the cases where the advice "always store UTC" is flawed. When you're working with future events, it doesn't always work. The problem is that governments change their mind about time zones often. Sometimes they give reasonable notice (ex. United States, 2007) but sometimes they don't (ex. Egypt, 2014).
When you made the original conversion from local time to UTC, you intentionally decided to trust that the time zone rules would not change. In other words, you decided that you would assign the event to the universal timeline based solely on the time zone rules as you knew them at that time.
The way to avoid this is simple: Future events should be scheduled in local time. Now, I don't mean "local to your computer", but rather "local to the user", so you will need to know the user's time zone, and you should also store the ID of the time zone somewhere.
You'll also need to decide what you want to do if the event falls into the spring-forward or fall-back transition for daylight saving time. This is especially important for recurrence patterns.
Ultimately though, you'll need to figure out when to run the event. Or in your case, you'll need to decide if the event has passed or not. There are a few different ways you can accomplish this:
Option 1
You can calculate the corresponding UTC value for each local time and keep it in a separate field.
On some cycle (daily, weekly, etc) you can recalculate upcoming UTC values from their local values and your current understanding of the time zone rules. Or, if you apply time zone updates manually, you can choose to recalculate everything at that time.
Option 2
You can store the values as a DateTimeOffset type instead of a DateTime. It will contain the original local time, and the offset that you calculated based on the time zone rules as you knew them at time of entry.
DateTimeOffset values can easily be coerced back to UTC, so they tend to work very well for this. You can read more in DateTime vs DateTimeOffset.
Just like in option 1, you would revisit the values periodically or after time zone data updates, and adjust the offsets to align with the new time zone data.
This is what I usually recommend, especially if you're using a database that has support for DateTimeOffset types, such as SQL Server or RavenDB.
Option 3
You can store the values as a local DateTime.
When querying, you would calculate the current time in the target time zone and compare against that value.
DateTime now = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, targetTZ);
bool passed = now >= eventTime;
The down side of this option is that you may have to make lots queries if you have events in lots of different time zones.
You may also have issues with values close to the fall-back DST transition, so be careful if you use this approach.
I recommend against the idea of serializing the time zone itself. If the time zone has changed, then it has changed. Pretending that it hasn't isn't a good workaround.
I am trying to program a world clock using Nodatime, and I have searched the web for samples on how to use the library and I have read the documentation, and it says that the class Instant is simply a number of "ticks" since some arbitrary epoch the Unix epoch, which corresponds to midnight on January 1st 1970 UTC. Well, I empirically guessed that if I used as an Instant a GMT value, then I could calculate with it the time values for each time zone (creating the world clock), and it worked. The problem that I have, is that I don't know a simple way to calculate the GMT time (or GMT instant) from the local time, my time zone is "America/Mexico_City", so my question is, is there a shortcut already defined in Nodatime to get the GMT time from a local time, or in the other hand, is there a simple way to implement the "Instant GetInstantGMT()" function (the function has to take in count the day light saving time issues)?
If you are just looking for the "current" instant, such represents now, then use:
Instant now = SystemClock.Instance.Now;
Calling it a "GMT instant" is redundant, since the Instant type is representing a universal moment in time without regard to time zone. It is (mostly) equivalent to UTC - which is essentially the same as GMT. In other words, you couldn't create an Instant that wasn't GMT.
Another way to think about an Instant is as if it were a DateTime whose .Kind property was permanently fixed to DateTimeKind.Utc and could not represent anything else.
Also, depending on exactly how your application is architected, you may find it useful to use the IClock interface instead:
IClock clock = SystemClock.Instance;
Instant now = clock.Now;
This would allow you to replace the system clock with a fake clock during unit testing.
Regarding how to go from a specific local time to an Instant, you would do something like this:
LocalDateTime ldt = new LocalDateTime(2013, 1, 1, 0, 0, 0);
DateTimeZone tz = DateTimeZoneProviders.Tzdb["America/Mexico_City"];
ZonedDateTime zdt = ldt.InZoneLeniently(tz);
Instant instant = zdt.ToInstant();
Note that I used InZoneLeniently in the conversion. This makes certain assumptions about how to translate from a local time that might be invalid or ambiguous due to daylight saving time. This might be acceptable, or you might instead prefer to use InZoneStrictly which will throw exceptions, or InZone which allows you to pass a resolver function so you can provide your own logic.
Does C# take Daylight savings time into consideration when converting between timezones?
I have a source date which is in the current time in London, and I want to convert it to my timezone (CET). Here's the code I'm using.
DateTime time = DateTime.ParseExact(timeString, "HH:mm", null);
time = DateTime.SpecifyKind(time, DateTimeKind.Unspecified);
//Convert it to the right timezone. It is currently in GMT
TimeZoneInfo gmt = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
TimeZoneInfo current = TimeZoneInfo.Local;
DateTime utc = TimeZoneInfo.ConvertTimeToUtc(time, gmt);
DateTime local = TimeZoneInfo.ConvertTimeFromUtc(utc, core.startTime = local;
It's currently working well. However when DST rears its ugly head, will it continue working or will it break horribly? I'm a bit wary of TimeZones due to having had tons of issues in the past.
The short answer is "Not everywhere, not perfectly."
TimeZoneInfo.GetAdjustmentRules will give you a collection of rules about changes in the DST offset and when they come into and go out of effect.
However, your user can still cock things up by un-checking "Automatically adjust for daylight savings" in Windows Control Panel Date and Time. If DST is turned off in Windows then you will get an empty collection of adjustment rules.
If you want automagical application of adjustment rules you must use DateTime objects for which the DateTimeKind has been set. If DST is turned off this will be honoured in the conversion.
GMT is solar time at the Royal Observatory in Greenwich. The British invented the whole business of timezone offsets from a date-line because they were the first to coordinate anything on a global scale. In halcyon days of yore they had a planet-wide navy of sailboats and no radio. Lag on orders was weeks or months, so consistent, precise, global time-keeping was invented by the only people with a frame of reference larger than a planet - the Royal Astronomers.
The moon's tidal forces are slowing the Earth's rotation. It takes a lot of juice to slosh an ocean of water up and down and it's not magic, it comes from the spin moment of the planet.
Also the duration of a solar orbit isn't constant either, so we have leap seconds every now and then to synch the calendar with planetary reality. Sidereal time on the other hand has no such foolishness, so we drift away from it. Then there is relativistic drift. GPS satellites move so fast they actually have to compensate for slight time-warping.
Does C# take Daylight savings time into consideration when converting between timezones?
Yes, assuming your computer is kept updated as the timezone info is sometimes updated with windows update. It should still work even without windows update if the country hasn't changed their DST time periods (this happened in Australia recently)
I have a source date which is in the current time in London, and I want to convert it to my timezone (CET)
What do you mean 'source date which is the current time in London' ?
Always store your dates as UTC and convert them 'at the last minute' to the desired local time.
If you're wondering what happens when daylight savings changes then you can test this by changing the clock on your computer.
Be careful when working with dates before 1987 in .NET. Default AdjustmentRules in TimeZoneInfo for the time zone that you are interested in may not be sufficient for your purpose. Read more here : http://blog.appliedis.com/2013/03/06/beware-daylight-saving-time-transitions-in-dot-net/
At least in .net 4.5 TimeZoneInfo does handle daylight saving time.
The easiest way to check it is to compare BaseUtcOffset and GetUtcOffset
var baseOffset = timeZoneInfo.BaseUtcOffset;
var currentOffset = timeZoneInfo.GetUtcOffset(currentLocalTime);
var isDst = currentOffset > baseOffset;
var delta = currentOffset - baseOffset;
This is much easier than dealing with AdjustmentRule which you don't need if you are only interested in adjusting a DateTime for DST.
Btw GMT is obsolete and is replaced by UTC.
I am storing date/times in the database as UTC and computing them inside my application back to local time based on the specific timezone. Say for example I have the following date/time:
01/04/2010 00:00
Say it is for a country e.g. UK which observes DST (Daylight Savings Time) and at this particular time we are in daylight savings. When I convert this date to UTC and store it in the database it is actually stored as:
31/03/2010 23:00
As the date would be adjusted -1 hours for DST. This works fine when your observing DST at time of submission. However, what happens when the clock is adjusted back? When I pull that date from the database and convert it to local time that particular datetime would be seen as 31/03/2010 23:00 when in reality it was processed as 01/04/2010 00:00.
Correct me if I am wrong but isn't this a bit of a flaw when storing times as UTC?
Example of Timezone conversion
Basically what I am doing is storing the date/times of when information is being submitted to my system in order to allow users to do a range report. Here is how I am storing the date/times:
public DateTime LocalDateTime(string timeZoneId)
{
var tzi = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
return TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tzi).ToUniversalTime().ToLocalTime();
}
Storing as UTC:
var localDateTime = LocalDateTime("AUS Eastern Standard Time");
WriteToDB(localDateTime.ToUniversalTime());
You don't adjust the date for DST changes based on whether you're currently observing them - you adjust it based on whether DST is observed at the instant you're describing. So in the case of January, you wouldn't apply the adjustment.
There is a problem, however - some local times are ambiguous. For example, 1:30am on October 31st 2010 in the UK can either represent UTC 01:30 or UTC 02:30, because the clocks go back from 2am to 1am. You can get from any instant represented in UTC to the local time which would be displayed at that instant, but the operation isn't reversible.
Likewise it's very possible for you to have a local time which never occurs - 1:30am on March 28th 2010 didn't happen in the UK, for example - because at 1am the clocks jumped forward to 2am.
The long and the short of it is that if you're trying to represent an instant in time, you can use UTC and get an unambiguous representation. If you're trying to represent a time in a particular time zone, you'll need the time zone itself (e.g. Europe/London) and either the UTC representation of the instant or the local date and time with the offset at that particular time (to disambiguate around DST transitions). Another alternative is to only store UTC and the offset from it; that allows you to tell the local time at that instant, but it means you can't predict what the local time would be a minute later, as you don't really know the time zone. (This is what DateTimeOffset stores, basically.)
We're hoping to make this reasonably easy to handle in Noda Time, but you'll still need to be aware of it as a possibility.
EDIT:
The code you've shown is incorrect. Here's why. I've changed the structure of the code to make it easier to see, but you'll see it's performing the same calls.
var tzi = TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time");
var aussieTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tzi);
var serverLocalTime = aussieTime.ToLocalTime();
var utcTime = serverLocalTime.ToUniversalTime();
So, let's think about right now - which is 13:38 in my local time (UTC+1, in London), 12:38 UTC, 22:39 in Sydney.
Your code will give:
aussieTime = 22:39 (correct)
serverLocalTime = 23:39 (*not* correct)
utcTime = 22:39 (*not* correct)
You should not call ToLocalTime on the result of TimeZoneInfo.ConvertTimeFromUtc - it will assume that it's being called on a UTC DateTime (unless it's actually got a kind of DateTimeKind.Local, which it won't in this case).
So if you're accurately saving 22:39 in this case, you aren't accurately saving the current time in UTC.
It's good that you are attempting to store the dates and times as UTC. It is generally best and easiest to think of UTC as the actual date and time and local times are just pseudonyms for that. And UTC is absolutely critical if you need to do any math on the date/time values to get timespans. I generally manipulate dates internally as UTC, and only convert to local time when displaying the value to the user (if it's necessary).
The bug that you are experiencing is that you are incorrectly assigning the local time zone to the date/time values. In January in the UK it is incorrect to interpret a local time as being in a Summertime time zone. You should use the time zone that was in effect at the time and location that the time value represents.
Translating the time back for display depends entirely on the requirements of the system. You could either display the times as the user's local time or as the source time for the data. But either way, Daylight Saving/Summertime adjustments should be applied appropriately for the target time zone and time.
You could work around this by also storing the particular offset used when converting to UTC. In your example, you'd store the date as something like
31/12/2009 23:00 +0100
When displaying this to the user, you can use that same offset to convert, or their current local offset, as you choose.
This approach also comes with its own problems. Time is a messy thing.
The TimeZoneInfo.ConvertTimeFromUtc() method will solve your problem:
using System;
class Program {
static void Main(string[] args) {
DateTime dt1 = new DateTime(2009, 12, 31, 23, 0, 0, DateTimeKind.Utc);
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
Console.WriteLine(TimeZoneInfo.ConvertTimeFromUtc(dt1, tz));
DateTime dt2 = new DateTime(2010, 4, 1, 23, 0, 0, DateTimeKind.Utc);
Console.WriteLine(TimeZoneInfo.ConvertTimeFromUtc(dt2, tz));
Console.ReadLine();
}
}
Output:
12/31/2009 11:00:00 PM
4/2/2010 12:00:00 AM
You'll need .NET 3.5 or better and run on an operating system that keeps historical daylight saving time changes (Vista, Win7 or Win2008).
Correct me if I am wrong but isn't
this a bit of a flaw when storing
times as UTC?
Yes it is. Also, days of the adjustment will have either 23 or 25 hours so the idiom of the prior day at the same time being local time - 24 hours is wrong 2 days a year.
The fix is picking one standard and sticking with it. Storing dates as UTC and displaying as local is pretty standard. Just don't use a shortcut of doing calculations local (+- somthing) = new time and you are OK.
This is a huge flaw but it isn't a flaw of storing times in UTC (because that is the only reasonable thing to do -- storing local times is always a disaster). This is a flaw is the concept of daylight savings time.
The real problem is that the time zone information changes. The DST rules are dynamic and historic. They time when DST starting in USA in 2010 is not the same when it started in 2000. Until recently Windows did not even contain this historic data, so it was essentially impossible to do things correctly. You had to use the tz database to get it right. Now I just googled it and it appears that .NET 3.5 and Vista (I assume Windows 2008 too) has done some improvement and the System.TimeZoneInfo actually handles historic data. Take a look at this.
But basically DST must go.