DateTime versus Two Time Zones? - c#

I have a data table that stores events. Events are saved with local time. I am developing a form, where the user inputs a date and time in the past. After filling out the form handful of times, I need to take those dates and times, and find the events that occurred at those times.
The problem is that the user is imputing their local time, not mine. I can add a offset, or timezone selection. But then I realized that Day Light savings time is going to make things not work.
I cannot change my data table date and time values. Where I can convert their date and time to mine, while taking in consideration daylight savings?
Thanks

Premise: I really think the ideal way to solve your problem is convert your stored datetimes to UTC, and then use that when you search.
But you said you can't change those, so using ConvertTime to convert them at request time is probably your best second option.
Here you can find all the details you need.
You might also want to give a look at Noda Time, which is quite useful when dealing with TimeZones and DateTime.

As you are storing the event time as local time, you will always have problems with daylight savings time. For events where the time overlaps, it's impossible to tell if the event occured the first or second time that the local time had that value. You have already lost information by storing it as local time. If you would store the event time as UTC instead, it would be possible to avoid that daylight savings problem.
Without changing the data that you store, it's impossible to get around the daylight savings problems. The best you can do is to convert the time from the user's time zone to UTC, then to your local time. Example:
TimeZoneInfo TimeZone = TimeZoneInfo.GetSystemTimeZones().Where(t => t.StandardName == "W. Europe Standard Time").Single();
DateTime utc = TimeZoneInfo.ConvertTimeToUtc(inputTime);
DateTime local = utc.ToLocalTime();

Perhaps something like MyDateTime.ToLocalTime() will work for you?
Check out the following article, it should help.
https://msdn.microsoft.com/en-us/library/ms973825.aspx

Related

Convert UTC to different time zones without day light savings

Currently I have an UTC date which I have to convert to different time zone based on where my client time zone is.
This is the code am currently using:
var timezoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timezone);
var clientTime = TimeZoneInfo.ConvertTimeFromUtc(createDateTime, timezoneInfo);
I know this automatically uses the daylight saving time and provide me the current time based on time zone.
I have a boolean which determines whether is daylight saving time is enabled or not. The above method doesn't work well based on my boolean value.
Is there a inbuilt method or code that converts UTC to different time zone without daylight saving time?
It is generally a bad idea to provide a boolean daylight saving time flag in your application at all. Time zones are all about the actual local time in a particular area, and that always includes whether the government that controls that time zone has decided if daylight saving time applies or not. In other words - it's not up to the user, it's up to their government. Microsoft and the IANA time zone community go out of their way to ensure the time zones accurately reflect those governmental decisions.
If you really must do this, you could consider either using the TimeZoneInfo.BaseUtcOffset property, or you could inspect the result of TimeZoneInfo.IsDaylightSavingTime to decide whether to subtract time from the result. However, both approaches suffer from edge cases that will give you errors in cases where the standard time offset has changed unrelated to daylight saving time. Additionally, not all time zones shift by a full hour for DST, there's one that shifts by 30 minutes.
Ultimately, my recommendation would be to keep your code as-is and not try to use that boolean flag. Instead, go back to the UX or wherever that flag originates and remove it.

Get local DateTime in Denmark from server in Ireland

