How to ignore user's time zone and force Date() use specific time zone
Asked Answered
S

7

125

In an JS app, I receive timestamp (eq. 1270544790922) from server (Ajax).

Basing on that timestamp I create Date object using:

var _date = new Date();
_date.setTime(1270544790922);

Now, _date decoded timestamp in current user locale time zone. I don't want that.

I would like _date to convert this timestamp to current time in city of Helsinki in Europe (disregarding current time zone of the user).

How can I do that?

Snooker answered 5/5, 2010 at 8:35 Comment(3)
I know that time zone offset in Helsinki is +2 in winter and +3 in DST. But who knows when is DST? Only some locale mechanism that is not available in JSSnooker
It is possible, but not using native methods of Javascript, because javascript has no method to determine a timezone transition history of other timezone than user's system's current timezone (and it is by the way browser dependent at least when we go to 80's dates). But this way it is possible: https://mcmap.net/q/179839/-how-to-ignore-user-39-s-time-zone-and-force-date-use-specific-time-zone and I think that my answer gives you the correct result.Doura
w3schools.com/jsref/jsref_setutcmilliseconds.aspAnaya
Z
80

A Date object's underlying value is actually in UTC. To prove this, notice that if you type new Date(0) you'll see something like: Wed Dec 31 1969 16:00:00 GMT-0800 (PST). 0 is treated as 0 in GMT, but .toString() method shows the local time.

Big note, UTC stands for Universal time code. The current time right now in 2 different places is the same UTC, but the output can be formatted differently.

What we need here is some formatting

var _date = new Date(1270544790922); 
// outputs > "Tue Apr 06 2010 02:06:30 GMT-0700 (PDT)", for me
_date.toLocaleString('fi-FI', { timeZone: 'Europe/Helsinki' });
// outputs > "6.4.2010 klo 12.06.30"
_date.toLocaleString('en-US', { timeZone: 'Europe/Helsinki' });
// outputs > "4/6/2010, 12:06:30 PM"

This works but.... you can't really use any of the other date methods for your purposes since they describe the user's timezone. What you want is a date object that's related to the Helsinki timezone. Your options at this point are to use some 3rd party library (I recommend this), or hack-up the date object so you can use most of it's methods.

Option 1 - a 3rd party like moment-timezone

moment(1270544790922).tz('Europe/Helsinki').format('YYYY-MM-DD HH:mm:ss')
// outputs > 2010-04-06 12:06:30
moment(1270544790922).tz('Europe/Helsinki').hour()
// outputs > 12

This looks a lot more elegant than what we're about to do next.

Option 2 - Hack up the date object

var currentHelsinkiHoursOffset = 2; // sometimes it is 3
var date = new Date(1270544790922);
var helsenkiOffset = currentHelsinkiHoursOffset*60*60000;
var userOffset = _date.getTimezoneOffset()*60000; // [min*60000 = ms]
var helsenkiTime = new Date(date.getTime()+ helsenkiOffset + userOffset);
// Outputs > Tue Apr 06 2010 12:06:30 GMT-0700 (PDT)

It still thinks it's GMT-0700 (PDT), but if you don't stare too hard you may be able to mistake that for a date object that's useful for your purposes.

I conveniently skipped a part. You need to be able to define currentHelsinkiOffset. If you can use date.getTimezoneOffset() on the server side, or just use some if statements to describe when the time zone changes will occur, that should solve your problem.

Conclusion - I think especially for this purpose you should use a date library like moment-timezone.

Zoon answered 5/5, 2010 at 8:44 Comment(10)
also... a similar task can be done, by simply using the gmt offset from one location. You don't need javascript at all in that case.Zoon
sorry but no, I mean the exact opposite :) I edited the question, maybe it is clearer nowSnooker
Ok I changed my solution. I think this is what you are looking for.Zoon
Unfortunately that's the only thing that I also came up with. I thought that maybe browser could generate "_helsinkiOffset" for me.Snooker
I believe *60*60 should instead be *60000, as getTime is in milliseconds and getTimezoneOffset is in minutes, of which there are 60000 milliseconds in a minute, not 60*60==3600Pignut
It seemed to work without it actually, but reviewing it now I don't see how it could actually work. I'll change it since everyone is getting into a fit. Thanks! :)Zoon
This is unnecessary complicated. Check my simple answer. Using setUTCMilliseconds() will set the time of the Date object ignoring the user's timezone.Crossland
"A date object in JS is in the user's local time" is not correct. A Date object's time value is based on UTC. The local time zone is used to calculate the value and for methods like toString and toLocalString, but the internal time value is still UTC.Vair
@Vair makes sense (will change the answer). At the time I thought that since new Date() in a console said described my local time, that it's internal representation was a local time. Good call out.Zoon
@Crossland setUTCMilliseconds will only work if you do new Date(0) first. .setUTCMilliseconds() will pretty much add the milliseconds you specify to the date. Even still that results in the time you set and nothing more. It is still UTC time with no modifications to shift the date to helsinki time.Zoon
M
21

