В чём состоит проблема
Из всех последних изменений, которые будут внедрены в ECMAScript, моим любимым с большим отрывом от остальных стало предложение Temporal. Это предложение очень прогрессивное, мы уже можем воспользоваться этим API при помощи полифила, разработанного командой FullCalendar.
Этот API настолько невероятен, что я, наверно, посвящу несколько постов описанию его основных возможностей. В первом из них я расскажу об одном из его главных преимуществ: у нас наконец появился нативный объект, описывающий Zoned Date Time.
Но что же такое Zoned Date Time?
Человеческие даты и даты JS
Когда мы говорим о человеческих датах, то обычно произносим что-то типа «У меня назначено посещение врача на 4 августа 2024 года в 10:30», но не упоминаем часовой пояс. Это логично, ведь чаще всего наш собеседник знает нас и понимает, что когда я говорю о датах, то имею в виду контекст своего часового пояса (Европы/Мадрида).
К сожалению, в случае с компьютерами это не так. Когда мы работаем с объектами Date в JavaScript, мы имеем дело с обычными числами.
В официальной спецификации говорится следующее:
«Значение времени ECMAScript — это число; или конечное целое число, описывающее момент времени с точностью до миллисекунд, или NaN, описывающее отсутствие конкретного момента»
Кроме того, что даты в JavaScript представлены не в UTC, а в POSIX (это ОЧЕНЬ ВАЖНО), где полностью игнорируются секунды координации, проблема с описанием времени в виде числа заключается в потере исходной семантики данных. То есть имея человеческую дату, мы можем получить эквивалентную дату JS, но не наоборот.
Рассмотрим пример: допустим, мне нужно зафиксировать момент осуществления платежа с моей карты. У многих разработчиков возникает искушение написать что-то вроде этого:
const paymentDate = new Date('2024-07-20T10:30:00');
Так как мой браузер находится в часовом поясе CET
, когда я записываю это, браузер просто «вычисляет количество миллисекунд с начала EPOX для этого момента CET».
Вот, что мы на самом деле сохраняем в дату:
paymentDate.getTime();
// 1721464200000
То есть в зависимости от того, как мы прочитаем эту информацию, мы получим разные «человеческие даты»:
Если считать их с точки зрения CET, то мы получим 10:30:
d.toLocaleString()
// '20/07/2024, 10:30:00'
а если считать с точки зрения ISO, то 8:30:
d.toISOString()
// '2024-07-20T08:30:00.000Z'
Многие считают, что работая с UTC или передавая данные в формате ISO, они обеспечивают безопасность, однако это не так, информация всё равно теряется.
Формата UTC недостаточно
Даже при работе с датами в формате ISO с учётом смещения, когда в следующий раз мы захотим отобразить дату, мы будем знать только количество миллисекунд, прошедших с эпохи UNIX, и смещение. Но этого всё равно недостаточно, чтобы знать «человеческий» момент и часовой пояс выполнения платежа.
Строго говоря, имея метку времени t0
, мы можем получить n
описывающих её человекочитаемых дат...
Иными словами, функция, отвечающая за преобразование метки времени в человекочитаемую дату, не инъективна, так как каждый элемент во множестве меток времени соответствует более чем одному элементу во множестве «человеческих дат».
Ровно то же самое происходит при сохранении дат в ISO, так как метки времени и ISO — это два описания одного момента:
Это происходит и при работе со смещениями, потому что разные часовые пояса могут иметь одинаковое смещение.
Если вы всё ещё не до конца понимаете проблему, то позвольте мне проиллюстрировать её примером. Представим, что вы живёте в Мадриде и отправились в Сидней.
Несколько недель спустя вы возвращаетесь в Мадрид и видите странное списание, которое не можете вспомнить... с меня взяли 3,50 в 2 часа ночи 16 числа? Чем я занимался? Той ночью я рано лёг!.. Не понимаю.
Немного поволновавшись, вы понимаете, что это оплата кофе, выпитого вами на следующее утро, поскольку прочитав статью, вы уже осознаёте, что ваш банк хранит все транзакции в UTC, а приложение преобразует их в часовой пояс телефона.
Это может оказаться невинной историей, но что, если ваш банк позволяет бесплатно снимать наличные один раз в день? Когда начинается и завершается день? ПО UTC? По Австралии?... Всё становится сложнее, поверьте мне...
Надеюсь, теперь вы уже поняли, что работа исключительно с метками времени представляет собой проблему; к счастью, у неё есть решение.
ZonedDateTime
Кроме всего прочего, в новом Temporal API внедряется концепция объекта Temporal.ZonedDateTime, специально предназначенного для описания дат и времени в соответствующем часовом поясе. Разработчики также предложили расширение RFC 3339 для стандартизации сериализации и десериализации строк, описывающих данные:
Вот пример:
1996-12-19T16:39:57-08:00[America/Los_Angeles]
Эта строка описывает 39 минут и 57 секунд после 16-го часа 19 декабря 1996 года со смещением -08:00 от UTC и дополнительно определяет связанный с датой часовой пояс («Pacific Time»), чтобы его могли использовать приложения, учитывающие часовой пояс.
Кроме того, этот API позволяет работать с различными календарями, и в том числе:
буддистским
китайским
коптским
корейским
эфиопским
григорианским
еврейским
индийским
исламским
исламским-umalqura
исламским-tbla
исламским-civil
исламским-rgsa
японским
персидским
календарём Миньго
Среди них всех самым популярным будет iso8601
(стандартная адаптация григорианского календаря), с которым вы будете работать чаще всего.
Основные операции
Создание дат
Temporal API даёт большое преимущество при создании дат, особенно при помощи объекта Temporal.ZonedDateTime. Одна из его выдающихся особенностей — возможность беспроблемной работы с часовыми поясами, в том числе со сложными ситуациями, касающимися летнего времени (Daylight Saving Time, DST). Например, при создании объекта Temporal.ZonedDateTime следующим образом:
const zonedDateTime = Temporal.ZonedDateTime.from({
year: 2024,
month: 8,
day: 16,
hour: 12,
minute: 30,
second: 0,
timeZone: 'Europe/Madrid'
});
вы не не просто задаёте дату и время; вы обеспечиваете точное описание даты в указанном часовом поясе. Благодаря такой точности вне зависимости от изменений DST и любых других изменений локального времени ваша дата всегда будет отражать корректный момент во времени.
Эта функция особенно полезна при планировании событий или логировании действий, согласованных между несколькими регионами. Встроив часовой пояс непосредственного в процесс создания даты, Temporal устраняет часто возникающие проблемы традиционных объектов Date, например неожиданные сдвиги времени из-за DST или разницы в часовых поясах. Поэтому Temporal — это не просто способ облегчить себе жизнь, а необходимость в современной веб-разработке, где критически важна глобальная согласованность времени.
Если вам любопытно, чем же так хорош этот API, прочитайте статью с объяснением того, как работать с изменениями в определениях часовых поясов.
Сравнение дат
У ZonedDateTime есть статический метод compare
, который получает два ZonedDateTime и возвращает:
−1
, если первое меньше второго;0
, если оба описывают ровно один и тот же момент без учёта часового пояса и календаря;1
, если первое больше второго.
Можно легко сравнивать даты в необычных случаях, например, при повторе часа после завершения DST более поздние значения могут быть в часовом времени раньше, и наоборот:
const one = Temporal.ZonedDateTime.from('2020-11-01T01:45-07:00[America/Los_Angeles]');
const two = Temporal.ZonedDateTime.from('2020-11-01T01:15-08:00[America/Los_Angeles]');
Temporal.ZonedDateTime.compare(one, two);
// => -1
// (потому что `one` в реальном мире происходит раньше)
Отличные встроенные возможности
У ZonedDateTime есть заранее вычисленные атрибуты, упрощающие вам жизнь, например:
hoursInDay
Свойство только для чтения hoursInDay возвращает количество реальных часов между началом текущего дня (обычно полуночью) в zonedDateTime.timeZone до начала следующего календарного дня в том же часовом поясе.
Temporal.ZonedDateTime.from('2020-01-01T12:00-08:00[America/Los_Angeles]').hoursInDay;
// => 24
// (обычныый день)
Temporal.ZonedDateTime.from('2020-03-08T12:00-07:00[America/Los_Angeles]').hoursInDay;
// => 23
// (в этот день начинается DST)
Temporal.ZonedDateTime.from('2020-11-01T12:00-08:00[America/Los_Angeles]').hoursInDay;
// => 25
// (в этот день завершается DST)
Также у ZonedDateTime есть отличные атрибуты daysInYear, inLeapYear
Преобразование часовых поясов
У ZonedDateTimes есть метод .withTimeZone
, позволяющий по необходимости менять ZonedDateTime:
zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24:30+09:00[Asia/Tokyo]');
zdt.toString(); // => '1995-12-07T03:24:30+09:00[Asia/Tokyo]'
zdt.withTimeZone('Africa/Accra').toString(); // => '1995-12-06T18:24:30+00:00[Africa/Accra]'
Арифметика
Можно использовать метод .add
для прибавления части даты временного интервала при помощи календарной арифметики. Результат автоматически учитывает Daylight Saving Time на основе правил поля timeZone этого экземпляра.
Замечательно в этом то, что поддерживается возможность выполнять арифметические действия как с календарной арифметикой, так и простыми длительностями.
Прибавление или вычитание дней должно согласовывать часовое время при переходах DST. Например, если у вас назначена встреча в субботу в 13:00, и вы хотите перенести её на один день вперёд, то будете ожидать, что встреча снова будет назначена на 13:00, даже если ночью произошёл переход на летнее время.
Прибавление или вычитание части времени длительности должно игнорировать переходы DST. Например, если вы договорились с другом встретиться через два часа, то он расстроится, если вы придёте через час или три часа.
Должен существовать согласованный и достаточно ожидаемый порядок операций. Если результаты попадают на переход DST или рядом с ним, то неопределённость должна устраняться автоматически (без сбоев) и детерминированно.
zdt = Temporal.ZonedDateTime.from('2020-03-08T00:00-08:00[America/Los_Angeles]');
// Прибавляем день, чтобы получить полночь в день после дня начала DST
laterDay = zdt.add({ days: 1 });
// => 2020-03-09T00:00:00-07:00[America/Los_Angeles]
// Обратите внимание, что новое смещение отличается, это показывает, что результат учитывает DST.
laterDay.since(zdt, { largestUnit: 'hour' }).hours;
// => 23
// потому что один час потерялся из-за DST
laterHours = zdt.add({ hours: 24 });
// => 2020-03-09T01:00:00-07:00[America/Los_Angeles]
// Прибавление единиц времени не учитывает DST. Результат равен 1:00: спустя 24 часа
// реального времени, потому что один час был пропущен из-за DST.
laterHours.since(zdt, { largestUnit: 'hour' }).hours; // => 24
Вычисление разностей между датами
У Temporal есть метод .until
, который вычисляет разность между двумя моментами времени, представленными в zonedDateTime, опционально округляет её и возвращает в виде объекта Temporal.Duration. Если второе время было раньше, чем zonedDateTime, то получившаяся длительность будет отрицательной. Если использовать опции по умолчанию, то при сложении возвращаемого Temporal.Duration с zonedDateTime получится второе значение.
Это может показаться тривиальной операцией, но я советую прочитать полную спецификацию, чтобы понять её нюансы.
Заключение
Temporal API — это революционное изменение в обработке времени в JavaScript, благодаря чему он становится одним из немногих языков, где эта проблема решена исчерпывающе. В этой статье я рассмотрел тему лишь поверхностно, рассказав о разнице между человекочитаемыми датами (или временем на часах) и датами UTC, а также о том, как объект Temporal.ZonedDateTime можно использовать для точного описания первого.
В будущих статьях мы рассмотрим другие замечательные объекты, например Instant, PlainDate и Duration.
Комментарии (52)
plFlok
25.08.2024 11:39+7Буквально на днях завирусилась пара постов на тему WTF с датами в js, где как раз возникал вопрос, какого фига это до сих пор в таком виде существует
Сами посты
ImagineTables
25.08.2024 11:39+1А что не нравится Кристине? Оба конструктора, из числа и строки, работают вполне ожидаемо. Ну, может быть, стоило бы сделать конструктор из строки построже, чтобы год требовал хотя бы двух разрядов, т.е. ведущего нуля, а голый год в дате — всех четырёх.
Настоящая проблема в том, что в ES до сих пор нет режима запрета неявных типопреобразований (и можно нечаянно передать '0' вместо 0).
Как паллиатив, я бы сделал фабрику дат с кучей статических методов
.fromWhat()
.
CitizenOfDreams
25.08.2024 11:39+1как раз возникал вопрос, какого фига это до сих пор в таком виде существует
Потому что программисты занимались более важными вещами - например, совместимостью с двумя китайскими и пятью исламскими календарями.
Olegun
25.08.2024 11:39+2Можно было бы расширить Zoned Date Time. И сразу прилепить GPS координаты оставив еще поле под расширение для полетов за кофе на Марс.
ilyamodder
25.08.2024 11:39+11Многие считают, что работая с UTC или передавая данные в формате ISO, они обеспечивают безопасность, однако это не так, информация всё равно теряется.
Не очень понял аргументацию. Как она может теряться, если дата-время в UTC гарантированно обозначают ровно одну точку во времени? Про ISO-формат согласен, но в чем проблема хранить в UTC, а потом преобразовывать в таймзону клиента при отображении?
inkelyad
25.08.2024 11:39+4Судя по всему, проблема в том, что Date - страшно неправильное название. Должно быть Instant. Стандартная ошибка всех старых языков.
И тип использовали для того, для чего не надо бы (ввиду отсутствия альтернативы). Дата, которая выбирается в каком-нибудь виджете календаря или пишется на документе - это на самом деле интервал между двумя точками во времени. И когда ты вместо этого интервала получаешь Date (с установленными в 0 часом/минутой) - то ты, действительно, теряешь информацию - понять, какие именно точки были началом и концами этого интервала, нельзя.
ImagineTables
25.08.2024 11:39Дата, которая выбирается в каком-нибудь виджете календаря или пишется на документе - это на самом деле интервал между двумя точками во времени.
This is ground control to major Tom… )))))
Крайне сомневаюсь, что такая концепция даты зашла бы массовому программеру.
inkelyad
25.08.2024 11:39+1Крайне сомневаюсь, что такая концепция даты зашла бы массовому программеру.
Тем не менее, 'дата' именно это и означает, если подумать и если работаешь над/в системе, размазанной по таймзонам. И потом человек, сидящий в Москве, хочет посмотреть список событий за 'вчера', произошедших в Владивостоке. ("За чье 'вчера'? И когда оно началось?")
А так да, не очень заходит. Скажем в xml/xsd это сформулировали практически правильно. (потому что его придумывали как средство переноса данных между системами):
[Definition:] date represents top-open intervals of exactly one day in length on the timelines of dateTime, beginning on the beginning moment of each day, up to but not including the beginning moment of the next day). For non-timezoned values, the top-open intervals disjointly cover the non-timezoned timeline, one per day. For timezoned values, the intervals begin at every minute and therefore overlap.
Но когда всякие парсеры XML эти даты читают - ооочень редко когда оно превращается именно в интервал.
ImagineTables
25.08.2024 11:39Я думаю, что для большинства дата это Instant, округлённый до дней. А если рассматривать его как интервал, придётся опять от округлённости переходить к ненужной точности.
Например, в календаре можно установить для всех евентов, назначенных на даты без времени, начало рабочего дня, скажем, в 8-00, а с интервалами придётся указывать 8-00 сразу. А если к этому времени человек начнёт работать с 9-00?
inkelyad
25.08.2024 11:39+1Например, в календаре можно установить для всех евентов, назначенных на даты без времени, начало рабочего дня, скажем, в 8-00, а с интервалами придётся указывать 8-00 сразу. А если к этому времени человек начнёт работать с 9-00?
Не понял примера. событие с датой, но без времени - требует указания часового пояса, чтобы определять, когда эта дата начинается и кончается. Часовый пояс косвенным образом определяет интервал начала и конца даты.
Потому что событие назначенное на "2024-08-26" - может произойти, скажем, в 11 часов вечера по московскому времени. Но во Владивостоке оно же - это уже 27-ое число.
ImagineTables
25.08.2024 11:39Если у меня билет на 1 сентября, и из-за часовых поясов возможен попадос, я укажу ТОЧНОЕ ВРЕМЯ.
А если мне надо оплатить Интернет 1 сентября и я создаю напоминалку на 1 сентября (или 1-е число каждого месяца), мне глубоко фиолетовы эти тонкости. И если в интерфейсе приложения всплывут корни (дата это промежуток между двумя точными точками), я его выкину. А если не всплывут, то программист вынужден будет писать свою дату (может быть, на базе промежутковой).
inkelyad
25.08.2024 11:39+1А если мне надо оплатить Интернет 1 сентября и я создаю напоминалку на 1 сентября (или 1-е число каждого месяца), мне глубоко фиолетовы эти тонкости.
Потом расшариваешь кому-то календарь, это кто-то напоминалку видит, находясь в другом часовом поясе и в результате промахивается с датой.
интерфейсе приложения всплывут корни
Всплывут. Если не интерфейсе, так при общении. Потому что "созваниваемся 1-го числа у меня или у тебя?"
Да и разные - "У нашего офиса <там-то> рабочее время по нашему времени какое?". Тот самый интервал. Оно очень быстро всплывает. Вот как хранить - это можно по разному справляться.
Или даже: "Паспорт выдан <дата>", что в базе записали неудачно в виде Instant на начало дня московского времени. Человек идет в отделение банка опять же во Владивостоке и получает "а у вас срок действия паспорта вчера вышел". (Интересно, кстати, как юристам положено этот вопрос решать при удаленном обслуживании. Всякое совершеннолетие итд итп).
ImagineTables
25.08.2024 11:39Потом расшариваешь кому-то календарь, это кто-то напоминалку видит, находясь в другом часовом поясе и в результате промахивается с датой.
И что случится? Интернет будет оплачен не в восемь утра, а в четыре часа дня? Или даже на следующий день? Вот ведь беда-то какая!
Ну а если провайдер — мелочная скотина без грейс-периода и с посекундной тарификацией (у меня, кстати, как раз именно такой), я ставлю в настройке евента «Показывать reminder за 3 дня». То есть, опять же, я буду оперировать теми единицами (днями), в которых мне удобно вести подсчёты. Не надо требовать от меня задумываться о часах, когда речь идёт о днях.
Если речь пойдёт о таком событии, для которого важна точность (билет на балет), я буду думать в часах, минутах и секундах.
Что касается языков, платформ и фреймворков, то они отражают ожидания и подходы. Именно поэтому (по моему скромному мнению) мы имеем то, что имеем.
zelenin
25.08.2024 11:39+1бизнес-процессы могут быть привязаны к местному времени. Сохраняя дату в UTC, мы теряем информацию о местном времени. Другими словами из 13:00 UTC мы не можем восстановить 16:00 MSK
Dywar
25.08.2024 11:39Можем, для этого есть таймзона которую можно передать отдельно.
console.log(Intl.DateTimeFormat().resolvedOptions().timeZone)
Лучше всего передавать аналог DateTimeOffset как в C#, при помощи сторонних либ.
Многим вообще только УТС и хватает, их не интересует время клиента, нужен только точный момент времени. Учитывая что такого не может быть, где гарантия что на клиенте время верно указано, это если докапываться до миллисекунд. Проблема не новая, и раз все еще так работает значит большого кипеша нет.
Новое апи круто, посмотрим.
zelenin
25.08.2024 11:39+1Можем, для этого есть таймзона которую можно передать отдельно.
о том и речь. Информацию о зоне MSK мы уже потеряли, сохранив дату в UTC
Многим вообще только УТС и хватает, их не интересует время клиент
так понятно, что бизнес-требования у всех разные. Поэтому и существует два подхода. Об этом даже на хабре с пяток статей.
Bigata
25.08.2024 11:39Согласен с коллегами выше, хранить и пользоваться лучше временной меткой UTC. Только показать в нужном формате.
Разницы поудобнее наверное юзать.
nin-jin
25.08.2024 11:39Я просто оставлю это здесь: https://mol.hyoo.ru/#!section=docs/=giikl8_xe40dd
4кб и вы получаете простой и универсальный апи уже сейчас с поддержкой не только времени, но и интервалов.
Zenitchik
25.08.2024 11:39+2Самый главный вопрос: стандартными способами подключается? Я не хочу ради одной фичи тащить в проект ещё одну систему управления зависимостями.
flancer
25.08.2024 11:39Стандартными, но довольно оригинально:
import('https://cdn.jsdelivr.net/npm/mol_time_all@1.1.1096/web.mjs') .then(({default:Module}) => { debugger console.log(new Module.$mol_time_moment().toString()); });
Default-экспорт es6-модуля для браузера представляет собой глобальный объект Window:
Наверное, в этом есть какой-то смысл, но явно неявный.
nin-jin
25.08.2024 11:39Вам ли не знать про Inversion of Control? Это дефолтный контекст окружения.
flancer
25.08.2024 11:39+2В моей секте этот тип IoC не считается кошерным.
Ваш подход к построению браузерного приложения мне кажется эгоцентричным до солиптичности. Это неплохо в одних некоторых случаях (когда вы самый главный в песочнице), но может сильно мешать в других некоторых случаях (когда каждый считает себя самым главным в песочнице).
Я как-то в Magento-магазине совмещал больше пяти jQuery-библиотек разных версий, которые тянулись различными Magento-плагинами, и пришёл в восторг, что это всё работает (я тогда только-только из Java вышел). А в вашем подходе я вижу регресс в этом вопросе:
import('https://cdn.jsdelivr.net/npm/mol_time_all@1.1.1096/web.mjs') .then(({default: Module96}) => { import('https://cdn.jsdelivr.net/npm/mol_time_all@1.1.1095/web.mjs') .then(({default: Module95}) => { const polluted = (Module95 === Module96); debugger }); });
Лично я предпочитаю держать свой global object Window в чистоте и иметь возможность пользоваться разными версиями одной и той же библиотеки. Так-то мне это обычно не надо, но иметь возможность лучше, чем её не иметь.
nin-jin
25.08.2024 11:39flancer
25.08.2024 11:39+1Вы на стиле!! :))) Что "тут" и "тут"? Вы даёте ссылки на свои статьи, которые хоть каким-то боком относятся к дискутируемому вопросу и сваливаете в закат. Вы так рейтинг своего hyoo.ru в поисковиках поднимаете?
Вот так делается нормальное версионирование на нормальных платформах
Код:
import('https://cdn.jsdelivr.net/npm/svelte@4.2.19/+esm') .then((Svelte4) => { import('https://cdn.jsdelivr.net/npm/svelte@5.0.0-next.239/+esm') .then((Svelte5) => { const polluted = (Svelte4 === Svelte5); debugger }); });
Нажмите F12, скопируйте код в консоль браузера, запустИте, а затем походИте в отладчике по переменным и скоупам - посмотрИте, как выглядит в runtime код, спроектированный для работы с кодом других разрабов.
ОценИте кол-во элементов в импорте для Svelte (13 штук) и ваших добавлений в
window
с префиксом$mol_
(24 штуки). А ведь я подключил лишь одну вашу библиотеку по работе с датами. Представьте теперь, что будет вwindow
, когда я подключу другие ваши библиотеки, свои модули, модули других разработчиков (подскажу - как все файлы на компьютере, если их собрать в одном каталоге).В общем, ваш $mol - это аналог parser Студии Лебедева. Хороший инструмент для решения некоторого подмножества задач. Главное - не выходить за границы его применимости (студии).
nin-jin
25.08.2024 11:39Вместо того, чтобы писать тут столько глупостей, лучше бы сходили по ссылкам и почитали, что более опытные люди пишут. Там не много, глаза не отсохнут.
Ну и для справки: Svelte - не полноценный аналог $mol_view - одного из сотен модулей в $mol.
flancer
25.08.2024 11:39+1Мне тут чат-гопота разложила по понятиям:
Крауд-маркетинг — это метод продвижения, при котором размещаются комментарии, сообщения или ответы на тематических форумах, в социальных сетях, блогах и других онлайн-платформах с целью привлечения внимания к продвигаемому сайту, продукту или услуге. Часто такие сообщения содержат ссылки на основной сайт, который нужно продвигать в поисковых системах.
Главная идея заключается в том, чтобы создавать полезный контент и взаимодействовать с целевой аудиторией в рамках обсуждений, тем самым органично вставляя ссылки. Однако, если это делается без учета контекста и выглядит как спам, такие действия могут принести негативный эффект и повредить репутации компании или сайта.
Перефразируя известный афоризм про деньги - "неважно, о чём начинает говорить Карловский, Карловский всегда и всё сводит к hyoo".
amishaa
25.08.2024 11:39И в оригинале, и в переводе что-то не так со сниппетом про сравнение времени ( one/two) - выглядит так, что либо должна быть разная таймзона в разных строчках, либо как-то ещё указано, что в two время наступило второй раз.
Cherezzabo
25.08.2024 11:39+1А это точно хорошее решение привязывать таймзону к строковым литералам "Europe/Madrid" или "Asia/Tokio"? Понятно, что для удобства разработчика сделано, но что делать если вдруг гео-политическая реальность вмешается в действительность (не дай бог, конечно)?
vanxant
25.08.2024 11:39+3Это единственное хорошее решение. Из даты с частью Z+3:00 вы не можете восстановить местное время события, потому что DST, да и просто зоны иногда меняются властями. Так что да, таймзону придётся хранить вечно, tzdata обязательна и будет только расти и пухнуть
bBars
25.08.2024 11:39Вот именно. И dst, и сами таймзоны меняются периодически. Поэтому для полного понимания нужно после наименования зоны указывать ещё и текущий таймстамп, чтобы обозначить время применения смещения. И вдобавок иметь под рукой справочник: таак, вот в 12 ночи такого-то числа 2013 года отменили dst; потом тогда-то вернули.
А для указания времени применения тоже ведь нужно часовой пояс указать. Так, погодите-ка...
kykint
25.08.2024 11:39+2Этот справочник есть во всех системах и постоянно обновляется. Называется tzdata
inkelyad
25.08.2024 11:39постоянно обновляется.
Но иногда только вместе с железкой. Берем старый андроид телефон и убеждаемся.
И с этим связаны грабли - когда разные системы в цепочке передачи сообщений разными вариантами этой самой tzdata используется при конвертации из одного представления в другое.
Dadadam999
25.08.2024 11:39+1Да, потому что это не просто строковые литералы, а скорее регионы. Например в UTC +3:00 может быть несколько регионов и проблема в том, что в одном из них может быть перевод на летнее время, а в другом нет. Вообще автор не упомянул о летнем времени, а это очень важный момент.
VoodooCat
25.08.2024 11:39Откровенно говоря, zoned time не нужен вообще и UTC даты более чем достаточно. Вы мля приводите примеры с платежами где клиентское время в браузере вообще не может участвовать. И прочую дичь. Улучшения безусловно нужны, но примеры - высосаны из пальца и к жизни не имеют отношения.
muxa_ru
25.08.2024 11:39мы будем знать только количество миллисекунд, прошедших с эпохи UNIX, и смещение
Если коротко, то "нет", если полнее то "вот вообще нет".
jt3k
25.08.2024 11:39+1tl;dr: читайте спеку и описание сами.
статья как будто мусорная и ничего не объясняет. она просто обрывается на том месте где нужно объяснять
Metotron0
Пример с кофе имеет и обратную сторону. Я тоже пил кофе в области с временем на час больше моего, потом вернулся и опять попил кофе, в итоге в банковском приложении получилось, что второй кофе я попил раньше первого.
Если бы там обозначался часовой пояс, то, может, и было бы понятнее, но визуального мусора прибавилось бы.
Ещё бывает, что в поезде ставишь будильник и не знаешь, успеет ли телефон перейти на другой часовой пояс к тому моменту. А по проводнику просыпаться хуже, потому что в туалет будут очереди.
Farongy
Авиарежим?
Zenitchik
Слава богу, что я никогда не просыпался в поезде по будильнику на телефоне! Буду и впредь пользоваться для этого ТОЛЬКО устройствами, которые не переводят время без моей команды.
aamonster
Ещё бывает, что в поезде ставишь будильник и не знаешь, успеет ли телефон перейти на другой часовой пояс к тому моменту.
Вроде в календаре события к UTC привязаны, можно для таких случаев там напоминалку ставить.
boris_su
Лайфхак ))) ставишь два будильника, как только проснулся - второй отключаешь (не благодарите)...