Работа с датой и временем в JavaScript – ИТ Шеф

Работа с датой и временем в JavaScript - ИТ Шеф Каталог

Дата и время js: все, что нужно знать

Изучаем дату и время JS. В этой статье я расскажу все, что вам нужно знать об объекте Date, входящем в состав JavaScript.

В JavaScript для операций с датами используется только два часовых пояса: местное время и всемирное координированное время (UTC).

По умолчанию почти каждый метод объекта Date возвращает дату и время по местному часовому поясу. Вы получите UTC, только если явно укажете это.

Вы можете создать дату, использовав оператор new:

  1. Со строкой даты.
  2. С аргументами даты.
  3. С меткой времени.
  4. Без аргументов.

Мы передаем строку даты в конструктор new Date.

Но если вы передадите строковое представление даты в формате 21-03-1988, то получите ошибку.

new Date(’21-03-1988′) приводит к ошибке Invalid Date.

В JavaScript нужно использовать формат даты, который принят во всем мире –  ISO 8601.

Вот что означают значения:

  • YYYY: 4-значный год;
  • MM: двузначный месяц (где январь 01, а декабрь 12);
  • DD: двузначная дата (от 01 до 31);
  • -: разделители даты;
  • T: указывает на начало блока времени;
  • HH: часы в 24-часовом формате (от 00 до 23);
  • mm: минуты (от 00 до 59);
  • ss: секунды (от 00 до 59);
  • sss: миллисекунды (от 000 до 999);
  • :: разделители времени;
  • Z: если Z присутствует, дата будет установлена в UTC. Если Z отсутствует, это будет местное время.

Часы, минуты, секунды и миллисекунды необязательны, если вы создаете дату. Если нужно передать дату 11 июня 2022 года:

Но если вы находитесь к востоку от Гринвича, то получите дату 10 июня.

new Date(‘2022-06-11’) выдаёт 10 июня, если вы находитесь к востоку от Гринвича

Если в районе, который опережает время по Гринвичу, то получите дату 11 июня.

new Date(‘2022-06-11’) выдаёт 11 июня, если вы находитесь к западу от Гринвича

Это происходит потому, что вы передаете дату без указания времени. В итоге JavaScript выводит дату, установленную в UTC.

Строка кода new Date('2022-06-11') создает дату 11 июня 2022, 12:00 UTC. Вот почему люди, которые живут восточнее Гринвича, получают 10 июня вместо 11 июня.

Если вы хотите создать дату по местному часовому поясу, необходимо указать время. Когда вы включаете в дату время, вам нужно добавить, как минимум, часы и минуты (иначе Google Chrome возвращает недопустимую дату).

Дата по местному времени и дата в UTC.

Кстати, MDN предостерегает от использования строкового представления даты, поскольку браузеры могут ее трактовать по-разному.

MDN не рекомендует создавать даты с помощью строкового представления.

Конструктор Date принимает семь параметров:

  1. Год: четырехзначный год.
  2. Месяц: месяц года (0-11). По умолчанию 0.
  3. День: день месяца (1-31). По умолчанию 1.
  4. Час: час дня (0-23). По умолчанию 0.
  5. Минуты: Минуты (0-59). По умолчанию 0.
  6. Секунды: секунды (0-59). По умолчанию 0.
  7. Миллисекунды: миллисекунды (0-999). По умолчанию 0.

Многие разработчики избегают подхода с использованием аргументов, потому что он выглядит сложным. Но на самом деле все довольно просто.

Но помните, что порядок месяца начинаются с нуля. Например, январь – 0, февраль – 1, март – 2 и т. д.

Вот еще несколько примеров для закрепления:

// 21 марта 1988г. 00:00 местного времени
new Date(1988, 2, 21)
// 25 декабря 2022г. 8:00 местного времени 
new Date(2022, 11, 25, 8)
// 6 ноября 2023г. 2:20 местного времени
new Date(2023, 10, 6, 2, 20)
// 11 июня 2022г. 5:23:59 местного времени
new Date(2022, 5, 11, 5, 23, 59)