To account for milliseconds and the user's time zone, use the following:

var _userOffset = _date.getTimezoneOffset()*60*1000; // user's offset time
var _centralOffset = 6*60*60*1000; // 6 for central time - use whatever you need
_date = new Date(_date.getTime() - _userOffset + _centralOffset); // redefine variable
Monarski answered 22/11, 2011 at 1:0 Comment(4)
To replace using a fixed offset for central, I used the concept of creating a date using CST with a fixed time of 00:00, then getUTCHHours of that date.Moppet
+1 This worked for me. Not sure how 'the' answer can work without dealing in milliseconds.Frentz
@Monarski shouldn't you add the timezoneOffset to get to gmt and then subtract the central offset?Gatekeeper
What if you don't know the timezone?Markettamarkey
H
21

Just another approach

function parseTimestamp(timestampStr) {
  return new Date(new Date(timestampStr).getTime() + (new Date(timestampStr).getTimezoneOffset() * 60 * 1000));
};

//Sun Jan 01 2017 12:00:00
var timestamp = 1483272000000;
date = parseTimestamp(timestamp);
document.write(date);

Cheers!

Horton answered 21/4, 2016 at 11:45 Comment(2)
This could have unintended results due to the Daylight Savings Time. If the client is in a timezone that uses DST, the result of the parsing could be off by an hour (some zones use fractions of an hour). E.g.: The user is in NY and today is July 4th. That means that the user is in GMT -0400 (Eastern Daylight Timezone since the DST has kicked in); the passed in timestamp is for January 30th., which is GMT-0500 (Eastern Standard Timezone - no DST that time of the year). The result will be an hour off, because getTimezoneOffset() gives you the offset now, not what it was in January.Titanate
To fix this issue, you need to take the time offset of the date you are passing in (not the current time shift): new Date().getTimezoneOffset() should be changed to new Date(timestampStr).getTimezoneOffset()Titanate
C
14

I have a suspicion, that the Answer doesn't give the correct result. In the question the asker wants to convert timestamp from server to current time in Hellsinki disregarding current time zone of the user.

It's the fact that the user's timezone can be what ever so we cannot trust to it.

If eg. timestamp is 1270544790922 and we have a function:

var _date = new Date();
_date.setTime(1270544790922);
var _helsenkiOffset = 2*60*60;//maybe 3
var _userOffset = _date.getTimezoneOffset()*60*60; 
var _helsenkiTime = new Date(_date.getTime()+_helsenkiOffset+_userOffset);

When a New Yorker visits the page, alert(_helsenkiTime) prints:

Tue Apr 06 2010 05:21:02 GMT-0400 (EDT)

And when a Finlander visits the page, alert(_helsenkiTime) prints:

Tue Apr 06 2010 11:55:50 GMT+0300 (EEST)

So the function is correct only if the page visitor has the target timezone (Europe/Helsinki) in his computer, but fails in nearly every other part of the world. And because the server timestamp is usually UNIX timestamp, which is by definition in UTC, the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT), we cannot determine DST or non-DST from timestamp.

So the solution is to DISREGARD the current time zone of the user and implement some way to calculate UTC offset whether the date is in DST or not. Javascript has not native method to determine DST transition history of other timezone than the current timezone of user. We can achieve this most simply using server side script, because we have easy access to server's timezone database with the whole transition history of all timezones.

But if you have no access to the server's (or any other server's) timezone database AND the timestamp is in UTC, you can get the similar functionality by hard coding the DST rules in Javascript.

To cover dates in years 1998 - 2099 in Europe/Helsinki you can use the following function (jsfiddled):

function timestampToHellsinki(server_timestamp) {
    function pad(num) {
        num = num.toString();
        if (num.length == 1) return "0" + num;
        return num;
    }

    var _date = new Date();
    _date.setTime(server_timestamp);

    var _year = _date.getUTCFullYear();

    // Return false, if DST rules have been different than nowadays:
    if (_year<=1998 && _year>2099) return false;

    // Calculate DST start day, it is the last sunday of March
    var start_day = (31 - ((((5 * _year) / 4) + 4) % 7));
    var SUMMER_start = new Date(Date.UTC(_year, 2, start_day, 1, 0, 0));

    // Calculate DST end day, it is the last sunday of October
    var end_day = (31 - ((((5 * _year) / 4) + 1) % 7))
    var SUMMER_end = new Date(Date.UTC(_year, 9, end_day, 1, 0, 0));

    // Check if the time is between SUMMER_start and SUMMER_end
    // If the time is in summer, the offset is 2 hours
    // else offset is 3 hours
    var hellsinkiOffset = 2 * 60 * 60 * 1000;
    if (_date > SUMMER_start && _date < SUMMER_end) hellsinkiOffset = 
    3 * 60 * 60 * 1000;

    // Add server timestamp to midnight January 1, 1970
    // Add Hellsinki offset to that
    _date.setTime(server_timestamp + hellsinkiOffset);
    var hellsinkiTime = pad(_date.getUTCDate()) + "." + 
    pad(_date.getUTCMonth()) + "." + _date.getUTCFullYear() + 
    " " + pad(_date.getUTCHours()) + ":" +
    pad(_date.getUTCMinutes()) + ":" + pad(_date.getUTCSeconds());

    return hellsinkiTime;
}

