I'm looking to implement a function with a signature something like the following:
bool IsTimeZoneValid(string countryCode, DateTime localTime);
The intention is to determine whether the country has a time zone in which the local time would be valid, given that we know the current UTC time. Let's say, for the sake of argument, that "valid" means that when converted to UTC the time is +/- 30 minutes from what we believe the time is.
For example, say it is currently 03/08/2009 18:25:00 UTC then given the following method call for Australia, it should return true as this is a valid time in the "Eastern Standard Time" zone:
IsTimeZoneValid("AU", DateTime.Parse("04/08/2009 03:25:00"));
However the following call for France should fail as this is not a valid time in France's time zone.
IsTimeZoneValid("FR", DateTime.Parse("04/08/2009 03:25:00"));
This needs to be accurate, and take into account daylight saving time etc.
.NET 3.5 includes the new TimeZoneInfo class which can do a lot of the conversion for me if I know what time zones exist in a specific country, but I can't seem to find any built-in lookup for this. Am I missing something, or am I going to have to manually create a table of country to time zone mappings?
To reiterate, my question is: Given a country code, how can I get a list of time zones. Alternatively, is there another way to approach this that I've missed?
By default windows only adds Time Zone info for you local time zone, which may be the cause of the problem.
This class works only for the local time zone and any predefined time zones. If you want to use this for other times zones, you must have the registry settings added to the machine for all time zones needed or create custom time zone info using CreateCustomTimeZone.
http://msdn.microsoft.com/en-us/library/system.timezoneinfo.aspx?ppud=4
http://msdn.microsoft.com/en-us/library/bb384268.aspx
Related
I have an application with discount/deals for items that need to be configured to be enabled between specific datetimes i.e. 1st January 2022 7:00 am to 31st January 2022 5:00 pm.
The user which sets these start and end dates can be based anywhere in the world but the end consumers need to observe these start and end dates relative to their local time.
For example, the user setting up the deal is in Malaysia GMT+8 for their end consumers across Indonesia (which has 3 separate time zones GMT+7, GMT+8 and GMT+9) as well as some other end consumers in New Zealand (which has daylight savings and alternates between GMT+12 and GMT+13)
So, a consumer in GMT+13 must observe the deal being available in 1st Jan 7am while another consumer in GMT+7 will observe it a few hours later but still 1st Jan 7am in their local time.
They observe these deals 1. On an app on their phone and 2. In-store where they claim these deals. So even though consumers could change the time zone on their phone to see the deal being available sooner - they must go in-store to claim them and can only do so once the timezone of the store reaches the available time.
My current thought process is to store these without any time zone into using a DateTime type with Unspecified kind and any usage of this DateTime will be relative to the consumer/stores local time configured on the device. I don't see a way to do this with saving this date as UTC
Are there any alternative approaches?
Is there better support with this use case by using the new DateOnly and TimeOnly structs?
My current thought process is to store these without any time zone into using a DateTime type with Unspecified kind and any usage of this DateTime will be relative to the consumer/stores local time configured on the device.
Yes, that would be appropriate for the scenario you described.
This is sometimes referred to as a "floating time". You might also see it described as "television time", because such scenarios are common in broadcast television with regard to the time a show is aired.
Keep in mind the following:
When you store or transmit these values, do not treat them as UTC or associate them with any particular offset. Just use the date and time.
When you use these values on the user's device, you can apply the local time zone without actually knowing what that time zone is. For example, in .NET you can use TimeZoneInfo.Local or DateTimeKind.Local. In JavaScript, you can use the Date object, etc.
When you use these values elsewhere, such as in a back-end system, scheduler, or administrative UI, you will nee to know the user's time zone ID. Treat the value as belonging to that user's time zone, then convert it to a DateTimeOffset before comparing it to other values. For example:
TimeZoneInfo tz = TimeZoneInfo.FindBySystemTimeZoneId(userTZ);
TimeSpan offset = tz.GetUtcOffset(dt);
DateTimeOffset dto = new DateTimeOffset(dt, offset);
if (dto >= DateTimeOffset.UtcNow) ...
In windows, we get timezone list like this:
ID Time zone name Display string
-- -------------- --------------
0 Dateline Standard Time (UTC-12:00) International Date Line West
110 UTC-11 (UTC-11:00) Coordinated Universal Time -11
200 Hawaiian Standard Time (UTC-10:00) Hawaii
300 Alaskan Standard Time (UTC-09:00) Alaska
more here.
I use this list to convert from one timezone to another using TimeZoneInfo class which accepts time zone name shown in above list.
Ex.
// Local time zone to UTC
var utcOffset = new DateTimeOffset(DateTime.UtcNow, TimeSpan.Zero);
var localTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timezoneName); // here tz name can be any name from above table
var localOffset = new DateTimeOffset(date.Value, localTimeZone.GetUtcOffset(utcOffset));
DateTime utcDate = localOffset.UtcDateTime;
Now I came across SalesForce timezone representation like:
Time Zone Code Time Zone Name
-------------- --------------
GMT+14:00 Line Is. Time (Pacific/Kiritimati)
GMT+13:00 Phoenix Is.Time (Pacific/Enderbury)
GMT+13:00 Tonga Time (Pacific/Tongatapu)
GMT+12:45 Chatham Standard Time (Pacific/Chatham)
more here.
I couldn't find built in functionality to use either time zone code or time zone name given in above table for the conversion.
If you're happy to stick with TimeZoneInfo and DateTime/DateTimeOffset, you can use Matt Johnson's TimeZoneConverter library to convert the IANA ID (the part in brackets, e.g. Pacific/Kiritimati) to a Windows system time zone ID.
Examples from the project page docs:
string tz = TZConvert.IanaToWindows("America/New_York");
// Result: "Eastern Standard Time"
Or:
TimeZoneInfo tzi = TZConvert.GetTimeZoneInfo("America/New_York");
However, things to be aware of:
There won't always be a complete mapping from IANA IDs to Windows IDs. Some aren't mapped, although the situation is better than it used to be.
The Windows time zone data can be different to the IANA data sometimes. (Again, this is getting better.)
Time zone data changes over time. If you're storing future date/time values, you might want to store the time zone and local time rather than the UTC instant. (That instant may not map to the same local time in the future.)
Some local times can be ambiguous - e.g. if the clocks go back from 2am to 1am on a particular date in a particular time zone, then 1:15am on that day will occur twice in that time zone. Think about how you want to handle that, and test it.
I'd personally recommend using my Noda Time library anyway, as a cleaner way of handling date/time, but I acknowledge that if you've got a lot of code dealing with DateTime already, that may not be feasible.
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.
Hello!
I was confused in the problem of time zones. I am writing a web application that will contain some news with dates of publication, and I want the client to see the date of publication of the news in the form of corresponding local time. However, I do not know in which time zone the client is located.
I have three questions.
I have to ask just in case: does DateTimeOffset.UtcNow always returns the correct UTC date and time, regardless of whether the server is dependent on daylight savings time? For example, if the first time I get the value of this property for two minutes before daylight savings time (or before the transition from daylight saving time back) and the second time in 2 minutes after the transfer, whether the value of properties in all cases differ by only 4 minutes? Or here require any further logic? (Question #1)
Please see the following example and tell me what you think.
I posted the news on the site. I assume that DateTimeOffset.UtcNow takes into account the time zone of the server and the daylight savings time, and so I immediately get the correct UTC server time when pressing the button "Submit". I write this value to a MS SQL database in the field of type datetime2(0).
Then the user opens a page with news and no matter how long after publication. This may occur even after many years. I did not ask him to enter his time zone. Instead, I get the offset of his current local time from UTC using the javascript function following way:
function GetUserTimezoneOffset()
{
var offset = new Date().getTimezoneOffset();
return offset;
}
Next I make the calculation of the date and time of publication, which will show the user:
public static DateTime Get_Publication_Date_In_User_Local_DateTime(
DateTime Publication_Utc_Date_Time_From_DataBase,
int User_Time_Zone_Offset_Returned_by_Javascript)
{
int userTimezoneOffset = User_Time_Zone_Offset_Returned_by_Javascript; // For
// example Javascript returns a value equal to -300, which means the
// current user's time differs from UTC to 300 minutes. Ie offset
// is UTC +6. In this case, it may be the time zone UTC +5 which
// currently operates summer time or UTC +6 which currently operates the
// standard time.
// Right? (Question #2)
DateTimeOffset utcPublicationDateTime =
new DateTimeOffset(Publication_Utc_Date_Time_From_DataBase,
TimeSpan.Zero); // get an instance of type DateTimeOffset for the
// date and time of publication for further calculations
DateTimeOffset publication_DateTime_In_User_Local_DateTime =
utcPublicationDateTime.ToOffset(new TimeSpan(0, - userTimezoneOffset, 0));
return publication_DateTime_In_User_Local_DateTime.DateTime;// return to user
}
Is the value obtained correct? Is this the right approach to solving this problem? (Question #3)
UPDATED Oct 19 at 6:58 (I tried post it as a comment but it's too long by 668 characters)
Matt Johnson, Thank You for such a detailed answer despite that of the fact that you are doing this not the first time. Thank you for taking the time to explain this particular case, and not just provide links to other posts.
I have read the information that you have provided. Maybe I'm still not fully aware of all the details, but if I understand it right, for the right convertion of DateTime (which was written many years ago in the database) from UTC to the same user's moment, I need to know UTC offset which he had at that moment. And it is difficult taking into account that transfer rules for DST change constantly. And even now, though the platform ".NET" contains some TZDB that is used for the TimeZoneInfo type, I can't take advantage of it without the exact position of the user.
But what if I am only interested in the date and time of starting this year, and only in Russia, where DST was canceled in 2011? As I understand it, this means that if properly configured clock on user's computer, located in Russia, this approach will always give the correct result. And since 2011, the offset to UTC of user's clock should always be the same. Accordingly, the shift indicators in different browsers will not be different for the Russian user's.
Answer to Question 1
... does DateTimeOffset.UtcNow always returns the correct UTC date and time, regardless of whether the server is dependent on daylight savings time?
Yes. As long is your clock is set correctly, UtcNow always refers to the UTC time. The time zone settings of the server will not affect it. The value in your example will always be 4 minutes, regardless of DST.
Answer to Question 2
var offset = new Date().getTimezoneOffset();
Since new Date() returns the current date and time, this will return you the current offset. You then proceed to apply this current offset to some past value, which may or may not be the correct offset for that specific time. Please read the timezone tag wiki, especially the section titled "Time Zone != Offset".
Answer to Question 3
Is the value obtained correct? Is this the right approach to solving this problem?
No. This is not the correct approach. You have a few options:
First Option
Just pass the UTC value to JavaScript without modification.
Send it in ISO8601 format, such as 2013-10-18T12:34:56.000Z. You can get this in .Net easily using yourDateTime.ToString("o").
Be sure the DateTime you are starting with has .Kind == DateTimeKind.Utc, otherwise it won't get the Z on the end, which is essential.
If you are targeting older browsers that can't parse ISO8601, then you will need a library such as moment.js to parse it for you.
Alternatively, you could pass the number of milliseconds since 1/1/1970 UTC as a number and load that into a JavaScript Date instead of parsing.
Now you can just display the Date object using JavaScript. Let the browser convert it from UTC to the users local time.
Warning, with this approach, some conversions might be incorrect due to the problem I describe here.
Second Option
Like the first option, pass the UTC timestamp to JavaScript
Use that to get the offset for that timestamp.
Pass the offset back to the server in a postback or ajax call.
Apply the offset on the server
Output the local time zone
I don't particularly like this option because of the round trip. You might as well calculate it in JavaScript like the first option.
Third Option
Get the user's time zone. The best way is to ask them for it, usually on a user setting page.
Use it to convert from the UTC time to their local time completely on the server.
You can use Windows time zones and the TimeZoneInfo class to do the conversions.
Or you can use IANA/Olson time zones and the Noda Time library.
If you do this, you can optionally use a map-based timezone picker.
You can take a guess at the user's time zone with jsTimeZoneDetect.
This approach is more accurate, but requires more user interaction.
Also, please in future posts, ask just one question at a time. And please search before you post - I've written most of this in one form or another many times before.
i'm a little lost in the timezone :)
I have data stored with the time in UTC.
The server is in the Netherlands, so we live in utc+1 (now, with daylightsavingtime, in utc + 2)
Now a client says: give me the data from august 5th.
So i have to calculate the utc time from 'his time'.
For that i have to know:
what is your utc offset (we stored that in his profile, let's say utc -6)
are you in daylightsavingtime (because then we have to add +1 and make the utc offset -5)
Then my questions:
Can i ask the .Net framework: does country XX have daylightsavingtime?
Can i ask the .Net framework: is 08-05-2010T00:00:00 in country XXX daylightsavingtime at that moment?
i've been trying the .ToLocalTime(), but this only gives me the local time at the server, and that's not what i want, i want to calculate with the timezone of the user, and also with the fact that at that particular point in time, if he/she is in daylightsavingtime
I've also seen this VB example:
TimeZone As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time")
Dim Dated As DateTime = TimeZoneInfo.ConvertTimeToUtc(TempDate, TimeZone)
but imho this doesn't take into account that the user in this timezone is or is not in a daylightsavingtime (dst) country.
For instance, one user in this timezone is in the netherlands having dst, and one other is in another country which has no dst.
You can't ask the framework about a particular country - but you can ask about a particular time zone.
TimeZoneInfo does take DST into account. (Heck, it wouldn't have the IsDaylightSavingTime method otherwise.) If you've got two users, one of whom is currently observing DST and the other of whom isn't, then they aren't in the same time zone.
If you could specify which locations you're talking about, I could try to find out which time zones are involved. (It's generally easier to find out the Olson names, but it shouldn't be impossible to find out the Windows IDs.)
TimeZone class has a method IsDaylightSavingTime that take a date as parameter
and return a boolean indicating if that date in in daylightsavingtime for that timezone.
I promote my comment to an answer.
It appears to be a complicate problem. Look at http://www.timeanddate.com/time/dst2010.html, there is a table of DST in various country of the world. You cannot get this with static code because DST change not only from country to country, but also from year to year.
I guess you have to maintain a table with DST information and get information from it.
Or you can query a webservice to get such infos.
Look at http://www.earthtools.org/webservices.htm#timezone for a webservice that you can query to get time in various country that take in account DST (it work only for Western Europe time zones).
Also look at http://www.geonames.org/export/web-services.html. You have to get your users geolocation to use these services.