Я работаю над облачным приложением, которое широко работает со значениями даты и времени для пользователей по всему миру.
Рассмотрим сценарий на JavaScript, где моя машина находится в Индии (GMT + 05: 30), и мне нужно отображать часы, работающие в часовом поясе Калифорнии (GMT-08: 00).
В этом случае мне нужно получить новый объект даты,
let india_date = new Date()
добавить значение смещения часового пояса,
let uts_ms = india_date.getTime() + india_date.getTimezoneOffset()
добавить значение смещения часового пояса Калифорнии,
let california_ms = utc_ms + getCaliforniaTimezoneOffsetMS()
и, наконец, объект даты.
let california_date: Date = new Date(california_ms)
Есть ли способ напрямую работать с такими часовыми поясами без необходимости снова и снова преобразовывать значения?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Что ж, вам действительно нужно конвертировать каждый раз, когда вы хотите изменить отображение, но это не так плохо, как вы думаете.
Во-первых, сохраните все время как UTC. Вероятно, используя формат миллисекунд, например Date.UTC().
Во-вторых, делайте все манипуляции / сравнения, используя эту сохраненную информацию.
В-третьих, если ваше облачное приложение имеет API, этот API также должен говорить только в терминах UTC, хотя вы можете предоставить строку ISO, если вы предпочитаете это MS, или если вы ожидаете, что клиенты справятся с этим лучше.
В-четвертых, и наконец, только в пользовательском интерфейсе вы должны выполнять окончательное преобразование в локальную строку даты / времени либо с помощью метода, который вы описываете, либо с использованием библиотеки, такой как momentjs
Объекты даты JavaScript сохраняет дату и время в формате UTC, но метод toString() вызывается автоматически, когда дата представлена в виде текстового значения, которое отображает дату и время в локальном часовом поясе браузера. Итак, когда вы хотите преобразовать datetime в часовой пояс, отличный от вашего местного времени, вы действительно конвертируете из UTC в этот часовой пояс (а не из вашего местного часового пояса в другой часовой пояс).
Если ваш вариант использования ограничен определенными браузерами и вы можете гибко форматировать (поскольку браузеры могут отличаться по способу отображения форматов строк даты), вы можете использовать toLocaleString(), но такие браузеры, как Edge, Android webview и т. д., Не полностью поддерживают параметры locales и options.
В следующем примере локаль и часовой пояс устанавливаются для вывода даты в локальном формате, который может отличаться от браузера к браузеру.
const dt = new Date();
const kolkata = dt.toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' });
const la = dt.toLocaleString('en-US', { timeZone: 'America/Los_Angeles' });
console.info('Kolkata:', kolkata);
// example output: Kolkata: 19/3/2019, 7:36:26 pm
console.info('Los Angeles:', la);
// example output: Los Angeles: 3/19/2019, 7:06:26 AMВы также можете использовать Moment.js и Часовой пояс момента для преобразования даты и времени в часовой пояс, отличный от вашего местного часового пояса. Например:
const dt = moment();
const kolkata = dt.tz('Asia/Kolkata').format();
const la = dt.tz('America/Los_Angeles').format();
console.info(kolkata);
// example output: 2019-03-19T19:37:11+05:30
console.info(la);
// example output: 2019-03-19T07:07:11-07:00<script src = "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.23/moment-timezone-with-data.min.js"></script>Во-первых, давайте поговорим о коде вашего вопроса.
let india_date = new Date()
Вы назвали эту переменную india_date, но объект Date будет отражать Индию только в том случае, если код запускается на компьютере, установленном на часовой пояс Индии. Если он запущен на компьютере с другим часовым поясом, вместо этого он будет отражать этот часовой пояс. Имейте в виду, что внутри объект Date отслеживает только временную метку на основе UTC. Местный часовой пояс применяется при вызове функций и свойств, которым требуется местное время, а не при создании объекта Date.
add it's timezone offset value
let uts_ms = india_date.getTime() + india_date.getTimezoneOffset()
Такой подход неверен. getTime() уже возвращает метку времени на основе UTC. Вам не нужно добавлять местное смещение. (также используется аббревиатура UTC, а не UTS.)
Now add california's timezone offset value
let california_ms = utc_ms + getCaliforniaTimezoneOffsetMS()
Опять же, добавление смещения неверно. Кроме того, в отличие от Индии, в Калифорнии применяется летнее время, поэтому часть года смещение будет 480 (UTC-8), а часть года - 420 (UTC-7). Любая функция, такая как ваш getCaliforniatimezoneOffsetMS, должна иметь временную метку, переданную в качестве параметра, чтобы быть эффективной.
and finally the date object
let california_date: Date = new Date(california_ms)
Когда конструктору Date передается числовая метка времени, она должна быть в формате UTC. Передавая ему эту временную метку california_ms, вы просто выбираете другой момент времени. Вы не можете изменить поведение объекта Date, чтобы заставить его использовать другой часовой пояс, просто добавляя или вычитая смещение. Он по-прежнему будет использовать местный часовой пояс, в котором он работает, для любой функции, которая требует местного времени, например .toString() и других.
Есть только один сценарий, в котором такая корректировка имеет смысл, - это метод, известный как «смещение эпох». Отметка времени настраивается для смещения базовой эпохи от обычного 1970-01-01T00:00:00Z, что позволяет воспользоваться функциями UTC объекта Date (такими как getUTCHours и другие). Загвоздка в том, что после сдвига вы никогда не сможете использовать какие-либо функции местного времени для этого объекта Date или передать их чему-либо еще, ожидающему, что объект Date будет нормальным. Правильное смещение эпох - вот что делает библиотеки, такие как Moment.js, движущей силой. Здесь - еще один пример правильного сдвига эпох.
Но в вашем примере вы выполняете сдвиг (дважды по ошибке), а затем используете объект Date, как если бы он был нормальным и не сдвигался. Это может привести только к ошибкам, очевидным по часовому поясу, показанному в выходных данных toString, и возникнет математически вблизи любых переходов на летнее время местного часового пояса и намеченного целевого часового пояса. В общем, вы не хотите использовать этот подход.
Вместо этого прочтите мой ответ на Как инициализировать дату JavaScript для определенного часового пояса. Ваши варианты перечислены там. Спасибо.
новая дата создает объект Date со значением времени в формате UTC. Если вы можете гарантировать поддержку параметра часовой пояс для toLocaleString (например, корпоративная среда с управляемым SOE), вы можете использовать его для создания метки времени в любом часовом поясе и любом формате, но это может быть немного утомительно. Поддержка в Интернете может отсутствовать. Библиотека будет предпочтительнее в этом случае, если вам нужно, чтобы она работала надежно.
Например. чтобы получить значения для Калифорнии, вы можете использовать toLocaleString и "America / Los_Angeles" для параметра часовой пояс:
var d = new Date();
// Use the default implementation format
console.info(d.toLocaleString(undefined, {timeZone:'America/Los_Angeles'}));
// Customised format
var weekday = d.toLocaleString(undefined, {weekday:'long', timeZone:'America/Los_Angeles'});
var day = d.toLocaleString(undefined, {day:'numeric', timeZone:'America/Los_Angeles'});
var month = d.toLocaleString(undefined, {month:'long', timeZone:'America/Los_Angeles'});
var year = d.toLocaleString(undefined, {year:'numeric', timeZone:'America/Los_Angeles'});
var hour = d.toLocaleString(undefined, {hour:'numeric',hour12: false, timeZone:'America/Los_Angeles'});
var minute = d.toLocaleString(undefined, {minute:'2-digit', timeZone:'America/Los_Angeles'});
var ap = hour > 11? 'pm' : 'am';
hour = ('0' + (hour % 12 || 12)).slice(-2);
console.info(`The time in Los Angeles is ${hour}:${minute} ${ap} on ${weekday}, ${day} ${month}, ${year}`);Получить название часового пояса немного сложнее, без другой информации получить его сложно.
Вам понадобится momentjs.com/timezone.