Работа с timestamp (меткой времени) в Python
Timestamp – временная метка (штамп времени, печать времени) – информация о моменте, когда произошло то или иное событие. Так, например, используя timestamp, можно устанавливать и определять время создания и модификации файлов.
Часто timestamp выражают в секундах, прошедших с начала "эпохи Unix", то есть с полуночи 1 января 1970 года по UTC.
Время, выраженное в секундах с начала эпохи Unix, называется также Unix-временем, или POSIX-временем. Такой способ описания моментов бывает удобнее, когда дату и время не требуется представлять в удобном для восприятия человека виде.
В Python для работы с меткой времени предназначен ряд функций из модуля time
и несколько методов из datetime
.
Что касается модуля time
, то многие его функции являются "интерфейсом" к функциям системной библиотеки на языке Си. Поэтому могут быть доступны не во всех операционных системах или работают там иначе.
Сам модуль time
в документации Python относится к группе модулей, представляющих доступ к общим службам операционной системы. В данном случае речь идет о системных часах.
Узнать количество секунд, прошедших с начала эпохи Unix до настоящего момента, можно с помощью одноименной модулю функции time()
, которая не принимает никаких аргументов:
>>> import time >>> t = time.time() >>> print(t) 1686494808.3473074 >>> type(t) <class 'float'>
В данном случае метка времени привязана к системным часам и для нее не существует часового пояса. Если между двумя вызовами time()
мы переведем часы назад, то получим меньшее значение, чем предыдущее. Другими словами, системная метка времени может не совпадать с истинным timestamp этого момента – временем по UTC.
Поэтому в модуле предусмотрены две функции – localtime
и gmtime
– для конвертации секунд либо в локальное время, либо по UTC. В примере ниже им передается текущий момент времени, что не обязательно: можно передать любой, выраженный в секундах. Кроме того, если речь идет о текущем моменте, аргумент можно не передавать.
import time t = time.time() utc = time.gmtime(t) local = time.localtime(t) print(utc) print(local) print(time.asctime(utc)) print(time.asctime(local))
Пример выполнения скрипта на компьютере, часовой пояс которого смещено от UTC на +3 часа (функция asctime
используется для вывода объектов struct_time
в удобочитаемом виде):
time.struct_time(tm_year=2023, tm_mon=6, tm_mday=13, tm_hour=14, tm_min=56, tm_sec=16, tm_wday=1, tm_yday=164, tm_isdst=0) time.struct_time(tm_year=2023, tm_mon=6, tm_mday=13, tm_hour=17, tm_min=56, tm_sec=16, tm_wday=1, tm_yday=164, tm_isdst=0) Tue Jun 13 14:56:16 2023 Tue Jun 13 17:56:16 2023
В качестве результата функции gmtime
и localtime
возвращают экземпляры struct_time
, которые также не хранят информацию о часовом поясе. Если требуется узнать величину смещения времени на локальном компьютере от UTC, найти его можно в константах altzone
и timezone
. Смещение здесь выражено в секундах и отрицательно для "плюсовых" часовых поясов (с востока от UTC).
>>> import time >>> time.altzone -10800 >>> time.timezone -10800 >>> time.tzname ('MSK', 'MSK') >>> time.daylight 0
Объекты struct_time
в основном используются в рамках работы с модулем time
. Если речь идет о преобразовании timestamp в обычное представление даты, следует использовать методы fromtimestamp
, utcfromtimestamp
экземпляров datetime
(первый метод есть также у date
).
import time from datetime import datetime, timedelta, timezone t = time.time() local = datetime.fromtimestamp(t) utc = datetime.utcfromtimestamp(t) print(local) print(utc) zone = timezone(timedelta(seconds=abs(time.timezone))) local = datetime.fromtimestamp(t, zone) print(local)
2023-06-13 18:54:59.968364 2023-06-13 15:54:59.968364 2023-06-13 18:54:59.968364+03:00
Метод utcfromtimestamp
выполняет вычисление, какое было время по UTC, когда в это же время была взята метка на локальном компьютере. При этом в отличие от fromtimestamp
этот метод не дает возможности получить осведомленное время, передав второй аргумент. При необходимости превращения наивного времени в осведомленное без пересчета следует использовать метод replace
.
import time from datetime import datetime, timezone utc = datetime.utcfromtimestamp(time.time()) utc = utc.replace(tzinfo=timezone.utc) print(utc)
2023-06-13 16:23:24.222934+00:00
Для выполнения обратного действия – преобразования имеющейся даты в метку времени – у экземпляров datetime
есть метод timestamp
. Конвертация происходит в локальное время, но с учетом часового пояса даты, если передается время осведомленное.
from datetime import datetime dm = datetime.strptime('19:00+0300 MSK 21-06-2023', '%H:%M%z %Z %d-%m-%Y') du = datetime.strptime('19:00+0000 UTC 21-06-2023', '%H:%M%z %Z %d-%m-%Y') print(dm.timestamp()) # 1687363200.0 print(du.timestamp()) # 1687374000.0
В примере выше речь идет о разных моментах времени. Временная метка второго больше, чем у первого, то есть второе событие произошло позже. Ведь в это время в Москве было уже 22 часа. Если метод timestamp
вызывать на наивные дату-время, то метка будет вычислена по системным часам.
В рамках модуля time
перевод в секунды выполняется над экземплярами struct_time
. Делается это с помощью функции mktime
.
import time t = time.time() print(t) local = time.localtime(t) utc = time.gmtime(t) print(time.asctime(local)) print(time.asctime(utc)) print(time.mktime(local)) print(time.mktime(utc))
1686675715.9385638 Tue Jun 13 20:01:55 2023 Tue Jun 13 17:01:55 2023 1686675715.0 1686664915.0
Функция mktime
всегда переводит struct_time
в локальное время, так как последний не хранит смещение. В примере выше, получив два разных struct_time
одного момента времени и возвращая их обратно в формат timestamp мы получаем уже два разных момента времени. Исходные 17 часов по UTC считаются уже как 17 часов по Москве, что меньше метки времени, соответствующей 20-ти часам.
Поэтому в программе вам самостоятельно надо "запоминать", из локального времени был получен struct_time
или из UTC и во втором случае вместо mktime
использовать функцию timegm
из модуля calendar
.
import time import calendar local = time.localtime() utc = time.gmtime() print(time.asctime(local)) print(time.asctime(utc)) print(time.mktime(local)) print(calendar.timegm(utc))
Tue Jun 13 20:14:57 2023 Tue Jun 13 17:14:57 2023 1686676497.0 1686676497
Отметим, что классы date
и datetime
имеют методы timetuple
, а класс datetime
также utctimetuple
, которые переводят дату в struct_time
. Однако и здесь информация о часовом поясе не сохраняется.
import time from datetime import datetime, timedelta, timezone import calendar moscow = timezone(timedelta(hours=3)) # Это одинаковые моменты времени dm = datetime(2023, 6, 11, 19, tzinfo=moscow) du = datetime(2023, 6, 11, 16, tzinfo=timezone.utc) print(dm == du) # True # Это разные struct_time dtm = dm.timetuple() dtu = du.utctimetuple() print(dtm == dtu) # False dtm_stamp = time.mktime(dtm) # Здесь мы помним, что dtu - это момент времени в UTC, # а нам надо получить, сколько в это время было в нашей локали dtu_stamp = calendar.timegm(dtu) print(dtm_stamp == dtu_stamp) # True