Дата и время

Для представления времени в компьютере обычно используется так называемое Unix-время (иногда Epoch time, Posix time, seconds since the Epoch, или UNIX Epoch time), которое

Определяется как количество секунд, прошедших с полуночи (00:00:00 UTC) 1 января 1970 года (четверг); этот момент называют «эпохой Unix» (англ. Unix Epoch). Unix-время представлено целым числом, которое увеличивается с каждой прошедшей секундой без необходимости вычислений для определения года, месяца, дня, часа или минуты для удобства восприятия человеком.

Модуль стандартной библиотеки time

Функция time.time модуля time возвращает именно Unix-время, только не в виде целого числа, а в виде float, т.е. доли секунды тоже учитываются. Если доли секунды играют роль, то лучше прибегать к time.time_ns, которое возвращает количество наносекунд с Unix epoch.

Tip

Для измерения промежутков времени (например, замер времени исполнения блока кода) лучше использовать функцию time.perf_counter или time.perf_counter_ns, т.к. она точнее измеряет время. Но важно знать, что возвращаемое время отсчитывается от произвольной точки отсчета, т.е. только разница между двумя значениями имеет смысл.

Функция time.ctime преобразует Unix-время в строку в формате 'Sun Jun 20 23:21:05 1993', т.е. какая дата и какое время было при пройденном количестве секунд с Unix-epoch. Чтобы получить строку в другом формате, можно воспользоваться функцией time.strftime, которая в качестве первого аргумента принимает форматирующую строку. Как составить форматирующую строку можно прочитать здесь и здесь. Ниже приводится не полная таблица с допустимыми директивами в форматирующей строке.

Директива

Значение

Пример

%a

Аббревиатура дня недели

Sun, Mon, …, Sat (en_US)

%A

Полное имя дня недели

Sunday, Monday, …, Saturday (en_US)

%w

Номер дня в неделе, где 0 - воскресенье, 6 — суббота.

0, 1, …, 6

%d

Номер дня в месяце (двузначное десятичное число)

01, 02, …, 31

%b

Аббревиатура месяца

Jan, Feb, …, Dec (en_US);

%B

Полное название месяца.

January, February, …, December (en_US);

%m

Номер месяца (двузначное десятичное число)

01, 02, …, 12

%y

Год без учета века

00, 01, …, 99

%Y

Год с учетом века

0001, 0002, …, 2013, 2014, …, 9998, 9999

%H

Номер часа в 24-часовом формате

00, 01, …, 23

%I

Номер часа в 12-часовом формате

01, 02, …, 12

%p

До полудня или после

AM, PM (en_US);

%M

Номер минуты в часе

00, 01, …, 59

%S

Номер секунды в минуте

00, 01, …, 59

%f

Номер микросекунды в секунде.

000000, 000001, …, 999999

Директивы %a, %A, %b, %B, %p вставляют строковые представления в соответствии с переменными среды. Хотя в стандартной библиотеке есть модуль locale, который позволяет локализовать вывод таких значений методом locale.setlocale (locale.setlocale(locale.LC_ALL, "ru_RU") для русского), рекомендуется использовать для таких целей библиотеку babel (подробнее здесь).

import time


t0 = time.time()
time.sleep(1.0) # in seconds
print(t0)

print(time.ctime(t0))
print(time.ctime())

print(time.localtime(t0))
print(time.localtime())

print(time.strftime(r"%d.%m.%Y %H:%M:%S", time.localtime(t0)))
print(time.strftime(r"%d.%m.%Y %H:%M:%S"))
1654603934.5499537
Tue Jun  7 15:12:14 2022
Tue Jun  7 15:12:15 2022
time.struct_time(tm_year=2022, tm_mon=6, tm_mday=7, tm_hour=15, tm_min=12, tm_sec=14, tm_wday=1, tm_yday=158, tm_isdst=0)
time.struct_time(tm_year=2022, tm_mon=6, tm_mday=7, tm_hour=15, tm_min=12, tm_sec=15, tm_wday=1, tm_yday=158, tm_isdst=0)
07.06.2022 15:12:14
07.06.2022 15:12:15

Модуль стандартной библиотеки datetime

datetime — модуль стандартной библиотеки для работы с датами и временем, который предназначен для работы с временем в разном формате (парсинг и форматирование), а так же предусматривает арифметические операции над моментами во времени.

Основные типы объектов в модуле datetime:

  • datetime.date — дата с аттрибутами year, month и day;

  • datetime.time — время с аттрибутами hour, minute, second, microsecond;

  • datetime.datetime — комбинация datetime.date и datetime.time (и те и те аттрибуты);

  • datetime.timedelta — промежуток времени между двумя datetime.datetime (или просто time и date);

  • datetime.tzinfo и datetime.timezone — информация о часовом поясе.

Note

Объекты datetime и time опционально могут содержать информацию о часовом поясе. Тогда их называют aware. Объекты без информации о часовом поясе называют naive. Здесь будут рассматриваться только naive объекты.

