I realize this is a commonly asked question but I couldn't find any posts that point out the disadvantages of using/storing offset for user's time zone. Is this not a better and more efficient way?
Long drop down lists of time zones are not user friendly and most of such lists don't have all the cities anyway. They also require user to specify their time zone. I feel it may be much better to simply detect it. In my case, my app is an ASP.NET Core app with Reach front end and it's pretty easy to capture user's time zone offset via JavaScript.
Any reason why storing the offset of user's timezone is NOT a good idea?
Any reason why storing the offset of user's timezone is NOT a good idea?
Yes. Many. A time zone and an offset are not the same thing. A time zone represents a geographical area in which local time is aligned. A time zone may undergo several different changes in its offset from UTC. Some of which are regular (like daylight saving time), and some of which are irregular (like when a government changes its standard time or dst rules).
... In my case, I simply want to display all date time values in user’s current time zone so that they’re meaningful to the user.
Ok, so let's say you check the user's current time zone offset and it is UTC-7. So you apply that to some dates and times in your application and done - so you think. Except that you didn't take into account that the user is in California, and one of your dates is in December when the offset should be UTC-8.
So you try to correct for that, and work out the rules of "when I see -7, it might be -8 sometimes". Except now you have a user come along who is in Colorado, where it is -7 during the winter and -6 during the summer. Or another user from Arizona, where most of the state is in -7 for the whole year. How do you know which set of rules to follow? Without referencing an actual time zone, it cannot be done.
This gets even more complex worldwide. For example, the number of variations for UTC+2 is just crazy. Even for countries that switch between UTC+2 and UTC+3 - they don't all switch on the same dates or at the same time of day!
See also: The Problem with Time & Timezones - Computerphile (YouTube)
and the StackOverflow timezone tag wiki.
Related
I have a system where users from Washington DC can create a post. This post is saved in my system in UTC +0 time. Then, I can use a reporting system which will give me info about every created post in a certain date range. Lets say I select a date range from March 21st 00:00:00 to March 28th 23:59:59 but in my system someone created a post on March 28th 22:30:00 Washington DC time. Washington DC is several hours behind UTC, so this post would be saved at around March 29th 02:30:00, and so when I generate the report for March 21st to March 28th, I will not get the correct result because there is 1 post that has been created on March 28th Washington time, but that is March 29th UTC +0 time.
I first solved this by obtaining the UTC offset of the client and sending it to the server, and so adding that offset to my date range:
// JavaScript
"offsetHours" : parseInt(new Date().getTimezoneOffset() / -60)
"offsetMinutes" : (new Date().getTimezoneOffset() / -60) % 1 * 60
// Python
_range["from"] = strToDate(_range["from"]) - datetime.timedelta(hours = int(request.headers["offsetHours"]), minutes=int(request.headers["offsetMinutes"]))
This solved the issue, but it raised another. Now if I generate a report for the same time range (from 21st to 28th March) from 2 different timezones, I will get different results. This is due to the fact that the 2 users have different offsets and so they affect the from range in different intervals.
Is there any solution to this problem?
You're not necessarily describing a problem, but rather a side effect of how local times around the world work.
At any given time, there is usually more than one "date" in effect somewhere in the world. If you are saving the timestamp of an event that took place, and you have customers around the world, you're not necessarily saving it with the same date that the user thought it was in their own time zone. This is true whether you align the timestamps to UTC or to a specific time zone.
Therefore, you must make a business decision about how your application is intended to work. Do you want your daily reports to reflect posts that were made within a UTC day, or within the day according to the time zone of your business's headquarters? Then store the timestamp in UTC and (optionally) adjust to your business's time zone before or during reporting.
If however you want the daily reports to reflect the date in the user's time zone, then you might want to also store the user's time zone ID (such as America/New_York - not a numeric offset) so that you could convert to that. Keep in mind that if user's are in different time zones, your reports might look strange when examined from a single time zone's perspective.
Another technique that is often used (primarily for performance reasons, but also for clarity of logic), is to keep both a UTC-based timestamp and a separate field for the "business date" that applies. Usually such a field is just a date field, storing a value such as 2021-03-29 without any time or time zone. This field can be pre-converted to a time zone according to whatever rules you decide are applicable for your business. It then becomes a great candidate for an index and works well for range queries for daily reports.
In the end - there is no one "right" way to do it. You have to decide what works best for your use case. If you are working for a larger company and unsure of the business requirements, then ask someone who might already perform a similar activity manually. (Often this is an accounting or sales person in a larger organization.)
So a little context, I have an array of 24 arrays -- one for every hour in the day.
So midnight, 0 index, would be [133.00, 234.00] which would indicate 133 actions from 12 - 1230 and 234 actions between 1230 - 1am.
I need to adjust these indexed arrays to account for the user's timezone in a browser with JS, so that if the user is in New York the 0 index (midnight in the user's home turf) is displayed in China's offset (12pm tomorrow, from user's perspective).
I've been trying to think of a solution, I have a simple function for what I've been able to think of
function offsetHourIndex(hourIndex, dataCenterTimeZone) {
let userTime = new Date().setHour(hourIndex)
return moment(userTime).tz(dataCenterTimeZone).hour();
}
How reliable would this approach be?
Your approach has a few problems:
You are assuming that the current date in the local time zone is the correct date for the target time zone. Most of the time, there are two dates active somewhere around the world. For example, 2019-04-02 04:00 in London is 2019-04-01 23:00 in New York. If you just take hour 4 from London but apply it to the current date in New York, you've created a whole new point in time, a day too early.
You assume there will be exactly 24 hours in every day. In time zones that have transitions for daylight saving time or changes in standard time, you may have more or less hours of local time on a the day of the transition.
In the case of a backward transition, there is a period of ambiguous local time. For example, when US Pacific Time moves from PDT to PST in November, the hour from 1:00-1:59 is repeated. If data from both hours are summarized into array element 1, then you will have higher than normal results for that hour. The opposite is true for forward transitions - you will have an hour with no data.
The time zone setting of the server can be a fickle thing. What if you change data centers? What if you move to the cloud? What if you are operating multiple data centers? What if a server administrator thinks all they are affecting by changing the system time zone is the readout on the taskbar or front panel, etc., and then it affects your application? In general one should avoid these things by never relying on the server's local time zone setting.
You can avoid all of these problems by basing everything on Coordinated Universal Time (UTC) - especially the array. Then you can be ignorant of any server time zone setting, and just base everything off the current UTC day, which is the same all over the world.
This will give you the local hour from the given UTC hour in your index:
var localHour = moment.utc({hour: hourIndex}).local().hour();
You do not need moment-timezone for the above recommendation.
However, if you really feel like you need to convert from a specific time zone to the browser local time, then you would use moment-timezone like this:
var localHour = moment.tz({hour: hourIndex}, timeZoneName).local().hour();
Note when you do this, you also have another problem - not every time zone is offset by a whole number of hours. For example, India uses UTC+05:30. There are many that are :30 and a few that are :45. By tracking hours only, you're not providing enough information to properly convert to the correct local hour. Your results may be off by one.
It seems reasonable. And the code should work as long as you have the properly formatted inputs. I like the brevity and clarity of the function. Any reason you are concerned about reliability?
You might mention in your question that you are using the moment and moment-timezone packages here to derive your data via its functions (moment & tz) on this line of code:
return moment(userTime).tz(dataCenterTimeZone).hour();
Your function may appear a bit cryptic without the imports in your example for folks reading here to understand, such as :
import * as moment from 'moment';
import 'moment-timezone';
So to clarify why I want to do this: I need to send push notifications to users when it's 7pm in their timezone.
For each registered device, I have a timezone string, like "Europe/Paris".
I'm creating a background job which will run every hour. It should fetch the list of users for which it's 7pm, and send them a notification.
So the question I'd like to answer is:
"Where in the world is it 7pm now"
Edit The important thing is to get the timezone, even if it's not formatted like "Europe/Paris", I can do that conversion manually with an array.
There's no built-in support in Javascript for converting the "standard" timezone names (e.g. Europe/London) to timezone offsets.
You mention push notifications so that suggests you're not running in a browser. If you're using Node.js there's a good library I've used called timezone which uses a local set of timezone spec files to handle conversion between timezones.
Note that timezone specs do sometimes change, for example when a national government decides with little notice that they're not doing daylight savings this year. It's vital that your local mappings are kept up to date accordingly.
There may be an easier way to do this, but have a database of users, who have a timezone. Then have a hour difference between your notification server, and that time zone. So your server is in pacific time, and the timezone is eastern, it will have a difference of 3.
Use the difference of the time you want, and what it is on your server, to determine the timezone. You want 16:xx it is 13:xx you get +3 hour difference, use that to look up the time zone, then push to all users associated with that time zone.
There may be an easier way, but that way is real simple solution with a db and a little sql knowledge.
EDIT: Also if you dont mind the time delay of using a web api, you should check this out: https://developers.google.com/maps/documentation/timezone/
Whoops numbers were off with the time-zones, Thanks Trent
I am working on a application, which detects the browser's time zone and asks the user's to set the time zone to the browser detected values.
So, I have populated, a select drop down which list all the All the keys present in ActiveSupport::TimeZone::MAPPING.
Now, I have a user whose timezone is (GMT-08:00) Pacific/Pitcairn.
There are many such values which are not present in ActiveSupport::TimeZone.
How should I handle such values. I've checked many js files which claim to send in Rails Supported Values, but none of them seem to work.
If I set my timezone to UTC-08:00 in Windows machine then my javascript plugin detects a time zone and sends in a value of America/Los Angeles.
We have a check box to not allow DST Timings on Windows machine.
So in this case with UTC-08:00 as time zone and DST box unchecked we're getting a value Pacific/Pitcairn.
So to go with matching before / for two different values of America/Los Angeles and Pacific/Pitcairn it's not possible.
Set the value by UTC offset, by ignoring name also wouldn't help. We have two seperate value with (UTC-08:00) Pacific Time (US & Canada) and (UTC-08:00) for Tijuana. So how do I decide which value to set.
showTimeZoneInfo: function(member_time_zone, timeZoneInfo, invertTZHash){
var tzValue = jQuery().get_timezone({'defaultvalue' : 'Etc/UTC'});
var railsOffset = TimeZoneFlash.extractOffset(timeZoneInfo[member_time_zone]);
var browserOffset = TimeZoneUtils.zoneWithoutDST();
if ( railsOffset != browserOffset) {
jQuery(".time_zone_text").text(browserOffset + " " + invertTZHash[tzValue]);
jQuery('.cjs_display_time_zone').removeClass('hide');
}
}
Now we have a case where invertTZHash doesn't contain Pacific/Pitcairn. And It returns a undefined value.
I am working on building a alert box for users who are in a different time zone compared to their browser's timezone. Even Intl.DateTimeFormat().resolved.timeZone wouldn't help because most of my traffic is from IE and FF browsers
Time Zone != Offset. See the timezone tag wiki. So just swapping it for another time zone that happens to be on UTC-8 is not a good idea. You will pick up all the DST rules for that zone, which don't necessarily apply.
Even Pacific/Pitcairn isn't purely UTC-8 if you deal with past dates. It moved from UTC-08:30 to UTC-08:00 in 1998 - you can see that here.
This is one of the reasons I'm not happy with ActiveSupport::TimeZone. They make it quite clear in their docs that it is limited:
Limit the set of zones provided by TZInfo to a meaningful subset of 146 zones.
This seems rather arbitrary. Who is it that determines what is "meaningful" and what isn't? If it wasn't meaningful, it wouldn't have been in the database to begin with!
You should look at using the tzinfo gem, which is what ActiveSupport based theirs on anyway. It has the full TZDB with all of its time zone data instead of a limited subset.
Regarding time zone detection, I'm not sure what JavaScript you are using to "detect" the time zone. You show calling some functions get_timezone and TimeZoneFlash.extractOffset that must be custom to your app or provided by external libraries. Please elaborate on exactly what you're using.
The only timezone detection library for JavaScript that I am aware of is jsTimeZoneDetect - which makes it quite clear that it just takes an educated guess. Unless you are depending on the new internationalization APIs in the very newest Chrome an Opera (which I don't think you are), then there's no guaranteed way to detect a time zone without user involvement. See also this answer.
We have a check box to not allow DST Timings on Windows machine. So in this case with UTC-08:00 as time zone and DST box unchecked we're getting a value Pacific/Pitcairn.
Yes, that is an awful reality. IMHO - that should never be unchecked. It would be better if the checkbox wasn't there. Whether DST applies or not is handled by the time zone, so there's no good reason to disable it. I'm sure that they left it there specifically for cases like Pitcairn, because there isn't a Windows time zone entry for them specifically.
Update
From your comments:
I already have a drop-down for this. I am working on building a alert box for users who are in a different time zone compared to their browser's timezone.
Take the current offset from JavaScript using new Date().getTimezoneOffset(). (It's in minutes, and the sign is reversed, so you may need some simple math.) Compare it to the current offset for the selected time zone in your drop down. (You should be able to get that from your server-side code.)
If they don't match, then alert your user. No need for jsTimeZoneDetect at all for this.
First Option: Set by UTC Offset
Get the UTC Offset
Search for UTC offset in your ruby array.
Set value by UTC offset, ignore name. Choose the first timezone which has the correct UTC offset.
Some code for this:
timezone = ActiveSupport::TimeZone.all.select { |tz| tz.utc_offset == my_offset }.first
Second Option: Add a new Timezone to the list
If you know the UTC offset, add a new TimeZone to the list. See Docs.
Hope that helps!
I need to find the time zone of the client machine using ASP.NET (C#) or JavaScript. What are the different time zones that are available all over the world and how to convert the date and time based on the users time zone.
Please provide some suggestions or sample coding to change the time based on the time zone.
You cannot find client time zone settings from ASP.NET.
You can use JavaScript to tell the current time, but there are several time zones that can be synchronized at any given time.
In Chrome, you can get the time zone from the JavaScript date object. There's no specific function for it, from what I've found, but the code
(new Date()).toString()
will yield something like
Mon Apr 18 2011 08:58:59 GMT+0200 (W. Europe Daylight Time)
In websites, the best approach I've found has been to have a setting for each user to specify the time zone to display all times in. If the JavaScript getUtcOffset gives a different offset than what is expected for the user's time zone, I'll show a notice for the user to review their settings. If time zone can be guessed from the date (which I've only found to be the case in chrome), I'll suggest that time zone, but I still resort to a select box for the user to manually pick the time zone.
Even so, it is possible to have the wrong time zone setting, without the script noticing it, because for a great part of the year, the two time zones may be perfectly synchronized.
Once you have a time zone (you can enumerate them all with System.TimeZoneInfo.GetSystemTimeZones()), you can convert UTC dates with System.TimeZoneInfo.ConvertTimeFromUtc and System.TimeZoneInfo.ConverTimeToUtc, respectively.