Даты

Для работы с датами полезно определить структуру из следующих полей:

struct date
{
    int day, month, year;
};

Операции, которые полезно переопределить для дат: ==, !=, <, <=, >, >=, ++, --. Также будут полезны операции сложения даты и числа и разности двух дат.

Удобно также переводить даты в числа типа int, пронумеровавав все даты подряд. Для перевода даты в число желательно определить оператор int, для перевода числа в дату желательно определить конструктор от одного значения типа int.

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

const char * MONTH_NAMES[] = {"", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
Для вывода названия дня недели лучше всего использовать константный массив строк-названий:
const char * DAY_OF_WEEK[] = {"Sunday", "Monday", "Tuesday", "Wednesday",  "Thursday", "Friday", "Saturday"};

Основным способом ввода-вывода дат должны быть переопределенные операторы << и >>, даты вводятся и выводятся в формате dd.mm.yyyy. Кроме этого будет ещё и текстовое представление даты вида January 1, 2017, для считывания которого необходимо реализовать метод from_text, а для вывода которого — метод to_text.

Упражнения для разминки

A: День года

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

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

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

const int Months[13] = {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 - номер года без лидирующих нулей.

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

Требования к оформлению класса

Класс должен содержать оператор считывания даты из потока:

istream & operator>> (istream &, date &);

Класс должен содержать метод

string to_text() const;
возвращающую строку с данным представлением.
Ввод Вывод
12.10.2008
October 12, 2008
01.01.0001
January 1, 1

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

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

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

Требования к оформлению класса

Класс должен содержать оператор вывода даты в поток:

ostream & operator<< (ostream &, const date &);

Класс должен содержать метод

void from_text(const string & s);
получающую на вход строку с описанием даты и устанавливающую дату на данное значение в соответствии со строкой.
Ввод Вывод
October 12, 2008
12.10.2008
January 1, 1
01.01.0001

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

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

F: Инкремент

Реализуйте операторы == и != для сравнения дат. Эти операторы должны возвращать значение типа bool.

Реализуйте оператор префиксного инкремента ++, который увеличивает дату на один день и возвращает ссылку на объект.

Префиксный оператор инкремента или декремента — это оператор, который пишется перед объектом (++a), он возвращает новое значение объекта.

Например, если оператор инкремента объявляется, как член класса, то объявление должно быть таким:

class date
{
    date & operator++()
    {
         //  Put your code here
         return *this;
    }
};
Ввод Вывод
12.10.2008
13.10.2008
31.12.2008
01.01.2009

G: Декремент

Реализуйте операторы <, <=, >, >= для сравнения дат. Эти операторы должны возвращать значение типа bool.

Реализуйте оператор постфиксного декремента --, который уменьшает дату на один день и возвращает ссылку на объект.

Постфиксный оператор инкремента или декремента — это оператор, который пишется после объекта (a--), он изменяет значение объекта, но возвращает ссылку на старое значение.

Например, если оператор декремента объявляется, как член класса, то объявление должно быть таким:

class date
{
    date operator--(int)
    {
         //  Put your code here
         return /* old saved value */;
    }
};

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

Также обратите внимание на то, что постфиксный инкремент должен возвращать не ссылку на объект, а сохраненное значение объекта, т.к. объект был изменен.

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

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

Реализуйте оператор вычитания двух дат. Его можно оформить в виде метода:

class date
{
    int operator-(const date &) const;
};

или в виде функции

int operator-(const date &, const date &);

Оператор возвращает разность — на сколько дней различаются две даты.

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

В примере ниже из второй даты вычитается первая.

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

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

По дате определите день недели, на который она приходится. Реализуйте алгоритм в виде метода

class date
{
    string day_of_the_week() const;
};

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

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

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

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

Решение оформите в виде оператора int.

class date
{
    operator int() const;
};

Обратите внимание, что при таком объявлении нельзя указывать тип возвращаемого значения, это должен быть int.

Пример использования: (int)date(1, 1, 2) возвращает 366.

Решение должно иметь сложность \(O(1)\), то есть не должно содержать циклы.

K: Разница дат - 2

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

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

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

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

Решение оформите в виде конструктора

class date
{
    date(int);
};

Не забудьте про существование других конструкторов: от трёх целых чисел и конструктора по умолчанию (без аргументов).

В решении допускаются циклы, ограничение по времени — 1 секунда на 200.000 операций считывания числа, перевода числа в дату и вывода даты.

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

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

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

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

Несложные задачки с использованием дат

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

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

На вход программе в первой строке подается количество людей в списке 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

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

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

Формат входных данных аналогичен предыдущей задаче. Программа должна вывести список дат, в которые наибольшее число сотрудников отмечает дни рождения в формате 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

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

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

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

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

Известно, что у всех сотрудников даты рождения различаются. Программа должна вывести фамилию и имя самого молодого сотрудника, празднующего день рождения в ближайшие 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

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

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

0.142857
0.142857
0.142857
0.142857
0.142857
0.142857
0.142857

Unix time

В следующих трех задачах вам нужно разобраться со стандартными функциями языка C, работающими со временем, а не реализовывать их самостоятельно. Эти функции описаны в заголовочном файле time.h.

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

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

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

Структура данных tm хранит дату и время в виде нескольких полей: год, месяц, день, часы, минуты, секунды, день недели, количество дней с начала года, флаг использования летнего времени. Эта структура также используется многими функциями, работающих с временем.

R: UNIX timestamp

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

Используйте функцию time.

Ввод Вывод
 
1483228800

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

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

Разберитесь с функциями gmtime и strftime.

Обратите внимание, что time_t в тестирующей системе является 64-битным типом данных, а у вас на компьютере он может быть 32-битным.

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

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

Решите обратную задачу. Вам поможет функция mktime. Обратите внимание на то, что функия mktime работает с местным временем, а вам нужно время в UTC. Обратите внимание на вызов функции setenv в примере на cppreference.com. Подробней о переменной TZ в GNU C Library.

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