Даты, созданные с помощью аргументов, указаны по местному времени. Если вам понадобится дата в формате UTC, используйте следующий синтаксис:

В JavaScript метка времени – это количество миллисекунд, прошедших с 1 января 1970 г. (от начала эпохи Unix). Метки времени используются только для сравнения различных дат.

Если использовать конструктор объекта Date() без аргументов, то он возвращает дату по местному времени.

Текущее время.

Объект Date предоставляет семь методов форматирования. Каждый из этих семи методов возвращает определенное значение (и все они совершенно бесполезны).

  1. toString выдаст Wed Jan 23 2022 17:23:42 GMT 0800 (Singapore Standard Time)
  2. toDateString выдаст Wed Jan 23 2022
  3. toLocaleString выдаст 23/01/2022, 17:23:42
  4. toLocaleDateString выдаст 23/01/2022
  5. toGMTString выдаст Wed, 23 Jan 2022 09:23:42 GMT
  6. toUTCString выдаст Wed, 23 Jan 2022 09:23:42 GMT
  7. toISOString выдаст 2022-01-23T09:23:42.079Z

Если вам нужен собственный формат, то придётся создать его самостоятельно.

Чтобы получить дату в формате Четверг, 23 января 2022, нужно применять методы объекта Date:

  1. getFullYear: возвращает четырехзначный год по местному времени.
  2. getMonth: возвращает месяц года (0-11) по местному времени.
  3. getDate: возвращает день месяца (1-31) по местному времени.
  4. getDay: возвращает день недели (0-6) по местному времени. Неделя начинается с воскресенья (0) и заканчивается субботой (6).

Чтобы получить Четверг, 23 января 2022, нужно использовать методы getFullYear и getDate:

Труднее вывести строки Четверг и января. Чтобы получить название месяца, нужно создать объект, который содержит значение всех двенадцати месяцев.

const months = {
  0: 'января',
  1: 'февраля',
  2: 'марта',
  3: 'апреля',
  4: 'мая',
  5: 'июня',
  6: 'июля',
  7: 'августа',
  8: 'сентября',
  9: 'октября',
  10: 'ноября',
  11: 'декабря'
}

Поскольку год начинается с нулевого месяца, то можно использовать массив вместо объекта.

const months = [
  'января',
  'февраля',
  'марта',
  'апреля',
  'мая',
  'июня',
  'июля',
  'августа',
  'сентября',
  'октября',
  'ноября',
  'декабря'
]

Чтобы получить названием месяца:

  1. Нужно использовать getMonth, чтобы получить индекс месяца.
  2. Получить название месяца из months.

Краткая версия:

Чтобы вывести день недели, нужен массив со всеми названиями:

Затем:

  1. Получаем dayIndex с помощью getDay.
  2. Используем dayIndex, чтобы получить dayName.

Краткая версия:

Объединим все переменные, которые создали, чтобы получить отформатированную строку.

Для форматирования времени используйте следующие методы:

  1. getHours: выдаст часы (0-23) по местному времени.
  2. getMinutes: выдаст минуты (0-59) по местному времени.
  3. getSeconds: выдаст секунды (0-59) по местному времени.
  4. getMilliseconds: выдаст миллисекунды (0-999) по местному времени.

В JavaScript для сравнения дат используются операторы >, <, >= и <=.

Сложнее сравнить даты по времени суток (часам и минутам). Вы не сможете сравнить их с помощью == или ===:

Чтобы проверить, совпадает ли в двух датах значение времени, нужно сравнить их временные метки с помощью getTime.

Чтобы проверить, совпадают ли в датах дни недели, сравните их значения  с помощью методов getFullYear, getMonth и getDate.

const isSameDay = (a, b) => {
  return a.getFullYear() === b.getFullYear() &&
    a.getMonth() === b.getMonth() &&
    a.getDate()=== b.getDate()
}
const a = new Date(2022, 0, 26, 10) // 26 января 2022, 10:00
const b = new Date(2022, 0, 26, 12) // 26 января 2022, 12:00
console.log(isSameDay(a, b)) // true

