Як ініціалізувати дату JavaScript до певного часового поясу


232

У мене є час дати у певному часовому поясі як рядок, і я хочу перетворити це на місцевий час. Але я не знаю, як встановити часовий пояс в об’єкті Date.

Наприклад, я Feb 28 2013 7:00 PM ET,тоді можу

var mydate = new Date();
mydate.setFullYear(2013);
mydate.setMonth(02);
mydate.setDate(28);
mydate.setHours(7);
mydate.setMinutes(00);  

Наскільки я знаю, я можу встановити час UTC або місцевий час. Але як мені встановити час в іншому часовому поясі?

Я намагався використовувати додавання / віднімання зміщення від UTC, але я не знаю, як протистояти літній економії. Не впевнений, чи рухаюсь я в правильному напрямку.

Як я можу перейти до перетворення часу з іншого часового поясу на місцевий час у JavaScript?



1
Об'єкти дат не мають часового поясу, вони є UTC.
RobG

Відповіді:


351

Фон

DateОб'єкт JavaScript внутрішньо відстежує час у UTC, але, як правило, приймає введення та видає висновок за місцевим часом на комп'ютері, на якому він працює. У ньому дуже мало можливостей для роботи з часом в інших часових поясах.

Внутрішнє представлення Dateоб'єкта - це єдине число, що представляє кількість мілісекунд, що минули з тих пір 1970-01-01 00:00:00 UTC, без огляду на високосні секунди. Немає часового поясу або формату рядка, що зберігається в самому об'єкті Date. Коли використовуються різні функції Dateоб'єкта, локальний часовий пояс комп'ютера застосовується до внутрішнього представлення. Якщо функція створює рядок, то інформація про локальний комп'ютер може бути врахована для визначення способу створення цієї рядки. Деталі залежать від функції, а деякі залежать від реалізації.

Єдині операції, які Dateоб'єкт може робити з немісцевими часовими поясами, це:

  • Він може проаналізувати рядок, що містить числове зміщення UTC за будь-який часовий пояс. Він використовує це для налаштування значення, що аналізується, і зберігає еквівалент UTC. Початковий локальний час і зміщення не зберігаються в отриманому Dateоб'єкті. Наприклад:

    var d = new Date("2020-04-13T00:00:00.000+08:00");
    d.toISOString()  //=> "2020-04-12T16:00:00.000Z"
    d.valueOf()      //=> 1586707200000  (this is what is actually stored in the object)
    
  • У середовищах, в яких реалізований інтернаціоналізаційний інтерфейс інтерфейсу ECMASCript (він же "Intl"), Dateоб'єкт може створювати специфічну для місцевості рядок, адаптовану до заданого ідентифікатора часового поясу. Це здійснюється за допомогою timeZoneопції до toLocaleStringта її варіацій. Більшість реалізацій підтримуватимуть ідентифікатори часового поясу IANA, такі як 'America/New_York'. Наприклад:

    var d = new Date("2020-04-13T00:00:00.000+08:00");
    d.toLocaleString('en-US', { timeZone: 'America/New_York' })
    //=> "4/12/2020, 12:00:00 PM"
    // (midnight in China on Apring 13th is noon in New York on April 12th)
    

    Більшість сучасних середовищ підтримують повний набір ідентифікаторів часового поясу IANA ( див. Таблицю сумісності тут ). Однак майте на увазі, що єдиний ідентифікатор, необхідний для підтримки Intl, - 'UTC'це слід ретельно перевірити, чи потрібно підтримувати старі браузери або нетипові середовища (наприклад, легкі пристрої IoT).

Бібліотеки

Є кілька бібліотек, які можна використовувати для роботи з часовими поясами. Хоча вони все ще не можуть змусити Dateоб'єкт поводитись інакше, вони, як правило, реалізують стандартну базу даних часових поясів IANA та надають функції для її використання в JavaScript. Сучасні бібліотеки використовують дані часового поясу, що надаються API Intl, але старші бібліотеки зазвичай мають накладні витрати, особливо якщо ви працюєте у веб-браузері, оскільки база даних може бути трохи великою. Деякі з цих бібліотек також дозволяють вибірково скорочувати набір даних, або за допомогою яких підтримуються часові пояси та / або за діапазоном дат, з якими можна працювати.

