Работа с датами, временем и временными промежутками.

Работа с датами и временем в любом языке программирования — довольно скользкая штука. Все эти хитрые високосные года, часовые пояса и т.п. создают бесконечное количество сложностей для программистов. В питоне для работы с этими объектами в модуле datetime реализованы классы

time

Время — это самый «беспомощный» тип. Создать экземпляр времени можно при помощи команды вида time(hour=0, minute=0, second=0, microsecond=0), при этом можно указывать любой набор параметров с исключительно разумными целыми значениями (25 часов или -3 минуты использовать нельзя).

Класс time поддерживает следующие операции:

ОперацияСмыслПример
t.hour
t.minute
t.second
t.microsecond
Количество часов, минут, секунд и микросекунд во времени
time(13, 45).minute
# 45
t.replace(...)   
Создать новую отметку времени, у которой отдельные значения заменены.
time(13, 45, 13).replace(minute=0, second=0)
# datetime.time(13, 0)   
t.isoformat()   
Время в формате ISO 8601 (HH:MM:SS или HH:MM:SS.ffffff)
time(13, 45, 13).isoformat()
# '13:45:13'   
t.strftime()   
Вывод форматированного времени (подробности ниже)
time(13, 45, 13).strftime('%H.%M.%S')
# '13.45.13'   

Отметки времени нельзя вычитать и выполнять с ними какую-либо арифметику.

date

date — это даты в предположении, что григорианский календарь действовал всегда.

Создать экземпляр даты можно при помощи команды вида date(year, month, day), при этом все параметры обязательны, и должны быть разумным целым числом. -3 месяца или 40 дней не предлагать.

Класс date поддерживает следующие операции:

ОперацияСмыслПример
d.year
d.month
d.day
Отдельные части даты
date(2020, 2, 29).day
# 29
date.today()   
Текущая дата
date.today()
# datetime.date(2019, 1, 3)   
date.fromordinal(ordinal)
d.toordinal()
Сделать дату из номера дня, начиная с 0001-01-01, и наоборот.
date(2019,1,3).toordinal()
# 737062
date.fromordinal(365)
# datetime.date(1, 12, 31)   
d.replace(...)   
Заменить часть даты
date(2019, 1, 3).replace(year=2022)
# datetime.date(2022, 1, 3)   
d.weekday()
d.isoweekday()
Номер дня недели, пн=0, ..., вс=6 для weekday и пн=1, ..., вс=7 для isoweekday
date(2019, 1, 3).isoweekday()
# 4   
d.isoformat()   
Вывести дату в формате ISO 8601 ‘YYYY-MM-DD’
date(2019, 1, 3).isoformat()
# '2019-01-03'   
d.strftime(...)   
Вывод форматированной даты (подробности ниже)
date(2019, 1, 3).strftime("%d/%m/%y")
# '03/01/19'
date(2019, 1, 3).strftime("%A %d. %B %Y")
# 'Thursday 03. January 2019'
import locale
locale.setlocale(locale.LC_ALL, 'ru_RU.UTF-8')
date(2019, 1, 3).strftime("%A %d. %B %Y")
# 'четверг 03. Январь 2019'

datetime