Чтобы получить дату из другой даты, нужно:

  1. Установить конкретное значение даты/времени из другой даты.
  2. Добавить/вычесть отрезок времени из другой даты.

Для этого можно использовать следующие методы:

  1. setFullYear: устанавливает четырехзначный год по местному времени.
  2. setMonth: устанавливает месяц года по местному времени.
  3. setDate: устанавливает день месяца по местному времени.
  4. setHours: устанавливает часы по местному времени.
  5. setMinutes: устанавливает минуты по местному времени.
  6. setSeconds: устанавливает секунды по местному времени.
  7. setMilliseconds: устанавливает миллисекунды по местному времени.

Например, если вы хотите задать дату для 15 числа месяца, вы можете использовать метод setDate().

Чтобы установить июнь месяц, примените метод setMonth:

Методы установки значений даты изменяют исходный объект Date(). На практике лучше модифицировать отдельный экземпляр объекта.

Чтобы добавить/вычесть период времени, нужно знать значение текущей даты. Его можно получить с помощью следующих методов:

  1. getFullYear: возвращает четырехзначный год по местному времени.
  2. getMonth: возвращает месяц года (0-11) по местному времени.
  3. getDate: возвращает день месяца (1-31) по местному времени.
  4. getHours: возвращает часы (0-23) по местному времени.
  5. getMinutes: возвращает минуты (0-59) по местному времени.
  6. getSeconds: возвращает секунды (0-59) по местному времени.
  7. getMilliseconds: возвращает миллисекунды (0-999) по местному времени.

Допустим, что вы хотите назначить свидание через три дня (начиная  с сегодняшнего дня). Предположим, что сегодня 28 марта 2022 года.

Сначала создаем новый объект Date (чтобы не изменять исходный экземпляр объекта):

С помощью getDate получаем значение, которое хотим изменить:

Свидание произойдет через три дня. Поэтому добавляем их к текущей дате:

Полный код для первого подхода:

Сначала используем getFullYear, getMonth, getDate и другие методы, пока не получим типа значения, которое мы изменить. Затем мы создаём искомую дату при помощи new Date.

Оба подхода работают.

Допустим, мы установили дату на 33 марта 2022 года. (В календаре нет 33 марта). В этом случае JavaScript автоматически переведёт 33 марта во 2 апреля.

33 марта автоматически преобразуется во 2 апреля.

Поэтому не стоит беспокоиться о расчете минут, часов, дней, месяцев при добавлении периода времени. JavaScript делает это автоматически.

30 3=33. 33 марта автоматически преобразуется во 2 апреля.

И это всё, что вам нужно знать о JavaScript-объекте Date.

Пожалуйста, оставляйте свои мнения по текущей теме материала. Мы очень благодарим вас за ваши комментарии, подписки, лайки, отклики, дизлайки!

Javascript: текущая дата и время (все методы получения)

Для получения значений даты и времени используются следующие методы:

1const date =newDate('September 20, 2020 11:10:05');

2

3date.getTime();

4

5

6date.getFullYear();

7

8

9date.getDate();

10

11

12date.getDay();

13

14

15date.getMonth();

16

17

18date.getHours();

19

20

21date.getMinutes();

22

23

24date.getSeconds();

25

26

27date.getMilliseconds();

28

29

30date.getTimezoneOffset();

31

Также есть возможность сразу получать значения даты и времени привязанных
не локальной зоне компьютера пользователя, а к зоне UTC.

1date.getUTCFullYear();

2

3

4date.getUTCDate();

5

6

7date.getUTCDay();

8

9

10date.getUTCMonth();

11

12

13date.getUTCHours();

14

15

16date.getUTCMinutes();

17

18

19date.getUTCSeconds();

20

21

22date.getUTCMilliseconds();

23

Установка компонентов даты и времени

Для установки отдельных компонентов даты или времени можно использовать следующие методы:

1const date =newDate('September 20, 2020 11:10:05');

2

3

4date.setDate(22);

5

6

7

8const newDate =newDate(1600762205000);

9

Другие методы установки отдельных компонентов объекта Date:

1