Кроме того, что объекты классов date, time, datetime можно сразу печатать функцией print (по умолчанию YYYY-MM-DD), они ещё имеют метод strftime, который работает аналогично функции из модуля time.

Note

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

import datetime

datetime.date

datetime.date — объект с атрибутами

  • year \(\in \{1, 2, ..., 9999\}\) — год (2021, например);

  • month \(\in \{1, 2, ..., 12\}\) — месяц;

  • day \(\in \{1, 2, ..., N\}\) — номер дня в месяце (\(N\) — количество дней в месяце);

Создать объект datetime.date можно несколькими способами.

Например, явно вызвав конструктор.

the_date = datetime.date(year=1963, month=4, day=12)
the_date_formatted = the_date.strftime("%m/%d/%Y")
print(f"Первый полёт в космос: {the_date_formatted}")
Первый полёт в космос: 04/12/1963

Из Unix-времени методом datetime.date.fromtimestamp.

unix_epoch = datetime.date.fromtimestamp(0)
print(f"Unix-epoch соответствует дате {unix_epoch}.")
Unix-epoch соответствует дате 1970-01-01.

Или методом datetime.date.today, если нужна дата на сегодня (согласно часам компьютера).

Note

Этот метод аналогичен datetime.date.fromtimestamp(time.time()).

today = datetime.date.today()
print(today)
print(f"День: {today.day}, месяц: {today.month}, год {today.year}")
print(f"{today.weekday() + 1} день недели: {today.strftime('%A')}")
2022-06-07
День: 7, месяц: 6, год 2022
2 день недели: Tuesday

datetime.time

datetime.time — объект для хранения времени внутри суток, т.е. он имеет поля

  • hour \(\in \{0, 1, ..., 23\}\) — час,

  • minute \(\in \{0, 1, ..., 59\}\) — минута,

  • second \(\in \{0, 1, ..., 59\}\) — секунда,

  • microsecond \(\in \{0, 1, ..., 999999\}\) — микросекунда,

  • fold \(\in [0, 1]\) — параметр, который используется для разрешения противоречий, если есть данные о часовом поясе (переводы часов назад и так далее).

the_time = datetime.time(hour=9, minute=7) 
the_time_formatted = the_time.strftime("%H:%M")
print(f"Старт корабля Восток 1 состоялся в {the_time_formatted} по Московскому времени.")
Старт корабля Восток 1 состоялся в 09:07 по Московскому времени.

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

datetime.datetime

datetime.datetime — по сути дела комбинация предыдущих двух типов datetime.date и datetime.time, т.е. имеет аттрибуты и того и того:

  • year;

  • month;

  • day;

  • hour;

  • minute;

  • second;

  • microsecond;

  • fold;

Конструктор datetime.datetime в качестве входных параметров принимает параметры обоих классов datetime.date и datetime.time. Также, можно предварительно создать объекты datetime.date и datetime.time и объединить их в datetime.datetime методом combine.

olympics = datetime.datetime(year=1980, month=7, day=19, hour=16)
print(olympics.strftime("Церемония открытия московской олимпиады началась %d.%m.%Y в %H:%M."))

the_date_the_time = datetime.datetime.combine(the_date, the_time)
print(the_date_the_time.strftime("Старт корабля Восток 1 состоялся %d/%m/%y в %H:%M по Московскому времени."))
Церемония открытия московской олимпиады началась 19.07.1980 в 16:00.
Старт корабля Восток 1 состоялся 12/04/63 в 09:07 по Московскому времени.

Также есть и метод fromtimestamp, который в данном случае учитывает часовой пояс системы, т.е. по количеству секунд, прошедших с полуночи по Гринвичу 1 января 1970 года, он возвращает дату и время, которые были в это время в этом часовом поясе. Метод utcfromtimestamp делает то же самое, но не учитывая часовой пояс.

unix_epoch_moscow = datetime.datetime.fromtimestamp(0)
print(unix_epoch_moscow.strftime("Мировое Unix-время отсчитывается с %H:%M по московскому времени"))

unix_epoch_UTC = datetime.datetime.utcfromtimestamp(0)
print(unix_epoch_UTC.strftime("Мировое Unix-время отсчитывается с %H:%M UTC"))
Мировое Unix-время отсчитывается с 03:00 по московскому времени
Мировое Unix-время отсчитывается с 00:00 UTC

Но чаще всего datetime.datetime образуются методом datetime.datetime.strptime из текстового представления. Формате текстового представления для времени разный для разных стран/учреждений/организаций и т.д. Например, где-то 26 января записывается в виде “26.01”, а где-то в “01/26”. Чтобы модуль datetime смог разобраться, где в строке расположены значения, соответствующие номеру месяца, а где номеру дня, году, времени и т.п., необходимо передать в метод strptime форматирующую строку в качестве второго аргумента.

