Работа с 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

Даты и время в Python. Курс




Все разделы сайта