Get client's timezone offset in ExpressJS - javascript

I'm searching for a way to get client's timezone offset in ExpressJS (with req object, for example, would be really great).

If you control the client, you can do this with client-side JavaScript.
If you don't (e.g. you're building a server-side component like an API), then you can't pull it out of the HTTP request (unless you're using sessions, but even that's not necessarily reliable).
On the upside, if it's only server-side, you shouldn't worry about it either: set all your date objects as UTC or a Unix timestamp and leave it to the client developer to handle timezones.

As others have mentioned, getting the client-side browser/OS timezone offset is not available via HTTP, so you need to send this data from the client side.
In my case I have an app that requires login...so I can store a user's preferences. The default is to use browser timezone, but the user can also configure a specific timezone to use all the time instead which is stored in their user profile (e.g. America/Dallas or Europe/Amsterdam).
I don't trust browser time to be correct. The user may have also screwed up their OS timezone setting...so timezone offset may also not be correct. However most modern OSes set timezone automatically based on Geo-IP implied location...so for many users the convenience of being able to travel around the world and login to the app and see date/time in whatever local timezone they happen to be in is worth the effort for the user experience. Users that always want to see a specific timezone can configure that and we will use that preference instead.
The way I do this is the following... on the login form add a hidden field and set the value with javascript. When the user logs in, store this timezone offset in the session. If the user hasn't set a preferred timezone, then we use this offset when rendering date/time. This means that if you have long sessions and a user can travel around between countries...they will still show the old timezone offset until the next logout/login. You can of course get this data more frequently or even on every request if you want...but for my purposes getting this on login is enough. My sessions are expired on IP address change anyway...so yeah. Of course if their session crosses a daylight savings switch then the offset won't be accurate until the next login (assuming their OS/browser TZ is correct in the first place).
Client Side
<input type="hidden" name="tzOffset" id="tzOffset">
<!-- ... -->
<script>
var tzOffset = new Date().getTimezoneOffset(),
tzInput = document.getElementById('tzOffset');
tzInput.value = tzOffset*(-1);
</script>
Note that I multiple it by -1 because I am going to use moment.js for formatting on the express end and the offset is backwards (utc offset from local vs local offset from utc). You could also do this on the server end. You should probably validate this number as well before using it...just minimal example code here.
Server Side
Then on the server side (express) if there is a successful login, I stick that tzOffset value in the session.
Then when formatting dates in express with moment-timezone I can do something like the following. This function is a "formatter" which I can expose to pug/views and then format any random date object with the right timezone/formats for the user.
function formatDateTimeForUser (date) {
var userTZ, // user specified TZ like Europe/Berlin
userTZoffset, // tzOffset we got from login form
userDateFormat, // user specified date format (or default)
userTimeFormat; // user specified time format (or default)
if (userTZ)
return moment(date).tz(userTZ).format(userDateFormat+' '+userTimeFormat+' zz');
else
return moment(date).utcOffset(userTZoffset).format(userDateFormat+' '+userTimeFormat+' ZZ');
}
The ZZ format is for showing timezone in numeric offset format (relevant when we use a fixed numeric offset from client.
The zz format is for showing timezone in character format (e.g. PDT, PST, EST, etc) which is relevant when we have a timezone like Europe/Berlin instead of a fixed numerical offset.
Another approach
Push raw dates to client and do client-side formatting. Downside is less control and consistency and more js to push to the browser. moment will also run client side if you like.
I just configure my formatters server-side based on user locale and user prefs and then expose those formatters for use in my pug templates in express. So far it works pretty well for me.
Funny Story
I had a co-worker that manually set the wrong timezone on their computer and so the time was wrong. Instead of fixing the timezone they disabled network time and manually set the time to be the "correct" time.
Then they got grumpy when everyone was showing up to meetings they scheduled an hour late.
So yeah...no guarantees the client-side time or timezone offset will be correct.

The reality is, even if you have got the timezone offset, it doesn't mean you have the the correct time on client side, considering some area have "daylight saving time".
But, you could "guess" which timezone the user is in, considering the following data:
req.get('Accept-Language'), for which country is the user may live in now;
req.ip, to search in a database and find out the approximate location;
client-side (new Date).getTimezoneOffset(), for timezone offset;
Finally, through the calculation of the above result, your guess should be almost there. The timezone should be stored in string like 'America/New_York' but not a timezone offset number.
http://momentjs.com/timezone/ should be a good tool to deal with timezone.
After you save the "almost correct" timezone, don't forget to leave a place where user can change it.

Some people like using the library momentJS for anything dealing with time, but I believe the Date object in Javascript suffices what you are looking for and does not require a library. The getTimezoneOffset is what you will need. I hope this helps! Let me know if you have other questions!

Related

How to handle BST and GMT timezones on client side? Storing them in a DB as UTC

I've got a Go app and I store all datetimes as UTC:
dateTime, err := time.Parse("2006-01-02T15:04:05.000Z", myDateTime)
This becomes something like: 2022-09-29T19:40:36.150Z.
Now I want to show this datetime client side (on a website), but showing the user the time in their timezone.
Since it's all UTC I could have the user pick their timezone from a list, like this one https://gist.github.com/valo/c07f8db33d223f57a4cc9c670e1b6050.
Then it's just a matter of adding/subtracting some time from the UTC and showing it.
But the problem is with daylight savings time, the client side timezone would have to change when it's summer, and change again when it's winter.
How are people doing this?
Usually, this happens on the client side. Information about the client's timezone is stored on the browser and is available to you if you need it. However, there are functions that sort this out automatically like toLocaleDateString.
Step 1.
Parse the time string 2022-09-29T19:40:36.150Z to a Date object in javascript
Step 2.
Use toLocaleDateString on the date. This will display the Date in the client's timezone setting stored in the browser
MDN docs for toLocaleDateString()

how to get clients actual time and timezone to perform system time check

This might be a very vague question but please bear with me.
I am working on an application that requires me to perform a system time check on clients machine. The issue I am trying to address is, when clients travel across timezones and change their system time instead of changing the timezone it creates problems during the oauth negotiation. How should I go about addressing this problem in such a way that this issue can be self-detected when a client tries to login to the app rather than having to manually detect this kind of issue?
You can call toUtc() on a new DateTime.now() to convert it to UTC. It shouldn't be affected by time zones if it's in UTC, and the string representation will look like "2013-10-18 08:52:16.861Z" (the Z at the end means UTC).
You should be able to use a DateTime in UTC for OAuth negotiation. If not, please share more details; it's probably a bug in either Dart or your OAuth client.
It sounds like your timezones are pretty important so you shouldn't trust JavaScript system time with a 10 foot stick.
If I were you I would use PHP to geo-locate them and apply the timezone based on their location.
You will want to check out Get PHP Timezone Name from Latitude and Longitude?

PHP dates and timezones

I have an admin panel where items entered will have a time_posted and an expiration time. These times use a timestamp (time()) upon being entered.
I want the entering of the expiration time to use a fancy date/time selector, but the selector uses JavaScript client time rather than server time.
A way to fix this would be to save the timezone a user is in, then use that as offset for the data entered, but it's something that has to be 100% correct.
Is there any 'proper' way to approach this? I saw that some PHP functions do use the timezone setting but others do not, for instance the actual time() function doesn't and even if I create a DateTime object, output the current timestamp, change the timezone and output the timestamp again it just returns the same timestamp twice.
Any pointers would be lovely
I assume that the actual timestamp value would still be correct (independent) regardless of what settings/timezone the end user has.
Problems may apper when/if you want to generate date strings server-side and thus return times with offsets.
Either
1. dont generate date strings server side, if you are not sure about the timezone offsets or
2. warn users and make sure that they specify their timezone and then force that timezone whenever you output date string from timestamps
A few things:
You probably don't need to identify the user's time zone for the scenario you describe, but if you did then please recognize that a time zone and an offset are two different things. See "time zone != offset" in the timezone tag wiki. If you actually think you need the user's time zone (such as "America/Los_Angeles"), then refer to this answer.
If you want the entered date to be relative to the client's local time zone, then you need to use either a Date object, or a library like moment.js in JavaScript. Use the individual date/time components from the date picker to get either a unix timestamp, or an ISO8601 formatted date/time string at UTC. For example, "2016-09-02T01:23:45Z"
If you want the entered date to be relative to some other time zone, then you need to transmit the actual values the client entered without modification. The best way is in ISO8601 format without offset. For example, "2016-09-02T08:00:00". On the server side, parse that value and apply whatever time zone is applicable.
If you are only selecting a date, and not a time, then you should really think about whether any time is applicable or not. Is 00:00 really applicable? Or should it be 24:00 or 23:59:59.999? If you don't care about the user's time zone, then really you shouldn't assign any time value at all. Just pass the date. For example: "2016-09-02"
Don't rely on the server's time zone setting to be anything in particular. Though PHP has functionality for setting a "default" time zone, you should try to avoid using it. It is much safer to be explicit about time zones on a per-operation basis. Use the DateTime class in PHP, not time().
Be sure to read Daylight saving time and time zone best practices, and to search thoroughly for other questions on StackOverflow, as much of this has been answered already in various other questions.

Passing a time zone into a web api call from moment with nodatime

I'm about to go insane dealing with datetime issues and the web.
I have a web server hosted in the Central Time Zone. When clients in the Eastern Time Zone try and schedule an item for a given day using my app, they pass in the value of (for example) 3/14/2015. When we pass the code back to our model, which is sent to the web api, we persist is using something like the code below.
moment.utc($("#mydatepicker").val).hour(0).minute(0).second(0)).toISOString();
This results in a string like the following:
2015-03-14T04:00:00.000Z
When the item is converted back on the server in web api, it converts to
3/13/2015 11:00:00 PM
Logic then strips off time and you can see what happens from here. Since I stripped off the time, it is now the day prior and that is the value persisted to the database.
I need to know some way to send a value from moment, into the web api preferrably as a ZonedDateTime in the client's time zone. I can then convert it to UTC for persistance in the DB.
I've seen things about using NodaTime.Serialization.JsonNet, but I am unclear on how to to use it with Moment and pass it back and forth across web api/ajax.
I need to know some way to send a value from moment, into the web api preferrably as a ZonedDateTime in the client's time zone. I can then convert it to UTC for persistance in the DB.
If that's what you want, then:
In your moment.js code, use .format() instead of .toISOString(), which will still give you an ISO8601 string, but will include the local offset instead of setting it to UTC.
In your ASP.Net code, define your values as a DateTimeOffset (or a noda OffsetDateTime) rather than a DateTime.
However, I don't think that's really what you want. When it comes to dates and times, context is super important. Here, you said you were picking a date from a date picker. When you do that - what time is being chosen by the user? In most cases, they aren't choosing a time - they're just picking a date. But since the JavaScript Date object is really a "date + time" object, it assigns midnight as a default time. Moment is no better in this regard.
Really, converting to UTC doesn't make logical sense when you are just talking about a calendar date. The string value you probably should be sending across the wire should just be a whole date, as in "2015-03-14". My guess is that is what you are starting with anyway. If not, then do moment.utc(yourvalue).format("YYYY-MM-DD") to get it. (Using UTC here is just a way to avoid local time zone issues, like midnight not existing in Brazil on the spring-forward day.)
This corresponds to the NodaTime LocalDate type in your .NET code. If you weren't using Noda Time, you would define the type as a DateTime and just ignore the time portion. In your database, if there's a date-only type available, then use it. For example, SQL Server has a date type.
I'd also encourage you to watch my Pluralsight course, Date and Time Fundamentals - which covers many of these issues.
Regarding using NodaTime.Serialization.JsonNet in WebAPI (so you can use LocalDate directly), in your WebApiConfig.cs file, wire it up like so:
config.Formatters.JsonFormatter.SerializerSettings
.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
Then it should just work.
I would start by sending all dates and times to the server as UTC times. If you store UTC times only then you should be able to show the correct time that something is scheduled on the client side.
When you create your moment on the client side, do you run it out .toDate() first before sending it to the server side? What code are you running on the server side? Is it a .Net WebApi?

For a given hour, how to get the timezone where the hour is

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

Categories