2date.setFullYear(новое значение)

3date.setMonth(month)

4date.setHours(hour)

5date.setMinutes(min)

6date.setSeconds(sec)

7date.setMilliseconds(ms)

8date.setTime(ms)

Методы setHours(), setMinutes() и setSeconds() помимо базовых значений принимают значения минут,
секунд или миллисекунд для более точных установок. Формат ввода выглядит следующим бразом:

1setHours(18,45,20,10);

2setMinutes(min, sec, ms);

3setSeconds(sec, ms);

Стоить помнить, что если мы укажем обновленное значение часов следующим образом date.setHours(72), то это приведет к обновлению нашей даты (прибавится 3 дня).

Передать дату в объект Date – в формате строки

Мы также можем использовать строчные выражения для передачи в объект Date и получения соответствующего момента времени.
В этом случае в объекте Date будет автоматически использоваться метод parse для определения даты и времени, которые мы в него передаем.

1newDate('2020-09-20');

2

3

4newDate('2020/09/20');

5

6

7newDate('September 20, 2020');

8

9

10newDate('20 September 2020');

11

12

13newDate('September 2020');

14

15

16newDate('2020 September');

17

18

19newDate('2020-09');

20

Также есть возможность использовать метод Date.parse(), который возвращает временную метку в миллисекундах.

1newDate('September 20, 2020');

2

3

4Date.parse('September 20, 2020');

5

Объект date: форматирование

Объект Date содержит ряд методов, которые возвращают дату и время в формате строки:

1const date =newDate('September 20, 2020 11:10:05');

2

3date.toString();

4

5

6date.toTimeString();

7

8

9date.toDateString();

10

11

12date.toUTCString();

13

14

15date.toISOString();

16

17

18date.toLocaleTimeString();

19

20

21date.toLocaleString();

22

Использование библиотеки moment.js

– одна из самых популярных библиотек для работы с датой и временем в Javascript.

Рассмотрим несколько примеров ее использования.

Сначала устанавливаем пакет с помощью команды npm install moment.

1

2import moment from ‘moment’;

3

4

5const date =moment();

6

7

8

9const date =moment('September 20, 2020');

10

11

12const date =moment().format('MM Do, YYYY');

Список опций форматирования доступен в документации библиотеки (пара примеров):

  • YYYY-MM-DD – 2022-12-14
  • HH:mm:ss – 16:34:10

Один из методов библиотеки, которые часто приходится использовать – fromNow(),
который возвращает количество времени, которое прошло с указанной даты.

1moment('2020-06-20').fromNow();

2

Я использую этот метод для отображения времени, которое прошло с даты публикации постов в блоге.

Как сравнить 2 даты

Самый простой способ сравнить 2 даты – использовать метод Date.getTime().

1const date1 =newDate('September 20, 2020 11:10:05');

2const date2 =newDate('September 25, 2020 11:10:05');

3

4const difference = date2.getTime()- date1.getTime();

5

Также для сравнения дат можно использовать условия if:

1if(date2.getTime()=== date1.getTime()){

2console.log('даты равны');

3

4}

Кто виноват


Первая проблема возникает когда нужно задать дату/время в тайм-зоне отличной от UTC и от локальной. Конструктор Date не имеет такого параметра.

new Date(); 
new Date(value); 
new Date(dateString); 
new Date(year, month[, day[, hour[, minute[, second[, millisecond]]]]]);

Единственный вариант, где можно указать смещение относительно UTC — третий способ. Вызов конструктора в этом формате позволяет передать смещение как часть строки:

new Date('Sun Feb 01 1998 00:00:00 GMT 0700')

Строка принимается в формате RFC2822, весьма неудобном и трудном к ручному вводу. Получить такую строку из пользовательского ввода практически невозможно. Я не могу себе представить человека, который бы согласился вводить дату в таком формате.

Не смотря на то, что в Date можно установить все параметры по отдельности для таймзоны UTC — проблемы это не решает – таймзона останется локальной. Но это не единственная проблема.