datetime — это класс, позволяющий работать с датой-временем (timestamp'ом). Создать экземпляр даты-времени можно при помощи команды вида datetime(year, month, day, hour=0, minute=0, second=0, microsecond=0), при этом год, месяц и день — обязательные параметры. Если параметр передан, то он должен быть разумным целым числом.

Класс datetime поддерживает следующие операции:

ОперацияСмыслПример
dt.year, dt.month, dt.day
dt.hour, dt.minute, dt.second,
dt.microsecond
Отдельные части даты-времени
datetime(2019, 1, 3, 22, 35).hour
# 22   
datetime.now()   
Текущее дата-время
datetime.now()
# datetime.datetime(2019, 1, 3, 22, 36, 42, 180165)   
datetime.fromordinal(ordinal)
dt.toordinal()
Сделать дату из номера дня, начиная с 0001-01-01, и наоборот.
datetime(2019,1,3).toordinal()
# 737062
datetime.fromordinal(365)
# datetime.datetime(1, 12, 31, 0, 0)   
dt.replace(...)   
Заменить часть даты-времени
datetime.now().replace(minute=0, second=0, microsecond=0)
# datetime.datetime(2019, 1, 3, 22, 0)   
datetime.combine(date, time)   
Собрать дату-время из даты и времени
datetime.combine(date(2019, 1, 3), time(22, 0, 0))
# datetime.datetime(2019, 1, 3, 22, 0)   
dt.date()
dt.time()
Выдернуть только дату или только время
datetime.now().date()
# datetime.date(2019, 1, 3)   
dt.timestamp()
datetime.fromtimestamp(ts)
Преобразовать в/из POSIX- (или UNIX-) время (количество секунд, прошедших с полуночи (00:00:00 UTC) 1 января 1970 года)
datetime.now().timestamp()
# 1546544800.385689  
dt.weekday()
dt.isoweekday()
Номер дня недели, пн=0, ..., вс=6 для weekday и пн=1, ..., вс=7 для isoweekday
datetime(2019, 1, 3).isoweekday()
# 4   
dt.isoformat()   
Текстовое представление в ISO 8601 формате, YYYY-MM-DDTHH:MM:SS.ffffff
datetime.now().isoformat()
# '2019-01-03T22:48:22.375209'   
d.strftime(...)   
Вывод форматированной даты (подробности ниже)
datetime(2019, 1, 3).strftime("%d/%m/%y")
# '03/01/19'
datetime.now().strftime("%A, %d. %B %Y %I:%M%p")
# 'Thursday, 03. January 2019 10:56PM'
import locale
locale.setlocale(locale.LC_ALL, 'ru_RU.UTF-8')
datetime(2019, 1, 3).strftime("%A %d. %B %Y")
# 'четверг 03. Январь 2019'
datetime.strptime(...)   
Собрать дату из текстовой строки с указанием формата (подробности ниже)
datetime.strptime("2009-11-12 23:18:53",
                  "%Y-%m-%d %H:%M:%S" )
# datetime.datetime(2009, 11, 12, 23, 18, 53)
datetime.strptime("21/11/06 16:30", "%d/%m/%y %H:%M")
# datetime.datetime(2006, 11, 21, 16, 30)
import locale
locale.setlocale(locale.LC_ALL, 'ru_RU.UTF-8')
datetime.strptime('Понедельник 10 Декабрь 2018г.',
                  '%A %d %B %Yг.')
# datetime.datetime(2018, 12, 10, 0, 0)   

Даты можно сравнивать. Разность двух дат-времён — это timedelta.

timedelta

Объект типа timedelta возникает при вычитании двух дат или дат-времён. Также промежуток может быть задана явно: timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0). Можно указывать любой набор параметров с любыми разумными целыми и действительными значениями. Внутри хранятся только дни, секунды и микросекунды, поэтому после создания промежутки времени «нормализуются» так, чтобы использовать только эти значения. Поэтому timedelta(microseconds=-1) содержит -1 день, 86399 секунд и 999999 микросекунд.

Класс timedelta поддерживает следующие операции:

ОперацияСмыслПример
dt1 + dt2
dt1 — dt2
Сумма и разность промежутков
timedelta(days=3) + timedelta(seconds=3600)
# datetime.timedelta(days=3, seconds=3600)
timedelta(days=3) — timedelta(seconds=3600)
# datetime.timedelta(days=2, seconds=82800)
dt * i
i * dt
dt * f
f * dt
Умножение на целое или действительное число При умножении на действительное может возникнуть округление. Вообще говоря, можно промежутки делить на числа, а также целочисленно делить на числа
timedelta(hours=1) * 49
# datetime.timedelta(days=2, seconds=3600)
(3/7) * timedelta(weeks=1)
# datetime.timedelta(days=3)
abs(dt)
Промежуток по модулю
abs(timedelta(hours=-1))
# datetime.timedelta(seconds=3600)
dt1 / dt2
Отношение промежутков
timedelta(weeks=1) / timedelta(hours=12)
# 14.0
t + dt
d + dt
Прибавление к дате или дате-времени промежутка При прибавлении к дате неполные сутки отбрасываются.
datetime(2019, 1, 1) + timedelta(days=365//2)
# datetime.datetime(2019, 7, 2, 0, 0)
date(2019, 1, 1) + timedelta(hours=47)
# datetime.date(2019, 1, 2)
dt.total_seconds()
Полное время промежутка в секундах. Если промежуток больше 270 лет, то микросекунды отбрасываются
(date(2019,1,1) — date(2009,1,1)).total_seconds()
# 315532800.0
d1 — d2
t1 — t2
Разность дат или дат-времён — это timedelta
date(2019,9,1) — date(2020,2,29)
# datetime.timedelta(days=-181)

strftime и strptime

Функция datetime.strptime и метод dt.strftime позволяют собрать дату-время из текстовой строки или форматировать дату-время в соответствии с шаблоном. При этом часть шаблонов зависят от локали, которую можно выставить при помощи команд в духе

import locale
locale.setlocale(locale.LC_ALL, 'ru_RU.UTF-8')
ФорматЗначениеПример
%aСокращенное название дня неделиSun, Mon, …, Sat (en_US);
Пн, Вт, ..., Вс (ru_RU)
%AПолное название дня неделиSunday, Monday, …, Saturday (en_US);
понедельник, ..., воскресенье (ru_RU)
%wНомер дня недели [0(Sunday),6]0, 1, …, 6 (0=воскресенье, 6=суббота)
%dДень месяца [01,31]01, 02, …, 31
%bСокращенное название месяцаJan, Feb, …, Dec (en_US);
янв, ..., дек (ru_RU)
%BПолное название месяцаJanuary, February, …, December (en_US);
Январь, ..., Декабрь (ru_RU)
%mНомер месяца [01,12]01, 02, …, 12
%yГод без века [00,99]00, 01, …, 99
%YГод с веком0001, 0002, …, 2013, 2014, …, 9999
В linux'е год выводится без ведущих нулей!
1, 2, …, 2013, 2014, …, 9999
%HЧас (24-часовой формат) [00,23]00, 01, …, 23
%IЧас (12-часовой формат) [01,12]01, 02, …, 12
%pДо полудня или после (при 12-часовом формате)AM, PM (en_US);
%MЧисло минут [00,59]00, 01, …, 59
%SЧисло секунд [00,59]00, 01, …, 59
%fЧисло микросекунд000000, 000001, …, 999999
%zРазница с UTC в формате ±HHMM[SS[.ffffff]] +0000, -0400, +1030, +063415, -030712.345216
%ZВременная зонаUTC, EST, CST
%jДень года [001,366]001, 002, …, 366
%UНомер недели в году (нулевая неделя начинается с воскресенья) [00,53]00, 01, …, 53
%WНомер недели в году (нулевая неделя начинается с понедельника) [00,53]00, 01, …, 53
%cДата и время в текущей локалиTue Aug 16 21:30:00 1988 (en_US);
03.01.2019 23:18:32 (ru_RU)
%xДата в текущей локали08/16/88 (None); 08/16/1988 (en_US);
03.01.2019 (ru_RU)
%XВремя в текущей локали21:30:00
%%Знак '%'
datetime.now().strftime('%a %A %w %d %b %B %m %y %Y %H %I %p %M %S %f %z %Z %j %U %W %c %x %X')
# 'Чт четверг 4 03 янв Январь 01 19 2019 23 11  27 56 008026   003 00 00 03.01.2019 23:27:56 03.01.2019 23:27:56'
# 'Thu Thursday 4 03 Jan January 01 19 2019 23 11 PM 28 16 558667   003 00 00 Thu Jan  3 23:28:16 2019 01/03/19 23:28:16'

datetime.strptime('Из удивительных 19 вещей январь можно собрать 02 дату',
                  'Из удивительных %y вещей %B можно собрать %d дату')
# datetime.datetime(2019, 1, 2, 0, 0)

A: День года

По заданному числу n от 1 до 365 определите, на какое число какого месяца приходится день невисокосного года с номером n. Программа получает на вход целое число n и должна вывести число месяца (от 1 до 31) и название месяца, на которое приходится данный день.

month_names = ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']

1
January 1
365
December 31
IDE

B: Обратная задача

Решите обратную предыдущей задачу: по записи названия месяца и дня определите номер дня в году.
Алгоритм решения задачи оформите в виде отдельной функции.

January 1
1
December 31
365
IDE

C: Перевод формата даты — 1

Дата задана в формате dd.mm.yyyy. Выведите ее в формате "Month d, y", где Month — английское название месяца, d — номер дня в месяце, без лидирующих нулей, y — номер года без лидирующих нулей.

12.10.2008
October 12, 2008
01.01.0001
January 1, 1
IDE

D: Перевод формата даты — 2

Решите обратную задачу.

October 12, 2008
12.10.2008
January 1, 1
01.01.0001
IDE

E: Tomorrow

Дата задана в формате dd.mm.yyyy. Выведите дату, следующую за ней в том же формате. Учтите правило формирования високосных годов. Следует считать, что григорианский календарь действовал всегда.

12.10.2008
13.10.2008
31.12.2008
01.01.2009
IDE

F: Yesterday

Дата задана в формате dd.mm.yyyy. Выведите дату, предшестующую ей в том же формате. Учтите правило формирования високосных годов. Следует считать, что григорианский календарь действовал всегда.

13.10.2008
12.10.2008
01.01.2009
31.12.2008
IDE

G: Разница дат

Две даты заданы в формате dd.mm.yyyy, каждая дата — в новой строке. Определите количество дней между этими датами. Вторая дата больше первой.

01.01.0001 02.01.0001
1
29.02.2004 01.03.2005
366
IDE

H: День недели

Дата задана в формате dd.mm.yyyy. Выведите название дня недели, на который приходится эта дата.

12.10.2008
Sunday
13.10.2008
Monday
IDE

I: День рождения

Задан день и месяц рождения в формате dd.mm. Задана текущая дата в формате dd.mm.yyyy. Определите, сколько дней осталось до дня рождения. Если сегодня — день рождения, то необходимо вывести 0.

19.04 19.04.2002
0
05.05 19.04.2002
16
29.02 28.02.2001
1096
IDE

J: Номер дня по дате

Пронумеруем все даты подряд, считая, что 01.01.0001 имеет номер 1, 02.01.0001 — номер 2 и т.д. По заданной дате определите ее порядковый номер.

Программа получает на вход число дат \(N\le 200000\), затем заданы \(N\) дат. Для каждой из них выведите ее порядковый номер.

2 01.01.0001 01.01.0002
1 366
IDE

K: Дата по номеру дня

Решите обратную задачу: определите дату по номеру дня.

2 1 366
01.01.0001 01.01.0002
IDE

L: Разница дат — 2

Научитесь быстро вычислять разницу между двумя датами.

Программа получает на вход число N, не превосходящее 105. Далее идет 2N дат, при этом не гарантируется, что первая дата меньше второй.

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

2 01.01.0001 02.01.0001 01.03.2005 29.02.2004
1 -366
IDE

Время

M: Количество секунд

Часы показывают время в формате hh:mm:ss. Определите количество секунд, которое прошло с начала суток.

00:01:01
61
IDE

N: Таймер

Часы показывают время в формате hh:mm:ss. На этих часах запустили таймер, который прозвенит через n секунд. Определите время, которое будет на часах, когда прозвенит таймер. Число n может принимать значения от 0 до 109.

09:00:00 90
09:01:30
23:59:59 1
00:00:00
IDE

O: Разница времен

Профессор лег спать, когда на часах было время h1:m1:s1, а когда он проснулся было время h2:m2:s2. Определите, сколько времени спал профессор, если известно, что он проспал не более суток. Время выведите в формате hh:mm:ss.

14:00:00 21:10:30
07:10:30
22:00:00 07:00:00
09:00:00
IDE

P: Будильники

Будильник в сотовом телефоне можно настроить так, чтобы он звонил каждый день в одно и то же время, либо в указанное время в определенный день недели. Независимо можно настроить несколько будильников.

По информации о будильниках и текущему времени и дню недели определите, когда прозвонит очередной будильник.

В первой строке вводится текущий день недели (число от 1 до 7), затем через пробел, текущее время в формате HH:MM.

Во второй строке вводится одно натуральное число N, не превосходящее 100 – количество будильников.

В следующих N строках вводится описание N будильников в таком же формате. Значение дня недели, равное 0, означает, что будильник звонит каждый день.

Выведите номер дня недели и время, когда будильник зазвонит в следующий раз, в таком же формате.

2 10:20 2 1 23:15 0 10:10
3 10:10
7 01:01 3 7 00:59 7 23:59 7 01:01
7 01:01
IDE