Timezone offset of any time using using JavaScript Date - javascript

I am using var offset = new Date().getTimezoneOffset() to get the current timezone offset of user.
But, how can I get the timezone offset for any future time?
This is required because the timezone offset is different when DST is enabled/disabled.So I cant assume the same offset for future time.

This question confused me into thinking that perhaps getTimezoneOffset() wouldn't correctly get the timezone offset for future dates already. It does.
As far as JavaScript is concerned, you give it a milliseconds since the epoch date, which is ubiquitous, and it gives you the offset in local time as it will be on that date, because all the Date functionality cares about is taking a universal date/time and passing back a date that will make sense to the local user, unless you specifically ask it for the universal equivalent, with methods like getUTCDate().
So running
var summer = new Date(1403170847000);
alert('Summer offset=' + summer.getTimezoneOffset());
var winter = new Date(1419500175000);
alert('Winter offset=' + winter.getTimezoneOffset());
in a console where the local time includes a DST period, reports for me in the UK an offset of -60 (i.e. GMT+1) for the date in the summer, and 0 (i.e. GMT) for the date in the winter.
http://jsfiddle.net/SX9SN/

Related

Working with different timezones in Javascript

I am working on a cloud based application which deals extensively with date and time values, for users across the world.
Consider a scenario, in JavaScript, where my machine is in India (GMT+05:30), and I have to display a clock running in California's timezone (GMT-08:00).
In this case I have to get a new date object,
let india_date = new Date()
add it's time zone offset value,
let uts_ms = india_date.getTime() + india_date.getTimezoneOffset()
add california's timezone offset value,
let california_ms = utc_ms + getCaliforniaTimezoneOffsetMS()
and finally the date object.
let california_date: Date = new Date(california_ms)
Is there any way to directly deal with these kinds of time zones without having to convert the values again and again?
First, let's talk about the code in your question.
let india_date = new Date()
You have named this variable india_date, but the Date object will only reflect India if the code is run on a computer set to India's time zone. If it is run on a computer with a different time zone, it will reflect that time zone instead. Keep in mind that internally, the Date object only tracks a UTC based timestamp. The local time zone is applied when functions and properties that need local time are called - not when the Date object is created.
add it's timezone offset value
let uts_ms = india_date.getTime() + india_date.getTimezoneOffset()
This approach is incorrect. getTime() already returns a UTC based timestamp. You don't need to add your local offset. (also, the abbreviation is UTC, not UTS.)
Now add california's timezone offset value
let california_ms = utc_ms + getCaliforniaTimezoneOffsetMS()
Again, adding an offset is incorrect. Also, unlike India, California observes daylight saving time, so part of the year the offset will be 480 (UTC-8), and part of the year the offset will be 420 (UTC-7). Any function such as your getCaliforniatimezoneOffsetMS would need to have the timestamp passed in as a parameter to be effective.
and finally the date object
let california_date: Date = new Date(california_ms)
When the Date constructor is passed a numeric timestamp, it must be in terms of UTC. Passing it this california_ms timestamp is actually just picking a different point in time. You can't change the Date object's behavior to get it to use a different time zone just by adding or subtracting an offset. It will still use the local time zone of where it runs, for any function that requires a local time, such as .toString() and others.
There is only one scenario where this sort of adjustment makes sense, which is a technique known as "epoch shifting". The timestamp is adjusted to shift the base epoch away from the normal 1970-01-01T00:00:00Z, thus allowing one to take advantage of the Date object's UTC functions (such as getUTCHours and others). The catch is: once shifted, you can't ever use any of the local time functions on that Date object, or pass it to anything else that expects the Date object to be a normal one. Epoch shifting done right is what powers libraries like Moment.js. Here is another example of epoch shifting done correctly.
But in your example, you are shifting (twice in error) and then using the Date object as if it were normal and not shifted. This can only lead to errors, evident by the time zone shown in toString output, and will arise mathematically near any DST transitions of the local time zone and the intended target time zone. In general, you don't want to take this approach.
Instead, read my answer on How to initialize a JavaScript Date to a particular time zone. Your options are listed there. Thanks.
JavaScript Date objects store date and time in UTC but the toString() method is automatically called when the date is represented as a text value which displays the date and time in the browser's local time zone. So, when you want to convert a datetime to a time zone other than your local time, you are really converting from UTC to that time zone (not from your local time zone to another time zone).
If your use case is limited to specific browsers and you are flexible on formatting (since browsers may differ in how they display date string formats), then you may be able to use toLocaleString(), but browsers like Edge, Android webview, etc do not fully support the locales and options parameters.
Following example sets both the locale and timezone to output the date in a local format that may vary from browser to browser.
const dt = new Date();
const kolkata = dt.toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' });
const la = dt.toLocaleString('en-US', { timeZone: 'America/Los_Angeles' });
console.log('Kolkata:', kolkata);
// example output: Kolkata: 19/3/2019, 7:36:26 pm
console.log('Los Angeles:', la);
// example output: Los Angeles: 3/19/2019, 7:06:26 AM
You could also use Moment.js and Moment Timezone to convert date and time to a time zone other than your local time zone. For example:
const dt = moment();
const kolkata = dt.tz('Asia/Kolkata').format();
const la = dt.tz('America/Los_Angeles').format();
console.log(kolkata);
// example output: 2019-03-19T19:37:11+05:30
console.log(la);
// example output: 2019-03-19T07:07:11-07:00
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.23/moment-timezone-with-data.min.js"></script>
Well, you really do kind of have to convert anytime you want to change the display, but it's not as bad as you think.
First, store all time as UTC. Probably using the milliseconds format, e.g. Date.UTC().
Second, do all manipulation / comparison using that stored info.
Third, if your cloud-based application has an API that API should only talk in terms of UTC as well, though you could provide the ISO string if you prefer that to the MS, or if you expect clients to handle that better.
Fourth and finally, only in the UI should you do the final conversion to the local date/time string, either with the method you're describing or using a library such as momentjs
new Date creates a Date object with a time value that is UTC. If you can guarantee support for the timeZone option of toLocaleString (e.g. corporate environment with a controlled SOE), you can use it to construct a timestamp in any time zone and any format, but it can be a bit tedious. Support on the general web may be lacking. A library would be preferred in that case if you need it to work reliably.
E.g. to get values for California, you can use toLocaleString and "America/Los_Angeles" for the timeZone option:
var d = new Date();
// Use the default implementation format
console.log(d.toLocaleString(undefined, {timeZone:'America/Los_Angeles'}));
// Customised format
var weekday = d.toLocaleString(undefined, {weekday:'long', timeZone:'America/Los_Angeles'});
var day = d.toLocaleString(undefined, {day:'numeric', timeZone:'America/Los_Angeles'});
var month = d.toLocaleString(undefined, {month:'long', timeZone:'America/Los_Angeles'});
var year = d.toLocaleString(undefined, {year:'numeric', timeZone:'America/Los_Angeles'});
var hour = d.toLocaleString(undefined, {hour:'numeric',hour12: false, timeZone:'America/Los_Angeles'});
var minute = d.toLocaleString(undefined, {minute:'2-digit', timeZone:'America/Los_Angeles'});
var ap = hour > 11? 'pm' : 'am';
hour = ('0' + (hour % 12 || 12)).slice(-2);
console.log(`The time in Los Angeles is ${hour}:${minute} ${ap} on ${weekday}, ${day} ${month}, ${year}`);
Getting the timezone name is a little more difficult, it's difficult to get it without other information.