Смещение относительно UTC — не константа. Это функция от даты, времени (ну или от таймштампа, если угодно) и, опять же, тайм-зоны. Например, для Москвы последний перевод времени даёт:

 new Date(2022, 9, 25, 0, 0, 0); // 26.10.2022, 21:00:00 GMT 3 
 new Date(2022, 9, 27, 0, 0, 0); // 25.10.2022, 22:00:00 GMT 4 

Таким образом, конструктор в третьем варианте становится практически бесполезным поскольку смещение нужно знать заранее. А оно, как было сказано, так просто получено быть не может. Единственная библиотека, из попавшихся мне, которая использует базу данных

для обсчета сдвигов —

. Проблема использования этой библиотеки в том, что низлежащие библиотеки (date/time picker-ы) про неё ничего не знают и внутри активно используют стандартный Date. Остальные библиотеки, работающие с объектом Date, явно на эту базу не ссылаются и обновления из нее не получают. (Поправьте меня в комментариях.)

В бизнес применении тайм-зоны имеют смысл только если заданы дата и время. К примеру, если рабочий день начинается в 9:00, то вряд ли вы ожидаете что ваш коллега из Владивостока начнет работать в 15:00. Таймзоны учитываться не должны и в таком случае отображать дату нужно в UTC.

Однако, в случае с регулярными событиями, происходящими в один момент времени в разных часовых поясах, таймзона все таки нужна. К примеру, ваш ежедневный скрам начинается в 10:00 для вас и в 13:00 для Новосибирска. Между прочим, в этом как раз и есть разница между GMT и UTC. UTC – это время без смещения, а GMT – это время со смещением 0. Поясню на примере:

31.12.2022, 20:59:59 GMT в Московском часовом поясе должно выглядеть как 31.12.2022, 23:59:59  
31.12.2022, 20:59:59 UTC в Московском часовом поясе должно выглядеть как 31.12.2022, 20:59:59 

Из-за этой арифметики их в основном и путают. К сожалению, напутано с этим параметром повсеместно. Отсутствие прямого указания таймзоны в JS трактуется как локальная таймзона, а указание UTC и GMT равнозначно.

В ситуации мог бы помочь Intl. Мог бы, но не обязан. В частности, там есть такой параметр timeZone, но, чуть далее стандартом определено: The time zone to use. The only value implementations must recognize is «UTC». В настоящее время кроме Chrome не один браузер произвольные таймзоны не поддерживает. С диапазонами времени в JS все совсем плохо — ничего подобного в языке нет. Хочешь сделать хорошо – делай сам.

Использовать конструктор new Date() с передачей временной метки, выраженной в миллисекундах

Вероятно, вы сталкивались с временной меткой Unix, которая использует ту же нулевую дату, но представлена не в миллисекундах, а секундах.

Мы можем передать временную метку Unix в объект Date и получить соответствующий момент времени следующим образом:

1const unixTimeStamp =1600855031;

2newDate(unixTimeStamp *1000);

3

Если мы передадим 0 в объект Date, то получим нулевую дату 1 Января 1970.

1newDate(0);

2

Можно также использовать встроенный метод Date.now(),
чтобы получить текущую временную метку в миллисекундах и передать ее в объект Date.

1Date.now();

2

3newDate(1600855352116);

4

Метод intl.datetimeformat()

В этом посте нас интересует метод Intl.DateTimeFormat(), который используется для форматирования даты и времени.

Рассмотрим следующий пример:

Давайте получим дату в локальном формате:

1const date =newDate('September 20, 2020 11:10:05');

2newIntl.DateTimeFormat().format(date);

3

Теперь получим дату в формате, который используется в США.
Для этого используем параметр en-Us (английский язык, используемый в США (US)):

1const date =newDate('September 20, 2020 11:10:05');

2newIntl.DateTimeFormat('en-Us').format(date);

3

Получение отдельных компонентов даты и времени

В JavaScript для получения отдельных компонентов даты и времени предназначены следующие методы:

Все эти методы возвращают отдельные компоненты даты и времени в соответствии с часовым поясом установленном на локальном устройстве пользователя.

Пример:

// создадим дату 11.11.2022 00:00 по UTC
var newDate = new Date(Date.UTC(2022,11,11));

// получим компоненты даты, если на устройстве пользователя местное время равно UTC 10:00
newDate.getFullYear(); //2022
newDate.getMonth(); // 10
newDate.getDate(); // 11
newDate.getHours(); // 10
newDate.getMinutes(); // 0
newDate.getSeconds(); // 0
newDate.getMilliseconds(); // 0

Пример, в котором поприветствуем пользователя в зависимости от того какой сейчас у него интервал времени:

// получим текущее время пользователя и компоненты этого времени
var 
  now = new Date(),
  hour = now.getHours(),
  minute = now.getMinutes(),
  second = now.getSeconds(),
  message = '';

// определим фразу приветствия в зависимости от местного времени пользователя 
if (hour <= 6) {
  message = 'Доброе время суток';
} else if (hour <= 12) {
  message = 'Доброе утро';
} else if (hour <= 18) {
  message = 'Добрый день';
} else {
  message = 'Добрый вечер';
}

// выполним форматирование времени с использованием тернарного оператора
minute = (minute < 10) ? '0'   minute : minute;
second = (second < 10) ? '0'   second : second;
hour = (hour < 10) ? '0'   hour : hour;

message  = ', сейчас '   hour   ':'   minute   ':'   second;

// выведем приветствие и время в консоль
console.log(message); // Добрый вечер, сейчас 22:50:39

В этом примере вывод времени в нужном формате осуществлён с использованием тернарного оператора.

В JavaScript имеются аналоги этих методов для получения отдельных компонентов даты и времени для часового пояса UTC 0. Эти методы называются аналогично, но с добавленным «UTC» после «get»: getUTCFullYear(), getUTCMonth(), getUTCDate(), getUTCHours(), getUTCMinutes(), getUTCSeconds(), getMilliseconds().

Получить номер дня недели в JavaScript можно с помощью метода getDay().

Данный метод возвращает число от 0 до 6 (0 – воскресенье, 1 – понедельник, …, 6 – суббота).

Пример, в котором переведём день недели из числового в строковое представление:

var days =["Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"];

// получим текущую дату
var now = new Date();
// выведем в консоль день недели
console.log('Сегодня '   days[now.getDay()]);

Получить количество миллисекунд прошедших с 01.01.1970 00:00:00 UTC в JavaScript можно с помощью метода getTime().

Узнать разницу (в минутах) между часовым поясом локального устройства и UTC в JavaScript можно с помощью метода getTimezoneOffset().

Объяснение кода

Давайте быстро взглянем на код, чтобы увидеть, как он работает. Сначала мы включаем библиотеки, необходимые для этого проекта.

  • NTPClient.h – это библиотека работы со временем, которая выполняет синхронизацию с NTP-сервером;
  • библиотека ESP8266WiFi.h предоставляет специальные методы для работы ESP8266 с WiFi, которые мы вызываем для подключения к сети;
  • библиотека WiFiUdp.h обрабатывает протокол UDP, например открытие порта UDP, отправку и получение пакетов UDP и т.д.
#include <NTPClient.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

Затем мы настраиваем несколько констант, таких как SSID, пароль WiFi и смещение UTC, о которых вы уже знаете. Мы также определяем двумерный массив daysOfTheWeek.

Теперь, прежде чем инициализировать объект NTP-клиента, нам нужно указать адрес NTP-сервера, который мы хотим использовать. pool.ntp.org – это открытый проект NTP, отлично подходящий для подобных вещей.

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds);

pool.ntp.org автоматически выбирает серверы времени, которые географически близки к вам. Но если вы хотите сделать выбор явно, используйте одну из подзон pool.ntp.org.

Весь мирpool.ntp.org
Азияasia.pool.ntp.org
Европаeurope.pool.ntp.org
Северная Америкаnorth-america.pool.ntp.org
Океанияoceania.pool.ntp.org
Южная Америкаsouth-america.pool.ntp.org

В функции setup() мы сначала инициализируем последовательную связь с ПК и подключаемся к сети WiFi с помощью функции WiFi.begin().