Ось бібліотеки, які слід врахувати:

Бібліотеки, засновані на міжнародному рівні

Нова розробка повинна обрати одну з таких реалізацій, яка спирається на API Intl для даних часового поясу:

Неінтегральні бібліотеки

Ці бібліотеки підтримуються, але несуть тягар упаковки власних даних часового поясу, який може бути досить великим.

* У той час, як раніше рекомендувались Moment і Moment-Timezone, команда Moment тепер вважає за краще, щоб користувачі обрали Luxon для нової розробки.

Бібліотеки, що припиняються

Ці бібліотеки були офіційно припинені і більше не повинні використовуватися.

Майбутні пропозиції

У Пропозиція TC39 Тимчасові мети , щоб забезпечити новий набір стандартних об'єктів для роботи з датами і часом в самій мові JavaScript. Сюди входить підтримка об’єкта, знаючого часовий пояс.


1
Будь ласка, змініть "представляти" на "вихід / аналіз", оскільки представлені часові позначки не залежать від часового поясу
Бергі

1
@Bergi - Я переосмислив це і погоджуюся з тобою. Відповідно оновив мою відповідь.
Метт Джонсон-Пінт

1
Коли ви зробите це в консолі Firebug: var date_time = new Date()значення date_timeIS (для мене в AEST) Date {Sun Aug 21 2016 22:18:47 GMT+1000 (AEST)}і так тому здається , що це вже зберігається часовий пояс. Як я можу впевнитись date_time, що суто UTC, без включеного часового поясу?
користувач1063287

Хм, відповідно до цієї відповіді ( stackoverflow.com/a/8047885/1063287 ), це так: new Date().getTime();
user1063287

1
@ user1063287 - метод toString використовує зміщення часового поясу хоста для створення дати та часу у "локальному" часовому поясі. Сам об’єкт дати має значення часу, яке є зміщенням від 1970-01-01T00: 00: 00Z, тому ефективно UTC. Щоб побачити дату та час UTC, використовуйте toISOString .
RobG

69

Як сказав Метт Джонсон

Якщо ви можете обмежити своє використання сучасними веб-браузерами, тепер ви можете робити наступні дії без спеціальних бібліотек:

new Date().toLocaleString("en-US", {timeZone: "America/New_York"})

Це не є комплексним рішенням, але воно працює для багатьох сценаріїв, які вимагають лише перетворення результатів (з UTC або місцевого часу в певний часовий пояс, але не в іншому напрямку).

Отже, хоча браузер не може читати часові пояси IANA під час створення дати або має будь-які способи зміни часових поясів на існуючому об’єкті дати, навколо цього, здається, є злом:

function changeTimezone(date, ianatz) {

  // suppose the date is 12:00 UTC
  var invdate = new Date(date.toLocaleString('en-US', {
    timeZone: ianatz
  }));

  // then invdate will be 07:00 in Toronto
  // and the diff is 5 hours
  var diff = date.getTime() - invdate.getTime();

  // so 12:00 in Toronto is 17:00 UTC
  return new Date(date.getTime() + diff);

}

// E.g.
var there = new Date();
var here = changeTimezone(there, "America/Toronto");

console.log(`Here: ${here.toString()}\nToronto: ${there.toString()}`);


6
Мені цікаво, чому ця відповідь не отримує більше любові. Для моїх цілей це абсолютно найкраще рішення. У моєму додатку всі дати є UTC, але їх потрібно вводити або виводити в явних часових поясах IANA в інтерфейсі користувача. Для цього я використовую це рішення, і воно прекрасно працює. Простий, ефективний, стандартний.
logidelic