Moment.js local relative time

I have dates in my database set to Europe/London time. I am using Moment.js to show relative time e.g. "3 minutes ago". This works fine for me as I am in the same timezone, but for example, someone who is PST timezone would see "in 8 hours". How can I fix this?
My current code is like this:
$('time').text( moment( '2016-01-22 18:00:00' ).fromNow() );
To echo Jon's answer, moment's relative time functionality is strictly UTC based, so the behavior you describe won't actually happen, unless you are interpreting the original timestamp in local time.
It's hard to say if you're doing that or not, as you didn't give a sample value of the input string.
If your times are indeed UTC based, but that's not reflected in the input string, then use moment.utc instead of just moment.
And no, London is not the same as UTC.
I believe that the best approach is to store the date in UTC and then convert this to the local time zone for display. Note that this is not necessarily the same as London time because UTC does away with daylight savings time nonsense. You can do everything that you need with the date class provided the time stamp stored in the database does not have to deal with the vagaries of time zone and DST. The date class maintains its own epoch internally as milliseconds elapsed since midnight 1 January 1970 UTC. You can evaluate the difference between two Date objects as follows:
var agora = Date.now();
var stored = ... // the date that was stored in your database
var diff_msec = agora.getTime() - stored.getTime();
Knowing that the difference and that its units are milliseconds, you can convert the difference to whatever units are best for presentation.