Serial.begin(115200);

WiFi.begin(ssid, password);

while ( WiFi.status() != WL_CONNECTED ) 
{
  delay ( 500 );
  Serial.print ( "." );
}

Когда ESP8266 подключится к сети, мы инициализируем NTP-клиент с помощью функции begin().

timeClient.begin();

Теперь мы можем просто вызывать функцию update() всякий раз, когда нам нужны текущие день недели и время. Эта функция передает пакет с запросом на NTP-сервер, используя протокол UDP, и преобразует полученный пакет с меткой времени в читаемый формат.

timeClient.update();

Вы можете получить доступ к информации о дне и времени, воспользовавшись функциями библиотеки NTP-клиента.

Serial.print(daysOfTheWeek[timeClient.getDay()]);
Serial.print(", ");
Serial.print(timeClient.getHours());
Serial.print(":");
Serial.print(timeClient.getMinutes());
Serial.print(":");
Serial.println(timeClient.getSeconds());

Оригинал статьи:

Временной интервал

С хранением и обработкой временных интервалов все просто: их величина не зависит от часового пояса, поэтому никаких особых рекомендаций здесь нет. Их можно хранить и передавать как количество единиц времени (целое или с плавающей точкой, в зависимости от необходимой точности). Если важна секундная точность — то как количество секунд, если миллисекундная — то как количество миллисекунд и т.д.

А вот вычисление интервала может иметь подводные камни. Предположим, у нас есть типовой код на C#, который считает интервал времени между двумя событиями:

DateTime start = DateTime.Now;
//...
DateTime end = DateTime.Now;
double hours = (end - start).TotalHours;

На первый взгляд, никаких проблем здесь нет, но это не так. Во-первых, могут возникнуть проблемы с юнит-тестированием такого кода, но об этом мы поговорим чуть позже. Во-вторых, давайте представим, что начальный момент времени пришелся на зимнее время, а конечный — на летнее (например, таким образом замеряется количество рабочих часов, а у работников есть ночная смена).

Предположим, код работает в часовом поясе, в котором переход на летнее время в 2022 году происходит в ночь 27 марта, и смоделируем описанную выше ситуацию:

DateTime start = DateTime.Parse("2022-03-26T20:00:15 02");
DateTime end = DateTime.Parse("2022-03-27T05:00:15 03");
double hours = (end - start).TotalHours;

Этот код даст в результате 9 часов, хотя фактически между этими моментами прошло 8 часов. В этом легко убедиться, изменив код вот таким образом:

DateTime start = DateTime.Parse("2022-03-26T20:00:15 02").ToUniversalTime();
DateTime end = DateTime.Parse("2022-03-27T05:00:15 03").ToUniversalTime();
double hours = (end - start).TotalHours;


Отсюда вывод —

любые арифметические операции с датой и временем нужно делать, используя либо UTC значения, либо типы, хранящие информацию о часовом поясе

. А потом обратно переводить в локальные в случае надобности. С этой точки зрения, изначальный пример легко исправить, поменяв DateTime.Now на DateTime.UtcNow.

Этот нюанс не зависит от конкретной платформы или языка. Вот аналогичный код на Java, имеющий тот же недостаток:

LocalDateTime start = LocalDateTime.now();
//...
LocalDateTime end = LocalDateTime.now();
long hours = ChronoUnit.HOURS.between(start, end);


Исправляется он также легко — например, использованием ZonedDateTime вместо LocalDateTime.

Преобразование даты в строку и её форматирование

В JavaScript методы, выполняющие преобразование даты в строку, можно разделить на 2 группы.

К первой группе можно отнести методы, которые выполняют это так, как это определено в браузере: toString(), toDateString(), toTimeString(), toUTCString().

Метод toString() возвращает полное представление даты, toDateString() – только дату, toTimeString() – только время, toUTCString() – полное представление даты, но в часовом поясе UTC 0.

Т.к. строковые представления, которые должны возвращать эти методы чётко не определены в стандарте, то они могут отличаться в разных браузерах.

