Время, UTC, GMT и много проблем¶
Warning
Этот пост находится в процессе написания. Прошу его не читать. Когда я закончу – пост попадёт в вашу рсс-ленту (и я наверняка напишу о нём на всякие хабры).
Работа со временем – вещь, с которой следует разобраться, пока еще не поздно.
Литература для просветления¶
Для начала неплохо почитать Как перестать думать о часовых поясах и начать жить, или лучше сразу выдержку из комментария к тому топику:
Автор статьи находится ровно на 1 шаг впереди вас в понимании того, о чем он писал. Если вы будете перечитывать это «до полного понимания», то вы просто заполните собственную голову тем… набором неупорядоченных фактов, который в голове у него. Суть статьи я бы изложил гораздо короче:
- При нахождении времени часовом поясе (временной зоне, time zone) A по времени в часовом поясе B удобно пользоваться в качестве посредника универсальным временем, которое одно и то же в любой точке Земли и даже в космосе. Оно называется всемирным координированным временем (Universal Coordinated Time, UTC).
- В Unix и многих языках программирования ряд функций возвращают и принимают в качестве аргумента время UTC не в удобном для человека виде (17:20 5 сентября 2001 года), а в виде, удобном для компьютера — в виде числа секунд, прошедших с условного начала «эпохи Unix» (полночь 1 января 1970 г.) Обычно это целое число, помещающееся в 32-битный регистр микропроцессора. Такое число называют Unix timestamp или просто Unix time.
- UTC отсчитывается атомными часами. Иногда, чтобы привести в соответствие UTС времени, получаемому из астрономических наблюдений, в него вводят так называемые leap seconds (аналогичные leap years — високосным годам), когда последняя минута в году состоит не из 60, а из 61 секунды.
- Для времени, получаемого из астрономических наблюдений (всемирное время, UT), раньше использовалась другая аббревиатура — GMT (Greenwich Mean Time, cреднее гринвичское время). Она является анахронизмом, совершенно не нужна программисту, и ее использование является признаком дурного тона и плохой осведомленности программиста о стандартах времени. Для тех, кто не понимает по-русски: GMT is deprecated.
Это практически всё, что нужно знать программисту. Разглагольствовать о том, когда солнце пересекает меридиан, нелепо, потому что ни автор статьи, ни 98% читающих это не смогут сказать, что такое небесный меридиан.
Вот так вот.
Ну а теперь, собственно, стоит пойти и почитать большой пост от Андрея Светлова о работе с датами из Питона (и различными проблемами): Питон: времена, даты и временные зоны.
Небольшой итог¶
Приведу только “итоги” из записи Андрея:
- Старайтесь работать с абсолютными временами.
- Для общения с внешним миром предпочитайте UTC.
- Если по каким-то причинам вам приходится иметь дело с относительным временем - сокращайте такие моменты до минимума, возвращаясь к абсолютным временам сразу же как только возможно.
- Помните: ввести абсолютные времена на разросшийся и уже массово использующийся проект стоит чрезвычайно дорого.
С точки зрения питона¶
С точки зрения питона, я лично, еще раз взглянул на работу с датами и осознал, что вещи стоит делать немного по-другому. К примеру, получить datetime для 00:00 сегодняшнего дня в UTC с таймзоной –
import datetime
import pytz
today = datetime.datetime.now(pytz.utc).replace(hour=0, minute=0, second=0, microsecond=0)
Note
Зачем мне нужен datetime с 00:00 сегодняшнего дня? Потому что именно так MongoDB хранит даты, в datetime. И отдаёт их так же в datetime. Второй вариант, конечно, – хранить даты в “YYYY-MM-DD”, но мне кажется это глупостью, потому что потом лишний раз преобразовывать придется.
Потом я осознал, что datetime.date не осведомлён о таймзоне (и не может быть, как будто у всех стран следующий день наступает одновременно).
Даты в JavaScript (ну и MongoDB)¶
Warning
Это очень важно! В JavaScript у new Date(y, m, d) второй параметр (месяц) бывает от 0 до 11ти (а не 1-12).
Если в кратце:
// 10 мая (05!) 2011
var d = new Date(Date.UTC(2011, 5-1, 10))
В MongoDB даты хранятся в осведомлённом UTC (aware), потому что рисуются с буквой Z на конце, но pymongo по-умолчанию это игнорирует и при выборке/вставке оперирует с datetime без таймзоны.
Но pymongo.connection.Connection умеет принимать параметр tz_aware=True, тогда, похоже, можно пользоваться aware-датами (я не пробовал, но верю).
Todo
дописать