Javascript - subtract two dates in milliseconds considering daylight time saving

I have the following function:
function subtractDatesMs(date1, date2) {
var departureDate = new Date(date1);
var arriveDate = new Date(date2);
var diff = (arriveDate.getTime() - departureDate.getTime());
return diff;
}
The dates sent as parameters are like this:
"2015-11-09T15:50:00-04:00",
"2015-11-09T12:40:00-05:00"
including timezone offset value.
It works fine in most of the cases, however when I subtract some dates from a region/area where are using daylight time saving the difference is incorrect (it is delayed/ahead 1 hour for example).
What is the best way to subtract these dates and get the correct difference in milliseconds considering daylight time savings from the region/area of the timezone it comes?
Any sample is welcomed. You can use jquery if you want or any other library to get the correct values.
I think the problem is in the date strings that your code is receiving. The UTC offset should properly reflect the additional DST offset. The example time you gave "2015-11-09T12:40:00-05:00" implies Eastern Standard Time* because of the -05:00. The other example time "2015-11-09T15:50:00-04:00" implies Atlantic Standard Time or Eastern Daylight Time (i.e. Eastern Standard plus DST)
JavaScript's Date object will properly convert the network time formats that you gave in the example.
If it is not possible to correct the input times, you will then need to also know the country since DST is specific to a country or region of a country. You can then use a solution like in the answer given for How to know whether a country is using Daylight Saving Time using JavaScript?, which retrieves the correct timezone offset for a given place or latitude & longitude.
* = This assumes North America

Inconsistent getTimezoneOffset results

The documentation seems to suggest getTimezoneOffset() always returns the offset of the current locale, irregardless of the date object. But I'm getting inconsistent results that I can't understand.
new Date().getTimezoneOffset() // -120
new Date("2015-03-10T15:48:05+01:00").getTimezoneOffset() // -60
new Date("2015-03-10T15:48:05-04:00").getTimezoneOffset() // -60
Also, is there a better way to get the timezone off a datetime string (maybe with moment.js)?
getTimezoneOffset returns the offset for the specific moment in time represented by the Date object it is called on, using the time zone setting of the computer that it executing the code.
Since many time zones change their offset for daylight saving time, it is perfectly normal for the value to differ for different dates and times. When you call it on new Date(), you're getting the current offset.
The value returned from getTimezoneOffset is in terms of minutes west of UTC, as compared to the more common offsets returned in [+/-]HH:mm format, which are east of UTC. Therefore, the time zone you gave alternates between UTC+1, and UTC+2. My guess is the computer that gave this output was in one of the zones that uses Central European Time - though it could be one of several others.
Also, when you pass in an offset as part of an ISO8601 formatted string, that offset is indeed taken into account - but only during parsing. The offset is applied, and the Date object holds on to the UTC timestamp internally. It then forgets anything about the offset you supplied. On output, some of the functions will explicitly use UTC, but most of them will convert to the local time zone before emitting their result.
You also asked about how to get the offset of a datetime string using moment.js. Yes, that is quite simple:
// Create a moment object.
// Use the parseZone function to retain the zone provided.
var m = moment.parseZone('2015-03-10T15:48:05-04:00');
// get the offset in minutes EAST of UTC (opposite of getTimezoneOffset)
var offset = m.utcOffset(); // -240
// alternatively, get it as a string in [+/-]HH:mm format
var offsetString = m.format("Z"); // "-04:00"
It's because of Daylight Savings Time. For your timezone, on June 11th it is in UTC+2 and on March 10th it's in UTC+1:
// when in DST (since it's June)
new Date("2015-06-11T00:00:00Z").getTimezoneOffset(); // -120
// when not in DST
new Date("2015-03-10T15:48:05+01:00").getTimezoneOffset(); // -60
For me, since I'm in the Eastern Time Zone, the following will happen:
// when in EST
new Date("2015-03-01T00:00:00Z").getTimezoneOffset(); // 300
// when in EDT
new Date("2015-06-01T00:00:00Z").getTimezoneOffset(); // 240