Examples of usage:

var server_timestamp = 1270544790922;
document.getElementById("time").innerHTML = "The timestamp " + 
server_timestamp + " is in Hellsinki " + 
timestampToHellsinki(server_timestamp);

server_timestamp = 1349841923 * 1000;
document.getElementById("time").innerHTML += "<br><br>The timestamp " + 
server_timestamp + " is in Hellsinki " + timestampToHellsinki(server_timestamp);

var now = new Date();
server_timestamp = now.getTime();
document.getElementById("time").innerHTML += "<br><br>The timestamp is now " +
server_timestamp + " and the current local time in Hellsinki is " +
timestampToHellsinki(server_timestamp);​

And this print the following regardless of user timezone:

The timestamp 1270544790922 is in Hellsinki 06.03.2010 12:06:30

The timestamp 1349841923000 is in Hellsinki 10.09.2012 07:05:23

The timestamp is now 1349853751034 and the current local time in Hellsinki is 10.09.2012 10:22:31

Of course if you can return timestamp in a form that the offset (DST or non-DST one) is already added to timestamp on server, you don't have to calculate it clientside and you can simplify the function a lot. BUT remember to NOT use timezoneOffset(), because then you have to deal with user timezone and this is not the wanted behaviour.

Countermarch answered 10/10, 2012 at 7:33 Comment(3)
nb. Helsinki only has one 'l'. This mistake really detracts from this answer.Kyphosis
@BenMcIntyre It's a little joke. Or supposed to be such one. :)Doura
ah, never code your own time/timezone implementation... but I'm too lazy to -1Hear
L
4

Presuming you get the timestamp in Helsinki time, I would create a date object set to midnight January 1 1970 UTC (for disregarding the local timezone settings of the browser). Then just add the needed number of milliseconds to it.

var _date	= new Date( Date.UTC(1970, 0, 1, 0, 0, 0, 0) );
_date.setUTCMilliseconds(1270544790922);

alert(_date); //date shown shifted corresponding to local time settings
alert(_date.getUTCFullYear());    //the UTC year value
alert(_date.getUTCMonth());       //the UTC month value
alert(_date.getUTCDate());        //the UTC day of month value
alert(_date.getUTCHours());       //the UTC hour value
alert(_date.getUTCMinutes());     //the UTC minutes value

Watch out later, to always ask UTC values from the date object. This way users will see the same date values regardless of local settings. Otherwise date values will be shifted corresponding to local time settings.

Lashkar answered 29/3, 2017 at 16:15 Comment(1)
The knowledge that each of the functions has a getUTC equivalent baked in was what I needed.Fusil
C
1

Use this and always use UTC functions afterwards e.g. mydate.getUTCHours();

   function getDateUTC(str) {
        function getUTCDate(myDateStr){
            if(myDateStr.length <= 10){
                //const date = new Date(myDateStr); //is already assuming UTC, smart - but for browser compatibility we will add time string none the less
                const date = new Date(myDateStr.trim() + 'T00:00:00Z');
                return date;
            }else{
                throw "only date strings, not date time";
            }
        }

        function getUTCDatetime(myDateStr){
            if(myDateStr.length <= 10){
                throw "only date TIME strings, not date only";
            }else{
                return new Date(myDateStr.trim() +'Z'); //this assumes no time zone is part of the date string. Z indicates UTC time zone
            }
        }  
        
        let rv = '';
        
        if(str && str.length){
            if(str.length <= 10){
                rv = getUTCDate(str);
            }else if(str.length > 10){
                rv = getUTCDatetime(str);
            } 
        }else{
            rv = '';
        }
        return rv;
    }

console.info(getDateUTC('2020-02-02').toUTCString());

var mydateee2 = getDateUTC('2020-02-02 02:02:02');
console.info(mydateee2.toUTCString());

// you are free to use all UTC functions on date e.g.
console.info(mydateee2.getUTCHours())
console.info('all is good now if you use UTC functions')
Cashew answered 26/11, 2020 at 23:21 Comment(0)
L
0

My solutions is to determine timezone adjustment the browser applies, and reverse it:

var timestamp = 1600913205;  //retrieved from unix, that is why it is in seconds

//uncomment below line if you want to apply Pacific timezone
//timestamp += -25200;

//determine the timezone offset the browser applies to Date()
var offset = (new Date()).getTimezoneOffset() * 60;   

//re-initialize the Date function to reverse the timezone adjustment
var date = new Date((timestamp + offset) * 1000);

//here continue using date functions.

This point the date will be timezone free and always UTC, You can apply your own offset to timestamp to produce any timezone.

Lenna answered 24/9, 2020 at 2:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.