t1 = "14 March 1879"
t2 = "2019-05-18T15:17:08.132263"
t3 = "11/12/63"


print(datetime.datetime.strptime(t1, "%d %B %Y"))
print(datetime.datetime.strptime(t2, "%Y-%m-%dT%H:%M:%S.%f"))
print(datetime.datetime.strptime(t3, "%m/%d/%y"))
1879-03-14 00:00:00
2019-05-18 15:17:08.132263
2063-11-12 00:00:00

Разберем примеры выше:

  1. В строковом представлении t1 идут сначала две цифры, обозначающие день, т.е. используем директива %d. Затем идёт полное название месяца (директива %B, а затем идёт год с указанием всех 4 цифр (директива %Y)). Т.к. все эти элементы разделены пробелом, то форматирующая строка равна "%d %B %Y";

  2. t1 соответствует iso-формату. Полный год (%Y), месяц (%m), день %d, символ "T", час (%H), минута (%M), секунда (%S), микро секунда (%f). Месяц отделяется от дня и год символом дефиса "-", минута отделяется от часа и секунды символом двоеточия ("-"), и микросекунда отделяется точкой перед ней. Комбинируя это всё, получаем форматирующую строку "%Y-%m-%dT%H:%M:%S.%f"

  3. Дата разделенная символами /, в годе не указан век. Подразумевается, что дата представлена в формате, используемом в USA, т.е. сначала идёт месяц, а затем день (день убийства Кеннеди). Хочется использоваться форматирующую строку "11/12/63", но она возвращает 2063 год. Т.к. такая строка не содержит достаточно информации, чтобы определить правильный век, то её необходимо обрабатывать отдельно.

timedelta

Объекты datetime.timedelta позволяют производить арифметику с объектами datetime.datetime, datetime.date и datetime.time. Пусть t — объект datetime или его аналог, а dt — объект deltatime, тогда определенны следующие операции:

  • dt = t2 - t1;

  • t2 = t1 \(\pm\) dt;

  • t1 < t2;

  • др.

today - datetime.date(2000, 1, 1)
datetime.timedelta(days=8193)

Между сегодня и началом века прошло 8005 дней.

a_day = datetime.timedelta(days=1)
yesterday = today - a_day
tomorrow = today + a_day
print(f"Вчера: {yesterday},\nСегодня: {today},\nЗавтра: {tomorrow}")
print(yesterday < today)
Вчера: 2022-06-06,
Сегодня: 2022-06-07,
Завтра: 2022-06-08
True
dt = datetime.datetime.now() - datetime.datetime.fromtimestamp(t0)
dt
datetime.timedelta(seconds=1, microseconds=187450)

pandas.DateTime

Для работы со временем pandas опирается на типы данных np.datetime64 и np.timedelta64 из библиотеки NumPy. Например, сделать pd.Series типа datetime64[ns] можно методом pd.to_datetime.

import pandas as pd

t = ["2022-1-1", "2022-1-2"]
t = pd.to_datetime(t)
t
DatetimeIndex(['2022-01-01', '2022-01-02'], dtype='datetime64[ns]', freq=None)

Если аргумент метода pd_todatetime — список, то создаётся объект pd.DateTimeIndex, а если pd.Series, то создаётся другая pd.Series типа datetime64[ns].

По умолчанию pandas парсит в формате "%Y-%m-%d", но если данные представлены в другом формате, то можно его указать параметром format.

t = pd.Series(["12:00 1.1.22", "11:00 2.1.22"])
t = pd.to_datetime(t, format="%H:%M %d.%m.%y")
t
0   2022-01-01 12:00:00
1   2022-01-02 11:00:00
dtype: datetime64[ns]

Иногда необходимо задавать точки времени с определенной периодичностью. Для это удобно использовать функцию pd.date_range.

datetimerange = pd.date_range(start=t.iloc[0], end=t.iloc[1], freq="H")
datetimerange
DatetimeIndex(['2022-01-01 12:00:00', '2022-01-01 13:00:00',
               '2022-01-01 14:00:00', '2022-01-01 15:00:00',
               '2022-01-01 16:00:00', '2022-01-01 17:00:00',
               '2022-01-01 18:00:00', '2022-01-01 19:00:00',
               '2022-01-01 20:00:00', '2022-01-01 21:00:00',
               '2022-01-01 22:00:00', '2022-01-01 23:00:00',
               '2022-01-02 00:00:00', '2022-01-02 01:00:00',
               '2022-01-02 02:00:00', '2022-01-02 03:00:00',
               '2022-01-02 04:00:00', '2022-01-02 05:00:00',
               '2022-01-02 06:00:00', '2022-01-02 07:00:00',
               '2022-01-02 08:00:00', '2022-01-02 09:00:00',
               '2022-01-02 10:00:00', '2022-01-02 11:00:00'],
              dtype='datetime64[ns]', freq='H')