2
@logidelic - це досить нова публікація. якщо чесно, я вразив себе, коли це мене вразило, що ти міг би використати toLocaleStringдля досягнення зворотного ефекту, і так, це повинно бути загальновідомим! піди написати про це статтю і
згадай

1
Для середовища вузла, якого вже немає в GMT, вищевказаний код потрібно змінити з var invdate = new Date (date.toLocaleString ('en-US', {timeZone: ianatz})); в var invdate = нова дата ($ {date.toLocaleString ('en-US', {timeZone: ianatz})} GMT`);
Майк П.

5
Браузерам не потрібно розбирати висновки toLocaleString , тому він заснований на несправній умові .
RobG

3
"... чому ця відповідь не отримує більше любові?" - тому що Dateоб’єкт, який він повертає, - брехня. Навіть показаний приклад, рядок виводу включає часовий пояс локальної машини. На моєму комп’ютері в обох результатах написано "GMT-0700 (тихоокеанський літній час)", де це правильно лише для першого (Торонто насправді в східний час.) Окрім лише вихідного рядка, часова мітка, проведена в межах Dateоб'єкта, була зміщені в інший момент часу. Це може бути корисною технікою, але вона має багато застережень - насамперед, що Dateфункції об'єкта не матимуть нормального результату.
Метт Джонсон-Пінт

30

Ви можете вказати зміщення часового поясу new Date(), наприклад:

new Date('Feb 28 2013 19:00:00 EST')

або

new Date('Feb 28 2013 19:00:00 GMT-0500')

Оскільки Dateзберігається UTC-час (тобто getTimeповертається в UTC), javascript буде перетворювати час у UTC, а коли ви викликаєте такі речі, як toStringJavaScript, перетворює час UTC у локальний часовий пояс браузера та повертає рядок у локальний часовий пояс, тобто якщо я використовую UTC+8:

> new Date('Feb 28 2013 19:00:00 GMT-0500').toString()
< "Fri Mar 01 2013 08:00:00 GMT+0800 (CST)"

Також ви можете використовувати звичайний getHours/Minute/Secondметод:

> new Date('Feb 28 2013 19:00:00 GMT-0500').getHours()
< 8

(Це 8означає, що час перетворюється на мій місцевий час - UTC+8число годин 8.)


16
Розбір будь-якого формату, крім розширеного формату ISO 8601, залежить від реалізації, і на нього не слід покладатися. Не існує стандартних скорочень часових поясів, наприклад, "EST" може представляти будь-яку з 3 різних зон.
RobG

1
Приклади цього коментаря часто не працюють в Internet Explorer. Як було сказано в попередньому коментарі до цього повідомлення, ISO 8601 є важливим. Я підтвердив, прочитавши специфікацію мови видання ECMA-262 (Javascript 5th).
Дакусан

12

Це має вирішити вашу проблему. Не соромтеся запропонувати виправлення. Цей метод враховуватиме також літній час для даної дати.

dateWithTimeZone = (timeZone, year, month, day, hour, minute, second) => {
  let date = new Date(Date.UTC(year, month, day, hour, minute, second));

  let utcDate = new Date(date.toLocaleString('en-US', { timeZone: "UTC" }));
  let tzDate = new Date(date.toLocaleString('en-US', { timeZone: timeZone }));
  let offset = utcDate.getTime() - tzDate.getTime();

  date.setTime( date.getTime() + offset );

  return date;
};

Як користуватися часовим поясом та місцевим часом:

dateWithTimeZone("America/Los_Angeles",2019,8,8,0,0,0)

Це рішення підходило для моєї потреби. Я не знав, як саме я можу пояснити літній час.
Амін

Чому б просто не повернутися tzDateбезпосередньо?
levi

8

Я знайшов найбільш підтримуваний спосіб зробити це, не турбуючись про сторонні бібліотеки, за допомогою getTimezoneOffsetобчислення відповідної мітки часу або оновлення часу, а потім використовувати звичайні методи, щоб отримати необхідну дату та час.

var mydate = new Date();
mydate.setFullYear(2013);
mydate.setMonth(02);
mydate.setDate(28);
mydate.setHours(7);
mydate.setMinutes(00);

// ET timezone offset in hours.
var timezone = -5;
// Timezone offset in minutes + the desired offset in minutes, converted to ms.
// This offset should be the same for ALL date calculations, so you should only need to calculate it once.
var offset = (mydate.getTimezoneOffset() + (timezone * 60)) * 60 * 1000;

// Use the timestamp and offset as necessary to calculate min/sec etc, i.e. for countdowns.
var timestamp = mydate.getTime() + offset,
    seconds = Math.floor(timestamp / 1000) % 60,
    minutes = Math.floor(timestamp / 1000 / 60) % 60,
    hours   = Math.floor(timestamp / 1000 / 60 / 60);

// Or update the timestamp to reflect the timezone offset.
mydate.setTime(mydate.getTime() + offset);
// Then Output dates and times using the normal methods.
var date = mydate.getDate(),
    hour = mydate.getHours();

EDIT

Раніше я використовував UTCметоди, коли виконував перетворення дати, що було невірно. Додавши зміщення до часу, використання локальних getфункцій поверне бажані результати.


Де ви порахували timezone_offset?
Ананд Сомані

1
На жаль, я неправильно позначив одну зі своїх змінних. timezone_offsetповинні були бути offsetабо візи навпаки. Я відредагував свою відповідь, щоб показати правильне ім’я змінної.
Shaun Cockerill

3

Я зіткнувся з подібною проблемою з одиничними тестами (зокрема, в жарті, коли одиничні тести виконуються локально для створення знімків, а потім сервер CI працює (можливо, в іншому часовому поясі), що призводить до невдалого порівняння знімків. Я знущався над нашими Dateта деякими підтримуючими методами, як-от так:

describe('...', () => {
  let originalDate;

  beforeEach(() => {
    originalDate = Date;
    Date = jest.fn(
      (d) => {
        let newD;
        if (d) {
          newD = (new originalDate(d));
        } else {
          newD = (new originalDate('2017-05-29T10:00:00z'));
        }
        newD.toLocaleString = () => {
          return (new originalDate(newD.valueOf())).toLocaleString("en-US", {timeZone: "America/New_York"});
        };
        newD.toLocaleDateString = () => {
          return (new originalDate(newD.valueOf())).toLocaleDateString("en-US", {timeZone: "America/New_York"});
        };
        newD.toLocaleTimeString = () => {
          return (new originalDate(newD.valueOf())).toLocaleTimeString("en-US", {timeZone: "America/New_York"});
        };
        return newD;
      }
    );
    Date.now = () => { return (Date()); };
  });

  afterEach(() => {
    Date = originalDate;
  });

});

2

Спробуйте використовувати ctoc з npm. https://www.npmjs.com/package/ctoc_timezone

У нього є проста функціональність для зміни часових поясів (більшість часових поясів близько 400), і всі користувацькі формати ви хочете, щоб вони відображалися.


2

Спираючись на наведені вище відповіді, я використовую цей нативний вкладиш для перетворення рядка довгого часового поясу в трибуквену лінійку:

var longTz = 'America/Los_Angeles';
var shortTz = new Date().
    toLocaleString("en", {timeZoneName: "short", timeZone: longTz}).
    split(' ').
    pop();

Це дасть PDT або PST залежно від вказаної дати. У моєму конкретному випадку використання, розвиваючись на Salesforce (Aura / Lightning), ми можемо отримати часовий пояс користувача у довгому форматі з бекенда.


Тільки тому, що час від часу мені подобається грати в педант, "America / Los_Angeles" не є часовим поясом, це "репрезентативне місце розташування" для певних правил зсуву та літнього часу та історії цих змін (за базою даних часового поясу IANA ) . ;-)
RobG

Ха-ха, ви повинні опублікувати цей коментар на кілька відповідей тут: P
Шейн

2

Я знаю, що його 3 роки занадто пізно, але, можливо, це може допомогти комусь іншому, тому що я не знайшов нічого подібного, крім бібліотеки моменту часового поясу, яка не є точно такою, що він тут просить.

Я зробив щось подібне для німецького часового поясу, це трохи складне завдання, оскільки літній час і високосні роки, де у вас 366 днів.

можливо, знадобиться трохи попрацювати з функцією "isDaylightSavingTimeInGermany", тоді як різні часові пояси змінюються в різний час літнього часу.

все одно, ознайомтеся з цією сторінкою: https://github.com/zerkotin/german-timezone-converter/wiki

Основними методами є: convertLocalDateToGermanTimezone convertGermanDateToLocalTimezone

Я доклав зусиль, щоб документувати це, так що це не буде так заплутано.


Це має бути коментар, це не відповідь.
RobG

1

Спробуйте: дата-від-часового поясу , вона вирішує очікувану дату за допомогою наявних Intl.DateTimeFormat.

Я використовував цей метод в одному зі своїх проектів уже кілька років, але зараз я вирішив опублікувати його як невеликий проект ОС :)


0

Для користувачів Ionic у мене було пекло, тому .toISOString()що його потрібно використовувати з HTML-шаблоном.

Це захопить поточну дату, але, звичайно, можна додати до попередніх відповідей на вибрану дату.

Я вирішив це за допомогою цього:

date = new Date();
public currentDate: any = new Date(this.date.getTime() - this.date.getTimezoneOffset()*60000).toISOString();

* 60000 вказує на UTC -6, який є CST, так що незалежно від TimeZone, кількість та різницю можна змінити.


Це не відповідь, яку запитуючий шукає. Він шукав, щоб дати дати до конкретного часового поясу.
Річард Вергіс

@RichardVergis У цьому питанні чітко зазначено, що користувач хоче змінити місцевий час, але не вказує, у якому часовому поясі він перебуває. Я вказав свій часовий пояс як приклад, і користувач може чітко читати, виходячи з мого прикладу, вони можуть ввести їхній часовий пояс зміщений.
Стівен Ромеро

0

Можливо, це вам допоможе

/**
 * Shift any Date timezone.
 * @param {Date} date - Date to update.
 * @param {string} timezone - Timezone as `-03:00`.
 */
function timezoneShifter(date, timezone) {
  let isBehindGTM = false;
  if (timezone.startsWith("-")) {
    timezone = timezone.substr(1);
    isBehindGTM = true;
  }

  const [hDiff, mDiff] = timezone.split(":").map((t) => parseInt(t));
  const diff = hDiff * 60 + mDiff * (isBehindGTM ? 1 : -1);
  const currentDiff = new Date().getTimezoneOffset();

  return new Date(date.valueOf() + (currentDiff - diff) * 60 * 1000);
}



const _now = new Date()
console.log(
  [
    "Here: " + _now.toLocaleString(),
    "Greenwich: " + timezoneShifter(_now, "00:00").toLocaleString(),
    "New York: " + timezoneShifter(_now, "-04:00").toLocaleString(),
    "Tokyo: " + timezoneShifter(_now, "+09:00").toLocaleString(),
    "Buenos Aires: " + timezoneShifter(_now, "-03:00").toLocaleString(),
  ].join('\n')
);


-2

Зіткнувся з тим же питанням, використовував це

Console.log (Date.parse ("13 червня 2018 10:50:39 GMT + 1"));

Він поверне мілісекунди, на які ти можеш перевірити, чи є +100 часова зона, щоб активізувати британський час Сподіваюся, що це допоможе !!


3
(Схоже, ця публікація не дає якісної відповіді на питання. Будь ласка, відредагуйте свою відповідь або просто опублікуйте її як коментар до питання).
sɐunıɔ ןɐ qɐp
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.