2019-06-01 21:08:41 +00:00
|
|
|
|
= Работа с датами и временем
|
|
|
|
|
:category: Программирование
|
2019-06-02 16:31:29 +00:00
|
|
|
|
:tags: программирование, время, часы, дата
|
|
|
|
|
:toc: auto
|
2019-06-01 21:08:41 +00:00
|
|
|
|
|
|
|
|
|
== Временные шкалы
|
|
|
|
|
|
|
|
|
|
В идеале системы учёта времени должны обладать тремя характеристиками:
|
|
|
|
|
|
|
|
|
|
. _точность_ (время базируется на атомном стандарте, каждая секунда
|
|
|
|
|
отсчитывается как секунда в системе СИ, нет високосных секунд, переводов
|
|
|
|
|
на зимнее или летнее время и т.п.);
|
|
|
|
|
. _простота_ (каждый '`день`' состоит из 86400 "`секунд`");
|
|
|
|
|
. _календарные дни_ (дни календаря точно соответствуют вращению Земли).
|
|
|
|
|
|
|
|
|
|
На практике есть возможность выбрать шкалу только с двумя
|
|
|
|
|
характеристиками из трёх.
|
|
|
|
|
|
|
|
|
|
. точность и календарные дни. Примером такой шкалы является UTC, в
|
|
|
|
|
которой отсчёт дней и секунд ведётся разными методами (секунды
|
|
|
|
|
исчисляются по атомному стандарту, а дни по суточному вращению Земли), а
|
|
|
|
|
соответствие достигается вводом _секунды координации_;
|
|
|
|
|
. календарные дни и простота. Примером такой шкалы является POSIX (IEEE
|
|
|
|
|
Std 1003.1-1988), в которой день всегда равен 86400 секундам;
|
|
|
|
|
. точность и простота. Этими характеристиками обладают технические шкалы
|
|
|
|
|
(атомные часы, GPS), в которых не важен учёт дней.
|
|
|
|
|
|
|
|
|
|
== Классы времени
|
|
|
|
|
|
|
|
|
|
Время можно условно поделить на два класса: физическое и гражданское.
|
|
|
|
|
|
|
|
|
|
. _Физическое_ время представляет собой точки на непрерывной шкале,
|
|
|
|
|
такую концепцию достаточно точно отражает UTC, если можно пренебречь
|
|
|
|
|
_секундой координации_ (дополнительная секунда, добавляемая к UTC 30
|
|
|
|
|
июня или 31 декабря для согласования со средним солнечным временем UT1.
|
|
|
|
|
В этот момент время условно обозначается как 23:59:60, а на шкале UTC
|
|
|
|
|
две секунды отображаются как одна).
|
|
|
|
|
. _Гражданское_ время представляется полями (год, месяц, число, час,
|
|
|
|
|
минута, секунда, доли секунды, а также временная зона и календарь,
|
|
|
|
|
который по умолчанию считается Григорианским).
|
|
|
|
|
|
|
|
|
|
Почти всегда существует возможность однозначно перевести физическое
|
|
|
|
|
время в гражданское и наоборот, но при этом следует различать их
|
|
|
|
|
свойства и применение (ближайшая аналогия — массивы байтов и символьные
|
|
|
|
|
строки).
|
|
|
|
|
|
|
|
|
|
Основная проблема состоит в том, что в обычном употреблении не всегда
|
|
|
|
|
можно сделать вывод о том, какой класс времени подразумевается, и
|
|
|
|
|
гражданское время меняется на основании юридических актов (указов,
|
|
|
|
|
постановлений и т.п.), что приводит к неоднозначности при планировании
|
|
|
|
|
событий.
|
|
|
|
|
|
|
|
|
|
== Обработка на компьютере
|
|
|
|
|
|
|
|
|
|
=== Хранение и отображение
|
|
|
|
|
|
|
|
|
|
Для ссылки на момент во времени необходимо использовать единую шкалу, на
|
|
|
|
|
которой нет разрывов, связанных с летним временем. Примером стандарта
|
|
|
|
|
времени с такой шкалой является UTC. Локальное или местное время для
|
|
|
|
|
таких целей не подходит, так как на его шкале имеются разрывы и
|
|
|
|
|
неоднозначности, связанные с переходами на летнее время и обратно.
|
|
|
|
|
|
|
|
|
|
Если нужно сохранить значение локального времени, то необходимо
|
|
|
|
|
сохранить также смещение относительно UTC, чтобы временную отметку можно
|
|
|
|
|
было интерпретировать однозначно. Необходимо помнить, что местное время
|
|
|
|
|
может отличаться от UTC на интервал не кратный часу (например, в
|
|
|
|
|
Нидерландах с 1909-05-01 по 1937-06-30 смещение времени от UTC
|
|
|
|
|
составляло 19 минут и 32.13 секунд).
|
|
|
|
|
|
|
|
|
|
Правила хранения и отображения времени:
|
|
|
|
|
|
|
|
|
|
. Время всегда хранится в UTC. При необходимости дополнительно
|
|
|
|
|
сохраняется информация о временной зоне (смещение и/или название).
|
|
|
|
|
. Для вывода на экран время переводится в местное.
|
|
|
|
|
. При выводе на экран отличного от местного времени необходимо указывать
|
|
|
|
|
название часового пояса или типа исчисления (например, Юлианский
|
|
|
|
|
календарь).
|
|
|
|
|
|
|
|
|
|
=== Системное время для администратора
|
|
|
|
|
|
|
|
|
|
Системный администратор должен следить за тем, чтобы файлы базы данных
|
|
|
|
|
описания временных зон *tzdata* имели одну версию в рамках комплекса и
|
|
|
|
|
обновлялись регулярно, чтобы минимизировать расхождения с внешними
|
|
|
|
|
системами. Особенно это важно делать при издании новых правил учёта
|
|
|
|
|
зимнего или летнего времени.
|
|
|
|
|
|
|
|
|
|
На серверах аппаратные часы всегда должны использовать UTC. Операционные
|
|
|
|
|
системы тоже должны использовать UTC, чтобы при возникновении проблем в
|
|
|
|
|
файлах журналов было указано время в едином формате.
|
|
|
|
|
|
|
|
|
|
В рамках комплекса следует использовать синхронизацию времени, даже если
|
|
|
|
|
нет доверенного источника более высокого уровня.
|
|
|
|
|
|
|
|
|
|
=== Клиентские приложения
|
|
|
|
|
|
|
|
|
|
В общем случае нельзя доверять временным отметкам внешних клиентов, так
|
|
|
|
|
как невозможно гарантировать их корректность. Программа должна
|
|
|
|
|
самостоятельно ставить временные отметки для происходящих событий.
|
|
|
|
|
Исключением являются программные комплексы, в которых предусмотрено
|
|
|
|
|
наличие доверенных приложений, ответственных за установку временных
|
|
|
|
|
отметок.
|
|
|
|
|
|
|
|
|
|
=== Postgresql
|
|
|
|
|
|
|
|
|
|
Сервер базы данных должен использовать только временную зону UTC. Для
|
|
|
|
|
этого в файле настройки сервера `postgresql.conf` должна быть строка
|
|
|
|
|
|
|
|
|
|
....
|
|
|
|
|
timezone = 'UTC'
|
|
|
|
|
....
|
|
|
|
|
|
|
|
|
|
При создании таблиц необходимо использовать типы данных
|
|
|
|
|
`timestamp without time zone` и `time without time zone`. Если данные о
|
|
|
|
|
времени должны содержать информацию о временной зоне или смещении
|
|
|
|
|
относительно UTC, то нужно создать дополнительные столбцы, информацию из
|
|
|
|
|
которых должно обрабатывать клиентское приложение. Например, временные
|
|
|
|
|
отметки можно хранить в таком виде
|
|
|
|
|
|
|
|
|
|
[source,sql]
|
|
|
|
|
----
|
|
|
|
|
CREATE TABLE time_stamps (
|
|
|
|
|
time_stamp time without time zone, -- временная отметка
|
|
|
|
|
time_zone character(12), -- текстовое название временной зоны
|
|
|
|
|
shift integer -- смещение локального времени относительно UTC в секундах
|
|
|
|
|
);
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Смещение следует записывать в четырёхбайтное знаковое целое, так как оно
|
|
|
|
|
может быть как положительным, так и отрицательным, а его максимальное
|
|
|
|
|
значение может составлять 14 * 60 * 60 = 50400 секунд. Несмотря на
|
|
|
|
|
приведённый выше пример с временной зоной Нидерландов, микросекундами
|
|
|
|
|
можно пренебречь, так как современные временные зоны имеют смещения
|
|
|
|
|
кратные минутам, а точность до микросекунд в далёком прошлом обычно не
|
|
|
|
|
требуется.
|
|
|
|
|
|
|
|
|
|
Чтобы клиентская программа могла получить выборку, привязанную к
|
|
|
|
|
некоторой временной зоне можно использовать оператор `AT TIME ZONE`,
|
|
|
|
|
например
|
|
|
|
|
|
|
|
|
|
[source,sql]
|
|
|
|
|
----
|
|
|
|
|
SELECT TIMESTAMP '2001-02-16 20:38:40Z' AT TIME ZONE 'MSK';
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
В результате время будет преобразовано из временной зоны UTC в MSK.
|
|
|
|
|
Модификатор `Z` в записи времени указывает на принадлежность зоне UTC.
|
|
|
|
|
|
|
|
|
|
=== Qt
|
|
|
|
|
|
|
|
|
|
Класс `QTimeZone` предназначен для получения информации о временной зоне
|
|
|
|
|
и перевода времени из одной временной зоны в другую.
|