Ко второй группе можно отнести методы с учётом часового пояса и языка локального компьютера: toLocaleString() – дату и время, toLocaleDateString() – только дату, toLocaleTimeString() – только время.

Методы toLocaleString(), toLocaleDateString(), toLocaleTimeString() имеют 2 необязательных параметра. Первый параметр предназначен для явного указания локали, второй – для задания опций форматирования.

Если локаль явно не установлена или undefined, то браузер берёт ту, которую он имеет по умолчанию.

Кроме этого, в JavaScript имеется ещё метод toISOString(). Он возвращает строку, содержащую дату и время в формате ISO (YYYY-MM-DDTHH:mm:ss.sssZ).

Пример:

// создадим дату 15.04.2022 18:43:59 (в часовом поясе пользователя)
var newDate = new Date(2022, 03, 15, 18, 43, 59);

console.log(newDate.toString()); // Mon Apr 15 2022 18:43:59 GMT 1000 (Владивосток, стандартное время)
console.log(newDate.toDateString()); // Mon Apr 15 2022
console.log(newDate.toTimeString()); // 18:43:59 GMT 1000 (Владивосток, стандартное время)
console.log(newDate.toLocaleString()); // 15.04.2022, 18:43:59
console.log(newDate.toLocaleDateString()); // 15.04.2022
console.log(newDate.toLocaleTimeString()); // 18:43:59
console.log(newDate.toUTCString()); // Mon, 15 Apr 2022 08:43:59 GMT
console.log(newDate.toISOString()); // 2022-04-15T08:43:59.000Z

Передать набор параметров в объект Date

Еще один способ создать новый экземпляр объекта Date – передать в него набор значений,
соответствующих году, месяцу (первый месяц равен 0), дню, часу, минутам, секундам и миллисекундам.

1newDate(2020,8,20,18,30,10,0);

2

Во всех случаях, указанных выше, мы получаем дату, привязанную к временной зоне нашего компьютера.
То есть, используя одинаковые входные данные на разных компьютерах, можно получить разные результаты.

Javascript использует UTC в качестве базовой временной зоны, и производит автоматическую конверсию в локальную временную зону вашего компьютера.

Как поменять временную зону?

У нас есть 2 варианта изменения временной зоны при создании нового экземпляра объекта Date:

  • Использование формата “ Часы”

1newDate('January 20, 2020 11:10:05 0700');

  • Передать название временной зоны в скобках

1newDate('January 20, 2020 11:10:05 (CET)');

Важно учитывать, что, если во 2-м случае вы передадите название временной зоны в неверном формате,
Javascript автоматически будет использовать зону UTC. При этом вы не получите сообщения об ошибке.

В первом случае, при передаче часов в неверном формате, Javascript оповестит вас ошибкой “Invalid Date”.

Что делать

  • Вариант 1.

    Не использовать произвольную таймзону. Вариант предпочтительный и, наверное, самый безболезненный. То есть, у вас есть только локальная таймзона и UTC. Для этих случаев во всех браузерах вроде как всё есть, пусть и не очень удобно. К тому же, таймзона выставляется глобально для ОС и менять ее для конкретного web-приложения некошерно.

  • Вариант 2.

    Если произвольные таймзоны необходимы — не использовать таймштамп. Совсем. Храните время в сберегательной кассе в RFC строке с указанием таймзоны. Не уверен что это поможет победить сдвиги таймзоны в кроссбраузерном понимании, но, как минимум, Chrome о таких сдвигах в курсе.

  • Вариант 3.

    Ситуации бывают разные и случается так, что в базу время записывается с какого ни будь устройства. То есть в виде таймштампа. Тут бежать некуда, что бы корректно отобразить время нужно знать либо таймзону устройства, либо таймзону пользователя, либо и того и другого и обсчитывать все сдвиги врукопашную. Без использования базы Олсона тут не обойтись.

  • Тут должна быть мораль сей басни, но добавить мне пока больше нечего. В черновиках стандарта ECMA я никаких подвижек не наблюдаю, наверное, их и не будет.

Оцените статью
Huawei Devices
Добавить комментарий