I have a solution in Azure in Ireland. I live in Denmark.
Would it be correct, to save all my times and dates as universal = DateTime.Now.ToUniversalTime() in my database in Ireland?
When I need the danish time, I cannot: universal.ToLocalTime() because I’m still on the server in Ireland. Instead I could write:
danish = TimeZoneInfo.ConvertTimeFromUtc(universal, TimeZoneInfo.FindSystemTimeZoneById("Romance Standard Time"));
But I think it’s a long way.
Alternative 1: I convert date to danish, before I save in my database in Ireland.
Alternative 2: There are one timezone between Denmark and Ireland, so I could: danish = dateTime.AddHours(1), but perhaps it could give errors with the summer and winther time.
A few things:
There's nothing wrong with writing DateTime.Now.ToUniversalTime(), but understand that under the hood that is fetching the current UTC time, converting to local time, then converting back to UTC. Thus, it's both shorter and more efficient to simply call DateTime.UtcNow to get the UTC time directly without conversions.
The server on which your code is running is irrelevant (or should be). Additionally, all Azure instances have their local time zone set to UTC anyway, so you will not see any effects from Ireland's local time zone.
Yes, "Romance Standard Time" is the correct Windows identifier for the local time in Denmark. If you are running on a non-Windows platform, you should use "Europe/Copenhagen" instead. If you are writing for multi-platform usage, then use "Europe/Copenhagen" with my TimeZoneConverter library.
If you just wanted to write shorter code, you might be able to do the following:
DateTime danish = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(universal, "Romance Standard Time");
I say might, because this will only be correct if the DateTime you've got in your universal variable has its Kind property set to DateTimeKind.Utc. If you've set that previously using DateTime.SpecifyKind, then this will work. On the other hand, if you have just retrieved this value from a database, then by default the Kind will by DateTimeKind.Unspecified. The key difference being that ConvertTimeFromUtc treats DateTimeKind.Unspecified as if it were DateTimeKind.Utc, while ConvertTimeBySystemTimeZoneId treats DateTimeKind.Unspecified as if it were DateTimeKind.Local.
In other words, if you don't set the Kind explicitly, it would convert from local time to Danish time. However, since Azure runs its local time zone as UTC, you'd get the same result. Just you might get different results when running elsewhere.
You might consider using DateTimeOffset instead of DateTime. This usually results in clearer code and less errors. (DateTimeKind is not used with DateTimeOffset.) Of course it depends on what exactly you're using this data for, but often it's a good idea.
About your proposed alternatives, I suggest neither. Your original code is better. My thoughts on each:
Alternative 1: I convert date to danish, before I save in my database in Ireland.
It depends on what you're representing.
If this is a unique point in time (the time something happened, usually in the past) - then you should keep it as UTC. That allows conversion to any time zone correctly.
On the other hand, if you are representing the time something is scheduled to happen (in the future), then most often it's the local time that matters most. This is especially important for recurring events in time zones with DST, or events in time zones that might be volatile (where the government makes frequent short-notice changes).
Alternative 2: There are one timezone between Denmark and Ireland, so I could: danish = dateTime.AddHours(1), but perhaps it could give errors with the summer and winter time.
You should not add or subtract time to adjust time zones. After all - you're not talking about a different point in time an hour in the future or an hour in the past.
Also, Denmark and Ireland may be similar today, but that doesn't mean they have always been that way, and it doesn't mean they will necessarily stay that way in the future. Ireland has a different time zone identifiers ("GMT Standard Time" on Windows, "Europe/Dublin" on other platforms).
Use server Utc time to save record in database in order to avoid any time differences when converting datetime from and then converting it back. Once the record use Utc datetime then you can easily convert it to any time zone, so for example: system requires to convert datetime to Denmark local time, and in future it might need to show in Germany datetime
DateTime timeUtc = DateTime.UtcNow;
TimeZoneInfo cstZone = TimeZoneInfo.FindSystemTimeZoneById("Romance Standard Time");
DateTime cstTime = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, cstZone);

Handling timezones with daylight saving in C#

