преобразование часового пояса с использованием date-fns

Я пытаюсь работать с date-fns-tz на своей веб-странице, основанной на реакции, и не смог заставить работать следующий вариант использования.

У меня есть ввод даты в форме, которая должна быть отправлена ​​​​на серверную часть, в которой хранятся данные в местном часовом поясе.

Пользователь в часовом поясе GMT+2 выбирает 14:00 1 февраля 2021 года в пользовательском интерфейсе, что соответствует отметке времени 1612180800 (как Пользовательский интерфейс был открыт в GMT+2), но в конечном итоге он должен быть отправлен на сервер как 14:00 в GMT-8, что на самом деле является отметкой времени 1612216800.

Как правильно заставить это преобразование (из 1612180800 --› 1612216800 ) работать?

Пробовал работать с разными функциями date-fns, но подходящей не нашел.


person Or Dotan    schedule 03.02.2021    source источник


Ответы (2)


Для корректной работы вам понадобятся две вещи:

  • Идентификатор часового пояса IANA для предполагаемого целевого часового пояса, например 'America/Los_Angeles', а не просто смещение от UTC.

  • Библиотека, поддерживающая ввод данных в определенном часовом поясе.

    • Since you asked about date-fns, you should consider using the date-fns-tz add-on library.
    • В качестве альтернативы вы можете использовать для этого Luxon.
    • Раньше я мог рекомендовать Moment с Moment-TimeZone, но вам следует просмотреть статус проекта Moment. страницу перед выбором этой опции.

Придерживаясь date-fns и date-fns-tz, вариант использования, который вы указали, описан в документации для zonedTimeToUtc, которую я скопирую сюда:

Скажем, пользователя просят ввести дату/время и часовой пояс события. Средство выбора даты/времени обычно возвращает экземпляр Date с выбранной датой в местном часовом поясе пользователя, а ввод select может предоставить фактическое имя часового пояса IANA.

Для эффективной работы с этой информацией необходимо найти эквивалент времени UTC:

import { zonedTimeToUtc } from 'date-fns-tz'

const date = getDatePickerValue() // e.g. 2014-06-25 10:00:00 (picked in any time zone)
const timeZone = getTimeZoneValue() // e.g. America/Los_Angeles

const utcDate = zonedTimeToUtc(date, timeZone) // In June 10am in Los Angeles is 5pm UTC

postToServer(utcDate.toISOString(), timeZone) // post 2014-06-25T17:00:00.000Z, America/Los_Angeles

В вашем случае единственное изменение состоит в том, что в самом конце вместо вызова utcDate.toISOString() вы будете вызывать utcDate.getTime().

Обратите внимание, что вам все равно нужно делить на 1000, если вы собираетесь передавать метки времени в секундах, а не в миллисекундах, предлагаемых объектом Date.

person Matt Johnson-Pint    schedule 03.02.2021
comment
Спасибо за подробный ответ! Я попробовал ваше предложение: zonedTimeToUtc(1612180800, 'America/Los_Angeles').getTime(), но оно просто привело меня к тому, с чего я начал - 1612180800, и мне нужно добраться до 1612216800 в соответствии с описанным мной вариантом использования. - person Or Dotan; 04.02.2021
comment
Хорошо, я думаю, что у меня это есть. Я использовал ваше решение, и вместо ввода явной метки времени (1612180800) я использовал дату в виде строки - 2021-02-01 14:00, и, похоже, это работает - person Or Dotan; 04.02.2021
comment
Я считаю, что это дизайн. Вы также можете передать объект Date, и он сделает эту часть за вас (см. исходный код здесь). Поскольку многие средства выбора даты возвращают Date объектов, это, вероятно, ваш лучший выбор. - person Matt Johnson-Pint; 04.02.2021
comment
Имейте в виду, что это все еще очень обходной путь, и у него могут быть крайние случаи, которые не работают. Например, если пользователь выбирает время, действительное в целевом часовом поясе, но попадает в промежуток, созданный переходом вперед в местном часовом поясе (например, с началом летнего времени), тогда результирующее время будет смещено на час после преобразования. - person Matt Johnson-Pint; 04.02.2021
comment
На самом деле я не использую средство выбора даты. Я использую текстовое поле с типом = время и когда-нибудь присоединяюсь к нему, чтобы построить свою полную дату. Есть ли решение для пограничного случая, о котором вы упомянули о переходе на летнее время? - person Or Dotan; 06.02.2021

Вы можете использовать «момент» для преобразования часового пояса.

1. Создайте момент с датой и временем, указав, что это выражается как utc, с moment.utc()

2. Преобразуйте его в свой часовой пояс с помощью moment.tz()

Например

moment.utc(t, 'YYYY-MM-DD HH:mm:ss')
    .tz("America/Chicago")
    .format('l');
person Frozen Heart    schedule 03.02.2021
comment
Это не совсем сработало. Вот что я пробовал: moment.tz(moment.utc(moment(1612180800*1000)),'America/Los_Angeles').format('MM-DD hh:mm'), и он действительно преобразовал 14:00 (GMT+2) в 04:00 (GMT-8), но мне нужна отметка времени для 14:00 в GMT-8, то есть 1612216800. - person Or Dotan; 03.02.2021
comment
Привет. См. страницу статуса проекта Moment. В StackOverflow, пожалуйста, воздержитесь от рекомендации Moment в качестве решения, если Moment специально не запрашивается в вопросе. Спасибо. - person Matt Johnson-Pint; 03.02.2021