Як ігнорувати часовий пояс користувача та змусити Date () використовувати конкретний часовий пояс


104

У програмі JS я отримую часову позначку (екв. 1270544790922) Від сервера (Ajax).

На основі цієї часової позначки я створюю Dateоб'єкт, використовуючи:

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

Тепер _dateрозшифрована часова марка в часовому поясі поточного користувача. Я цього не хочу.

Я хотів би _ dateперетворити цю мітку часу на поточний час у місті Гельсінкі в Європі (не зважаючи на поточний часовий пояс користувача).

Як я можу це зробити?


Я знаю, що зміщення часового поясу в Гельсінкі становить +2 взимку і +3 за літнім часом. Але хто знає, коли DST? Лише якийсь локальний механізм, який недоступний в JS
warpech

Це можливо, але не використовуючи вбудовані методи Javascript, тому що у JavaScript немає методу визначити історію переходу часового поясу іншого часового поясу, ніж поточний часовий пояс системи користувача (і це, до речі, від браузера залежить щонайменше, коли ми переходимо на дати 80-х). Але це можливо: stackoverflow.com/a/12814213/1691517, і я думаю, що моя відповідь дає вам правильний результат.
Тімо Кахьонен

Відповіді:


64

Основне значення об'єкта Date фактично в UTC. Щоб довести це, зауважимо , що якщо ви друкуєте new Date(0)ви побачите що - щось на кшталт: Wed Dec 31 1969 16:00:00 GMT-0800 (PST). 0 в GMT розглядають як 0, але .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()на сервері або просто використовувати деякі, якщо заяви, щоб описати, коли відбудуться зміни часового поясу, це повинно вирішити вашу проблему.

Висновок - Я думаю, що спеціально для цієї мети вам слід використовувати бібліотеку дат, наприклад, часовий пояс .


також ... подібне завдання можна виконати, просто скориставшись зсувом gmt з одного місця. У такому випадку вам взагалі не потрібен JavaScript.
Парис

вибачте, але ні, я маю на увазі прямо протилежне :) Я відредагував питання, можливо, це тепер зрозуміліше
warpech

Гаразд, я змінив своє рішення. Я думаю, що це те, що ти шукаєш.
Парріс

На жаль, це єдине, що я також придумав. Я думав, що, можливо, браузер може створити для мене "_helsinkiOffset".
warpech

2
Я вважаю, що *60*60натомість має бути *60000, оскільки getTime знаходиться в мілісекундах, а getTimezoneOffset - у хвилинах, з яких 60000 мілісекунд за хвилину, а не 60 * 60 == 3600
AaronLS

20

Для обліку мілісекунд та часового поясу користувача використовуйте наступне:

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

Щоб замінити використання фіксованого зміщення для центрального, я використав концепцію створення дати за допомогою CST з фіксованим часом 00:00, а потім отриматиUTCHHours цієї дати.
grantwparks

+1 Це працювало для мене. Не впевнений, як відповідь "може" працювати, не маючи справи в мілісекундах.
Кріс Уолліс

2
@Ehren, чи не слід додати timezoneOffset, щоб дістатися до gmt, а потім відняти центральне зміщення?
кодер

15

Просто інший підхід

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

Ура!


2
Це може мати непередбачувані результати через літній час. Якщо клієнт знаходиться в часовому поясі, який використовує DST, результат аналізу може бути вимкнений на годину (деякі зони використовують дроби в годину). Напр .: Користувач знаходиться в Нью-Йорку і сьогодні 4 липня. Це означає, що користувач перебуває в GMT -0400 (по часовому поясі в східний час з моменту початку дії DST); пропущена часова марка призначена для 30 січня. Це - GMT-0500 (Східний стандартний часовий пояс - немає даного часу за часом року). Результатом буде година, тому що getTimezoneOffset () дає вам компенсувати зараз, а не те, що було в січні.
Димитър Даражанський

2
Щоб виправити цю проблему, потрібно зняти час переходу дати, яку ви переносите (не поточну зміну часу) : її new Date().getTimezoneOffset()слід змінити наnew Date(timestampStr).getTimezoneOffset()
Димитър Даражанський

13

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

Справа в тому, що часовий пояс користувача може бути будь-коли, тому ми не можемо йому довіряти.

Якщо напр. часова мітка - 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)

А коли Finlander відвідує сторінку, попередження (_helsenkiTime) друкує:

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

Таким чином, ця функція є правильною лише в тому випадку, якщо відвідувач сторінки має на своєму комп’ютері цільовий часовий пояс (Європа / Гельсінкі), але не спрацьовує майже у всіх інших частинах світу. Оскільки часова мітка сервера зазвичай є часовою міткою UNIX, яка за визначенням є в UTC, кількість секунд з часу епохи Unix (1 січня 1970 00:00:00 GMT), ми не можемо визначити DST або non-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 (), оскільки тоді вам доведеться мати справу з часовим поясом користувача, і це не бажана поведінка.


2
нб. У Гельсінкі є лише одне "l". Ця помилка насправді шкодить цій відповіді.
Бен Макінтайр

@BenMcIntyre Це трохи жарт. Або, мабуть, такий. :)
Тімо Кехьонен

ах, ніколи не
кодуй свою

3

Припускаючи, що ви отримаєте позначку часу в гельсінський час, я створив би об'єкт дати, встановлений на півночі 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

Будьте уважні пізніше, щоб завжди запитувати значення UTC від об’єкта дати. Таким чином користувачі побачать однакові значення дати незалежно від локальних налаштувань. Інакше значення дат будуть зміщені відповідно до місцевих налаштувань часу.


0

Ви можете використовувати setUTCMilliseconds()

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

Ця відповідь неправильна. setUTCMilliseconds додає кількість заданих мілісекунд до дати.
Тібор

@Tibor: Від MozDev: setUTCMilliseconds()метод встановлює мілісекунди на вказану дату відповідно до загального часу. [...] Якщо вказаний параметр виходить за рамки очікуваного діапазону, setUTCMilliseconds()намагається відповідно оновити інформацію про дату в об'єкті Date. Іншими словами, ви створюєте Dateоб'єкт із заданою часовою позначкою Unix, за UTC.
jimasun

У школах w3schools: метод setUTCMilliseconds () встановлює мілісекунди (від 0 до 999), відповідно до універсального часу. Спробуйте відповідь вище і переконайтеся самі.
Тібор

Від MDN: Параметри мілісекунди. Значення: Число від 0 до 999, що представляє мілісекунди ...
Tibor

Ваш приклад працює не так, як очікувалося, оскільки новий Date () створює об'єкт дати з поточною локальною датою. А після цього додається вказана кількість мілісекунд. Тож у вашому прикладі, якщо поточна місцева дата 2017-05-04, отримана дата буде десь після 4027 року ...
Tibor
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.