We have an application related to aviation, and specifically flights.
Times have to be stored in local, so I chose to use UTC Time + an offset, but then now I realize it is a bad choice:
By storing the timezone as an offset, I lose track of the original timezone and this has implications when dealing with daylight savings.
For example I can store a time in Alpine, UT as UTC time and a -6 offset and also a time in Phoenix, AZ as UTC time and a -6 offset.
But when daylight saving comes, the time will change in Alpine, but not in Phoenix.
So, I need to store the proper timezone and I have seen that there are also different lists with a different syntax, so I am assuming there are different standards.
In C#, what would be the best option to store a local time with the local time zone to make it work with daylight saving changes?
From the discussion in the question's comments, I understand that you are working with flight time schedules - that is, the time a future flight is intended to depart. This is indeed a case where the local time is more important than the UTC time.
Since you have the local time and location of departure (ex: 5:00 PM in Salt Lake City), then you should be storing in your database of scheduled departure times two values:
17:00 - The relevant local time of the departure
SLC - The location where the time is relevant
If this is a specific occurrence of this flight, then you should store the date as well:
2018-06-01T17:00 - The specific relevant local time of the departure
SLC - The location where the local time is relevent
These are the details that are contextually relevant to your business use case. Do not lose sight of them by converting them to UTC.
That said, you might consider storing them as a DateTimeOffset (2018-06-01T17:00-06:00), which makes converting to UTC trivial for a given instance. However there are two problems with this approach:
It cannot work with recurrences, because the offset may change.
Even for a single instance, the offset might change - if the government controlling the time zone decides to change their standard offset or daylight saving time rules before that occurrence takes effect. If you do take a DateTimeOffset approach, or a UTC-based approach, you must be prepared to recalculate future events in the face of such changes. (For more on this, see my blog articles: On the Timing of Time Zone Changes and Time Zone Chaos Inevitable in Egypt. There are countless other examples.)
With regards to the location - because you are working with data that is contextually applicable to the airline industry, I recommend using IATA airport codes like the SLC that I showed above. In other contexts, one might store an IANA time zones identifier like America/Denver, or a Windows time zone identifier like Mountain Standard Time.
You may find my "Airport Time Zones" gist (code and output table) useful for working with IATA airport codes. You'll have to decide how that data will flow through your system. If you are running on Windows and want to use the TimeZoneInfo class to convert times to different time zones, then use the Windows time zone IDs shown there. If you want to use the IANA time zone IDs instead, consider using Noda Time, or you can use my TimeZoneConverter library. There are several different options here, so explore them all carefully and pick ones that make sense to you.
Noda Time would be a great choice, IMHO. Not only would you get excellent time zone support, but you'd also be able to use types like LocalTime or LocalDateTime which align well with the scenarios described.
As I wrote in my comment, do not store local dates. Instead, store datetime values as UTC, and convert to local datetime when you need to display it.
You can use the ConvertTimeFromUtc method of the TimeZoneInfo class for that.
This means you will have to also keep a list of locations and whatever TimeZoneInfo they are associated in - For example,
Jerusalem would be associated with Israel Standard Time,
Rome with W. Europe Standard Time,
Hawaii with Hawaiian Standard Time
and so on. (I'll bet you can find such a list online somewhere.)
Please note that the ConvertTimeFromUtc method handles the daylight savings problem for you as well.
Then you can do something like this to get the local time by location:
DateTime GetLocalDateByCityName(DateTime utc, string cityName)
{
var timeZoneInfoId = GetTimeZoneInfoIdByCityName(string cityName);
return TimeZoneInfo.ConvertTimeFromUtc(utc, TimeZoneInfo.FindSystemTimeZoneById(timeZoneInfoId);
}
And of course, in GetTimeZoneInfoIdByCityName you get the TimeZoneInfoId for the specific city.
You should use TimeZoneInfo.ConvertTime to convert DateTime between time zones if you need to handle Daylight Savings.
TimeZoneInfo.ConvertTime(myDateTime, timeZone);
TimeZoneInfo.ConvertTime(myDateTime, fromTimeZone, toTimeZone);

Best way to compare Time only between different Time Zones

I'm currently working on a project that requires a user (in their respective time zone) to set a 'cut off' time for certain Notifications that they receive throughout the day. The user will, for instance, say they want no alerts prior to 8:00 AM and no later than 7:30 PM. This user is located in CST (the time zone information for each user is saved) and the 'server' is in EST. The server is where all of the logical decisions occur for which user should be notified, etc.
What would be the best practice to convert the time part only for comparison purposes?
My current thought would be to take the time set by the user, say "7:00 AM", and create a full DateTime object and store that as .ToUniversalTime(). On the server I can convert the DateTime object to Local time and compare the Time portion only.
Does anyone have a 'better' solution or possibly any advice from someone who has tackled a similar issue?
Thanks!
I suggest that you store the minimum time and maximum time as local times, without dates. You will also want to store the users general timezone information, for example "Central Time", not CST or CDT.
When you are ready to make a determination about sending a notification, convert the current UTC time to the user's local timezone and compare the stored time range against the converted time.
Trying to create a full calendar date from stored time information will be problematic in certain cases, specifically for daylight savings changes. By converting from UTC to user's local time for the comparison, that problem will be avoided.

How does DateTime.ToUniversalTime() work?

How does the conversion to UTC from the standard DateTime format work?
More specifically, if I create a DateTime object in one time zone and then switch to another time zone and run ToUniversalTime() on it, how does it know the conversion was done correctly and that the time is still accurately represented?
There is no implicit timezone attached to a DateTime object. If you run ToUniversalTime() on it, it uses the timezone of the context that the code is running in.
For example, if I create a DateTime from the epoch of 1/1/1970, it gives me the same DateTime object no matter where in the world I am.
If I run ToUniversalTime() on it when I'm running the code in Greenwich, then I get the same time. If I do it while I live in Vancouver, then I get an offset DateTime object of -8 hours.
This is why it's important to store time related information in your database as UTC times when you need to do any kind of date conversion or localization. Consider if your codebase got moved to a server facility in another timezone ;)
Edit: note from Joel's answer - DateTime objects by default are typed as DateTimeKind.Local. If you parse a date and set it as DateTimeKind.Utc, then ToUniversalTime() performs no conversion.
And here's an article on "Best Practices Coding with Date Times", and an article on Converting DateTimes with .Net.
Firstly, it checks whether the Kind of the DateTime is known to be UTC already. If so, it returns the same value.
Otherwise, it's assumed to be a local time - that's local to the computer it's running on, and in particular in the time zone that the computer was using when some private property was first lazily initialized. That means if you change the time zone after your application was started, there's a good chance it will still be using the old one.
The time zone contains enough information to convert a local time to a UTC time or vice versa, although there are times that that's ambiguous or invalid. (There are local times which occur twice, and local times which never occur due to daylight saving time.) The rules for handling these cases are specified in the documentation:
If the date and time instance value is
an ambiguous time, this method assumes
that it is a standard time. (An
ambiguous time is one that can map
either to a standard time or to a
daylight saving time in the local time
zone) If the date and time instance
value is an invalid time, this method
simply subtracts the local time from
the local time zone's UTC offset to
return UTC. (An invalid time is one
that does not exist because of the
application of daylight saving time
adjustment rules.)
The returned value will have a Kind of DateTimeKind.Utc, so if you call ToUniveralTime on that it won't apply the offset again. (This is a vast improvement over .NET 1.1!)
If you want a non-local time zone, you should use TimeZoneInfo which was introduced in .NET 3.5 (there are hacky solutions for earlier versions, but they're not nice). To represent an instant in time, you should consider using DateTimeOffset which was introduced in .NET 2.0SP1, .NET3.0SP1 and .NET 3.5. However, that still doesn't have an actual time zone associated with it - just an offset from UTC. That means you don't know what local time will be one hour later, for example - the DST rules can vary between time zones which happened to use the same offset for that particular instant. TimeZoneInfo is designed to take historical and future rules into account, as opposed to TimeZone which is somewhat simplistic.
Basically the support in .NET 3.5 is a lot better than it was, but still leaves something to be desired for proper calendar arithmetic. Anyone fancy porting Joda Time to .NET? ;)
What #womp said, with the addition that it checks the DateTime's Kind property to see if it might already be a UTC date.
DateTime.ToUniversalTime removes the timezone offset of the local timezone to normalize a DateTime to UTC. If you then use DateTime.ToLocalTime on the normalized value in another timezone, the timezone offset of that timezone will be added to the normalized value for correct representation in that timezone.

Categories

Resources