Как игнорировать часовой пояс пользователя и заставить Date() использовать определенный часовой пояс

В приложении JS я получаю метку времени (экв. 1270544790922) с сервера (Ajax).

На основании этой временной метки я создаю Date использование объекта:

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

Сейчас, _date декодированная временная метка в часовом поясе текущего пользователя. Я не хочу этого

Мне бы хотелось _date преобразовать эту временную метку в текущее время в городе Хельсинки в Европе (без учета текущего часового пояса пользователя).

Как я могу это сделать?

6 ответов

Решение

Базовое значение объекта Date на самом деле в UTC. Чтобы доказать это, обратите внимание, что если вы введете new Date(0) вы увидите что-то вроде: Wed Dec 31 1969 16:00:00 GMT-0800 (PST), 0 обрабатывается как 0 в GMT, но .toString() Метод показывает местное время.

Замечание: UTC обозначает универсальный временной код. Текущее время в двух разных местах совпадает с UTC, но вывод может быть отформатирован по-разному.

Здесь нам нужно некоторое форматирование

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"

Это работает, но.... вы не можете использовать любой другой метод даты для своих целей, так как они описывают часовой пояс пользователя. Вам нужен объект даты, связанный с часовым поясом Хельсинки. На этом этапе вы можете использовать стороннюю библиотеку (я рекомендую это) или взломать объект date, чтобы вы могли использовать большинство его методов.

Вариант 1 - третье лицо, например момент-часовой пояс

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

Это выглядит намного элегантнее, чем то, что мы собираемся сделать дальше.

Вариант 2 - Взломать объект даты

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)

Он по-прежнему считает, что это GMT-0700 (PDT), но если вы не будете слишком пристально смотреть, вы можете ошибочно принять это за объект даты, полезный для ваших целей.

Я удобно пропустил часть. Вы должны быть в состоянии определить currentHelsinkiOffset, Если вы можете использовать date.getTimezoneOffset() на стороне сервера, или просто используйте некоторые операторы if, чтобы описать, когда произойдет изменение часового пояса, это должно решить вашу проблему.

Вывод - я думаю, что специально для этой цели вы должны использовать библиотеку дат, например момент-часовой пояс.

Для учета миллисекунд и часового пояса пользователя используйте следующее:

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

Просто другой подход

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

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

Ура!

У меня есть подозрение, что Ответ не дает правильного результата. В вопросе спрашивающий хочет конвертировать метку времени с сервера в текущее время в Хельсинки, не учитывая текущий часовой пояс пользователя.

Дело в том, что часовой пояс пользователя может быть тем, чем когда-либо, поэтому мы не можем доверять ему.

Например, если отметка времени 1270544790922 и у нас есть функция:

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);

Когда житель Нью-Йорка посещает страницу, предупреждение (_helsenkiTime) печатает:

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

И когда финляндер заходит на страницу, предупреждение (_helsenkiTime) печатает:

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

Таким образом, эта функция верна только в том случае, если посетитель страницы имеет целевой часовой пояс (Европа / Хельсинки) на своем компьютере, но не работает почти во всех других частях мира. А поскольку отметка времени сервера обычно указывается как отметка времени UNIX, то есть по определению в UTC, то есть в секундах с начала эпохи Unix (1 января 1970 г., 00:00:00 по Гринвичу), мы не можем определить DST или не-DST по отметке времени.

Таким образом, решение состоит в том, чтобы ОТКЛЮЧИТЬ текущий часовой пояс пользователя и реализовать какой-либо способ для вычисления смещения UTC независимо от того, находится ли дата в DST или нет. Javascript не имеет собственного метода для определения истории перехода DST другого часового пояса, кроме текущего часового пояса пользователя. Этого проще всего добиться с помощью сценария на стороне сервера, поскольку у нас есть простой доступ к базе данных часовых поясов сервера со всей историей переходов всех часовых поясов.

Но если у вас нет доступа к базе данных часовых поясов сервера (или любого другого сервера) И временная метка находится в UTC, вы можете получить аналогичную функциональность, жестко закодировав правила DST в Javascript.

Чтобы охватить даты 1998 - 2099 гг. В Европе / Хельсинки, вы можете использовать следующую функцию ( 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;
}

Примеры использования:

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);​

И это печатает следующее независимо от часового пояса пользователя:

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

Конечно, если вы можете вернуть временную метку в форме, в которой смещение (DST или не DST) уже добавлено к временной метке на сервере, вам не нужно рассчитывать ее на стороне клиента, и вы можете значительно упростить функцию. НО не забывайте НЕ использовать timezoneOffset(), потому что тогда вам приходится иметь дело с пользовательским часовым поясом, а это не желаемое поведение.

Предполагая, что вы получите временную метку в хельсинкском времени, я бы создал объект даты, установленный на полночь 1 января 1970 года по Гринвичу (для игнорирования настроек часового пояса в браузере). Затем просто добавьте к нему необходимое количество миллисекунд.

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

Остерегайтесь позже, чтобы всегда запрашивать значения UTC из объекта даты. Таким образом, пользователи будут видеть одинаковые значения даты независимо от локальных настроек. В противном случае значения даты будут смещены в соответствии с местными настройками времени.

Мои решения - определить настройку часового пояса, применяемую браузером, и отменить ее:

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.

В этот момент дата будет без часового пояса и всегда в формате UTC. Вы можете применить собственное смещение к метке времени для создания любого часового пояса.

Используйте это и всегда используйте функции UTC впоследствии, например 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')

Вы могли бы использовать setUTCMilliseconds()

var _date = new Date();
_date.setUTCMilliseconds(1270544790922);
Другие вопросы по тегам