Javascript countdown using absolute timezone?

I have a javascript countdown timer that works by taking a specified date and time, and comparing it to the current date and time. The issue is, the current time is relative to the users timezone, so the time remaining is different between users.
How can I have the timer countdown till a time in a specific timezone, in my case GMT -5 hours?
I understand i can use the below code to get the users timezone, but I am lost as how to use this.
myDateObj.getTimezoneOffset( ) / 60
You can use Date.UTC(year,month,day,hours,minutes,seconds,msec)
It operates just like the Date constructor, but returns the timestamp of the arguments at Greenwich time (offset=0) instead of local time.
var localtime=new Date(Date.UTC(year,month,day,hours,minutes,seconds,msec))
returns the local time for the UTC time specified.
Everyone (whose clock is set correctly) will end the countdown together.
A quick search reveals: convert-the-local-time-to-another-time-zone-with-this-javascript
Following the article verbatim gets you this example:
var d = new Date();
var localTime = d.getTime();
var localOffset = d.getTimezoneOffset() * 60000;
var utc = localTime + localOffset;
// obtain and add destination's UTC time offset
// for example, Bombay
// which is UTC + 5.5 hours
var offset = 5.5;
var bombay = utc + (3600000*offset);
var nd = new Date(bombay);
alert("Bombay time is " + nd.toLocaleString() + "<br>");
jsFiddle: http://jsfiddle.net/GEpaH/
Just update with your desired offset and you should be all set.
Just create a Date with an RFC 2822-timestamp with timezone. That time will then be converted to the users current location (based on OS settings). Even with corrections for daylight savings time!
I'm in Norway, which currently is in daylight savings time, so it's GMT+2. Here is what happens when I create a Date object using GMT-0500:
var myDateObj = new Date("Fri Apr 17 2015 12:00:00 GMT-0500 (CDT)");
myDateObj.toString();
Fri Apr 17 2015 19:00:00 GMT+0200 (CEST)
How to get the correct date string for your location? If it's the timezone you're currently in; just do myDateObj.toString() in your browsers dev-tools console. For a different timezone; change the timezone in your operating system first. (Or read the RFC)
new Date().toString();
Fri Apr 17 2015 12:36:57 GMT+0200 (CEST)
You don't really say exactly what your are trying to accomplish. The javascript date object retrieves the local time and the "timezone offset" (relative to GMT (UTC)). These are of course "set" by the user so even if in the same time zone, two users are very unlikely to have the same "time".
If you are trying to time between different users you need to be referencing some centralized time authority.
I would use an AJAX type call (XMLHttpRequest) to a page own my own server that returns my server's time. That way each user is referencing the same time.
Google XMLHttpRequest to find examples of the JS code you need (and oftentimes the corresponding server-side code for a simple service such as this).
PS: I would also install some simple client software to keep the time on my server accurate by synching with an atomic clock every 10 minutes.
You can't get an accurate date with JavaScript as it is client-side and it is based on the user's operating system clock. You can use jCounter to display countdowns based on server-side timezones.
But hey, if you really want to do it yourself, download jCounter and you'll find a dateandtime.php file as well which retrieves the current date server-side, as timestamp (it will have to be placed on a server btw, not on your desktop :P)
Check how that script uses that file and retrieves the real current date to operate against it and calculate accurate countdowns.
Cheers
To revise the approach Brandon has taken to calculate a UTC-shifted time, we can slim the code down into a two-line extension of the Date object:
/* getOffsetDate - Returns a Date shifted by a certain offset
* #param offset the UTC offset to shift, in hours
* #return new date object shifted by UTC offset
*/
Date.prototype.getOffsetDate = function( offset ) {
utc = this.getTime() + (this.getTimezoneOffset() * 60000);
return new Date(utc + (3600000*offset));
}
You can then calculate the UTC-5 shifted date as follows:
myDate = new Date().getOffsetDate(-5);
It should be noted that extending native prototypes in this manner is generally considered a bad practice since it muddles core objects that other libraries depend upon. To justify it, you'd have to argue this functionality should be a native part of the Date object.

Categories