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

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

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

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

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

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

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

Ответ 1

Начальное значение объекта Date фактически находится в UTC. Чтобы доказать это, обратите внимание, что если вы Wed Dec 31 1969 16:00:00 GMT-0800 (PST) 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"

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

Вариант 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, чтобы описывать, когда будут происходить изменения часового пояса, это должно решить вашу проблему.

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

Ответ 2

Чтобы учесть миллисекунды и часовой пояс пользователя, используйте следующее:

var _userOffset = _date.getTimezoneOffset()*60*1000; // user 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

Ответ 3

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

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

Если, например,. timestamp - 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 Epoch (1 января 1970 года 00:00:00 GMT), мы не можем определить DST или не-DST из метки времени.

Таким образом, решение состоит в том, чтобы DISREGARD текущего часового пояса пользователя и реализовать какой-то способ расчета UTC offset, соответствует ли дата DST или нет. Javascript не имеет встроенного метода для определения истории перехода DST другого часового пояса, чем текущий часовой пояс пользователя. Мы можем достичь этого проще всего с помощью серверной стороны script, поскольку у нас есть легкий доступ к базе данных часовых поясов сервера со всей историей перехода всех временных зон.

Но если у вас нет доступа к базе данных часовых поясов сервера (или любого другого сервера) И отметка времени находится в 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(), потому что тогда вам приходится иметь дело с пользовательским часовым поясом, и это не является желаемым поведением.

Ответ 4

Еще один подход

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

Ответ 5

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

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

Ответ 6

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

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