Даты

В этом листке вам нужно будет решать задачи, связанные с датами и временем, поэтому прежде всего нужно понять, как хранить даты и время в программе. Конечно, лучше всего для типа данных “Дата” создать класс.

class Date:

У объектов этого класса будет три поля: day, month, year.

Подумайте, какие операции можно выполнять с датами. Например, можно преобразовывать даты в строки для удобного вывода или, наоборот, преобразовывать строки в даты. К датам можно прибавлять (вычитать) целые числа (например, прибавить число 1 — это получить следующую дату). Наконец, можно брать разность двух дат (это количество дней между ними).

Для преобразования номера месяца в строку лучше всего использовать список строк-названий:

MonthNames = ('', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December')
Для вывода названия дней недели также лучше использовать список:
DayOfTheWeek = ('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday')

Данные списки лучше всего сделать глобальными и объявить в самом начале программы.

А вот для преобразования строки-названия месяца в число лучше использовать словарь:

MonthNums = {'January': 1, 'February': 2, 'March': 3, ...}

Инкапсуляция

Если имя поля начинается с двух символов подчеркивания, например, __day, __month, __year, то эти поля являются “скрытыми”, то есть доступ к ним можно получить только из методов класса. Для работы с этими полями нужно использовать методы класса: конструкторы, метод __str__, перегруженные операции и т.д. При необходимости явно получить или изменить значение поля пишутся специальные методы типа get_day или set_day.

Слоты классов

Если специальному атрибуту класса с именем __slots__ присвоить значение, в виде списка из нескольких текстовых строк, то только перечисленные в данном списке имена могут использоваться в качестве имен полей (атрибутов) классов. Например:

class Date:
    __slots__ = ['__day', '__month', '__year']

Это также приведет к уменьшению времени обращения к атрибутам класса за счет другого способа хранения атрибутов в экземпляре класса.

Упражнения

A: День года

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

Для определения номера дня в году лучше всего использовать константный массив количества дней в каждом месяце:

Months = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
Ввод Вывод
1
January 1
365
December 31

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

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

Ввод Вывод
January 1
1
December 31
365

Проектирование класса Date

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

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

Требования к реализации. Решение должно быть оформлено в виде класса Date. Класс должен содержать конструктор, параметром которого является строка вида  dd.mm.yyyy. Класс должен содержать метод text(), возвращающий представление даты в требуемом формате.

Сдайте на проверку только класс.

Код Вывод
print(Date("12.10.2008").text())
October 12, 2008
print(Date("01.01.0001").text())
January 1, 1

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

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

Требования к реализации. Конструктор класса принимает на вход строку, которая может быть одного из двух возможных форматов, конструктор сам распознаёт формат строки.

Для вывода даты реализуйте метод __str__.

Для форматирования вывода удобно использовать f-строки.

Код Вывод
print(Date("October 12, 2008"))
12.10.2008
print(Date("January 1, 1"))
01.01.0001
print(Date("03.12.2019"))
03.12.2019

E: Сравнение дат

Реализуйте операторы сравнения для дат: ==, !=, <, <=, >, >=.

F: Следующая дата

Реализуйте для класса Date метод next(), который возвращает следующую дату для данной. Метод не должен изменять экземпляр класса.

Код Вывод
print(Date('07.12.2019').next())
08.12.2019
print(Date('31.12.2019').next())
01.01.2020

G: Предыдущая дата

Реализуйте для класса Date метод prev(), который возвращает предыдущую дату для данной. Метод не должен изменять экземпляр класса.

Код Вывод
print(Date('07.12.2019').prev())
06.12.2019
print(Date('01.01.2020').prev())
31.12.2019

H: Разность дат

Определите метод __sub__ для вычисления разности дат. Метод должен возвращать значение типа int. Можно считать, что левый операнд не меньше правого.

Код Вывод
print(Date('02.01.0001') - Date('01.01.0001'))
1
print(Date('01.03.2020') - Date('01.03.2019'))
366

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

Программа получает на вход дату. Выведите название дня недели, на который приходится эта дата. Считайте, что григорианский календарь действовал всегда, начиная с 1 года н.э. (так называемый «пролептический григорианский календарь»).

Алгоритм решения задачи оформите в виде отдельного метода. На проверку нужно сдать программу целиком.

Ввод Вывод
07.12.2019
Saturday

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

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

Для этого реализуйте метод __int__, вызываемый при преобразовании объекта к типу int.

Решение не должно содержать цикла для последовательного увеличения номера даты.

Код Вывод
print(int(Date('01.01.0001')))
1
print(int(Date('01.01.0002')))
366

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

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

Решение оформите в виде конструктора, принимающего в качестве параметра значение типа int. Все предыдущие возможности конструктора должны быть сохранены.

Код Вывод
print(Date(1))
01.01.0001
print(Date(366))
01.01.0002

L: Арифметические операции с датами

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

Должны выполняться следующие операции:

Все значения типа int могут быть положительными, отрицательными или 0. При вычитании дат первый операнд может быть меньше второго. Все операции должны выполняться быстро.

Код Вывод
print(Date('01.01.0001') + 1)
02.01.0001
print(Date('01.01.0001') - Date('01.01.0002'))
-365

M: Самый старший

Имеется список людей с указанием их фамилии, имени и даты рождения. Напишите эффективную по времени работы и по используемой памяти программу, которая будет определять самого старшего человека из этого списка и выводить его фамилию, имя и дату рождения, а если имеется несколько самых старших людей с одинаковой датой рождения, то определять их количество.

На вход программе в первой строке подается количество людей в списке N. В каждой из последующих N строк находится информация в следующем формате:

<Фамилия> <Имя> <Дата рождения>

где <Фамилия> – строка, состоящая не более, чем из 20 символов без пробелов, <Имя> – строка, состоящая не более, чем из 20 символов без пробелов, <Дата рождения> – строка, имеющая вид DD.MM.YYYY.

Программа должна вывести дату рождения самого старшего человека в списке, затем через пробел его фамилию и имя. Если таких людей, несколько, то вместо фамилии и имени выводится их количество.

Ввод Вывод
3
Ivan Petrov 01.05.1995
Petr Sergeev 29.04.1995
Sergey Ivanov 01.01.1996
29.04.1995 Petr Sergeev
3
Ivan Petrov 01.05.1995
Petr Sergeev 29.05.1995
Sergey Ivanov 01.05.1995
01.05.1995 2

N: Дни рождения

Имеется список сотрудников организации с указанием их фамилии, имени и даты рождения. Администрация ежедневно поздравляет всех сотрудников, родившихся в этот день. Напишите эффективную по времени работы и по используемой памяти программу, которая будет определять, в какой из дней года родилось больше всего сотрудников и выводить этот день (или несколько дней).

Формат входных данных аналогичен предыдущей задаче. Программа должна вывести список дат, в которые наибольшее число сотрудников отмечает дни рождения в формате DD.MM по возрастанию дат, каждая дата в отдельной строке.

Ввод Вывод
5
Ivan Petrov 01.05.1995
Petr Sergeev 29.04.1995
Sergey Romanov 01.01.1996
Roman Grigoriev 01.01.1995
Grigoriy Ivanov 01.05.1995
01.01
01.05

O: Ближайшие дни рождения

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

На вход программе в первой сроке подается текущая дата, заданная в формате DD.MM.YYYY.

Во второй строке подается количество людей в списке \(N\). В каждой из последующих \(N\) строк находится информация о каждом сотруднике, как в предыдущей задаче.

Известно, что у всех сотрудников даты рождения различаются. Программа должна вывести фамилию и имя самого молодого сотрудника, празднующего день рождения в ближайшие 7 дней или сообщение No birthdays in next week, если никто из сотрудников не празднует день рождения в ближайшие 7 дней.

Ввод Вывод
25.11.2010
3
Ivan Petrov 01.12.1994
Petr Sergeev 25.11.1994
Sergey Romanov 02.12.1994
Ivan Petrov
25.11.2010
1
Sergey Romanov 02.12.1994
No birthdays in next week

P: Пятница, 13-е

Докажите, что 13-е число месяца чаще всего приходится на пятницу.
Напишите программу, которая выводит на экран 7 чисел: вероятности выпадения 13 числа каждого месяца на понедельник, вторник, среду, четверг, пятницу, субботу, воскресенье. Например, если бы данные вероятности были бы равны, то программа должна вывести следущий текст:
0.142857
0.142857
0.142857
0.142857
0.142857
0.142857
0.142857

Работа с модулем datetime

В Python есть модуль datetime, содержащий несколько классов, поддерживающих работу с датами и временем.

Изучите краткое описание модуля или английскую документацию.

В последующих задачах вам необходимо реализовать программу, использующую возможности модуля datetime для работы с датами.

Q: datetime: День недели

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

Ввод Вывод
12.10.2008
Sunday
13.10.2008
Monday

R: datetime: Перевод формата даты

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

Обратите внимание, что метод strftime выводит номер дня по подстановке "%d" с лидирующим нулём.

Ввод Вывод
12.10.2008
October 12, 2008
01.01.0001
January 1, 1

S: datetime: Tomorrow

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

В этой задаче номер года не меньше 1000. Это связано с тем, что метод strftime выводит номер года по подстановке "%Y" без лидирующих нулей, а в Windows — с лидирующими нулями.

Ввод Вывод
12.10.2008
13.10.2008
31.12.2008
01.01.2009

T: datetime: Разница дат

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

Ввод Вывод
01.01.0001
02.01.0001
1
29.02.2004
01.03.2005
366

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

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

Ввод Вывод
00:01:01
61

V: datetime: Таймер

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

Ввод Вывод
09:00:00
90
09:01:30
23:59:59
1
00:00:00

W: datetime: Разница времен

Профессор лег спать, когда на часах было время 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

Unix time

В операционной системе UNIX, и во всех системах, имеющих сходную архитектуру (удовлетворяющих стандарту POSIX), например, Linux, Android, macOS, iOS, время хранится, как целое число, равное количеству секунд, прошедших с полуночи 1 января 1970 года по UTC, то есть число 0 соответствует времени 0:00:00 01.01.1970 UTC. Секунды координации не учитываются.

Увидеть текущее время в формате UNIX (называется также Unix timestamp) и перевести время из формата UNIX в человекочитаемый формат можно на сайте unixtimestamp.com. В операционной системе Linux это же можно сделать в командной строке при помощи команды date +%s.

Для хранения времени используется 32-битное знаковое целое число, что приведёт к концу света в 03:14:07 19 января 2038 UTC. Поэтому в 64-битных системах UNIX time хранится в 64-битной переменной. Соответствующий тип данных в языке C называется time_t, он является 32-битным или 64-битным знаковым целым числом.

X: UNIX timestamp

Напечатайте на экран текущее значение UNIX timestamp в виде целого числа (в примере вывод соответствует вызову программы в 0:00:00 1 января 2017 UTC). Вам помогут методы now() и timespamp().

Ввод Вывод
 
1483228800

Y: UNIX timestamp в обычное время

Переведите значение из формата UNIX timestamp в обычную дату и время в формате UTC. Выведите результат в формате dd.mm.yyyy hh:mm:ss.

Ввод Вывод
1483228800
01.01.2017 00:00:00

Z: Обычное время в UNIX timestamp

Решите обратную задачу. Для считывания времени из строки используйте метод datetime.strptime, который позволяет из строки, содержащей время в заданном формате, получить значение класса datetime.

Ввод Вывод
01.01.2017 00:00:00
1483228800