Как только вы работаете с более старыми датами, вы должны быть осторожны с функциями даты и времени, которые вы используете. Старше может начаться уже накануне 1970-01-01 . И если вы вернетесь к 1582-10-15, то есть в силу вступления в силу григорианского календаря , тогда потребуется сверх-дополнительный уход.
В чем проблема?
В вашем коде mktime()
преобразует дату в time_t
, которую вы затем используете, чтобы сделать разницу между двумя датами. Согласно стандарту C:
Диапазон и точность времен, представляемых в clock_t
и time_t
, определяются реализацией.
Обычно time_t выражается в количестве прошедших секунд с 1970-01-01. Для этого необходимо знать (или предполагать) часовой пояс даты и времени (по умолчанию 0:00:00) и иметь полное представление не только о високосных годах, но и о високосных секундах .
mktime()
возвращает код ошибки -1, если дата недопустима или не может быть преобразована в диапазон time_t, поддерживаемый реализацией. Поэтому более безопасная версия вашего кода будет проверять потенциальную ошибку:
std::time_t th = std::mktime(&date);
std::time_t tl = std::mktime(&gregorian);
if (th==-1 || tl==-1) {
std::cout<<"At least one of the date couldn't be converted"<<std::endl;
}
else {
double aux = std::difftime(th, tl);
std::cout << aux<< " "<< aux/3600.0/24.0/365.25 <<std::endl;
}
Онлайн-демонстрация с реализацией, в которой он работает хорошо. Для MSVC преобразование завершается неудачно для любой даты до 1970-01-01. Это задокументировано здесь .
Есть ли лучшее решение?
Теперь, если вы вернетесь во времени к 1582-10-15, не имеет особого смысла преобразовывать это в точное количество секунд для сравнения. две старые даты.
Поэтому вы можете рассмотреть boost::gregorian::date
. Эта библиотека может работать с датами с 1400-янв-01 по 9999-декабрь-31. Вместо того, чтобы конвертировать в секунды, он конвертирует дату в число дней.
Затем можно надежно сравнить даты, например 1582-10-15 и 1582-10-14, получая разницу в днях:
using namespace boost::gregorian;
date d0(1582, 10, 15);
date d1(1582, 10, 25);
date d2(1582, 10, 14);
std::cout << d1-d0 << std::endl;
std::cout << d2-d0 << std::endl;
Почему старые даты настолько сложны?
Funilly, в приведенном выше примере вы увидите, что BOOST сделает теоретический расчет. Он найдет один день разницы между 1582-10-14 и 1582-10-15 ( демо ).
Конечно, мы все знаем, что это абсурдно , так как введение григорианского календаря привело к тому, что 4 октября 1582 г. немедленно последовало на следующий день 15Октябрь 1582 года.
Обратите внимание, что такие календарные несоответствия существуют в другие даты, в зависимости от страны. Например, для Великобритании, согласно википедии, за средой, 2 сентября 1752 года, последовал четверг, 14 сентября 1752 года.
И, наконец, если вы сравниваете даты до григорианского календаря, вам необходимо принять двусмысленность. Взять, к примеру, 12 октября 1492 года, в день, когда Колумб ступил на ноги в Америке. Это на самом деле дата, выраженная в юлианском календаре. В григорианском календаре на самом деле это будет 21 октября 1492 .
Какой-нибудь более простой вариант?
Теперь, time_t
- немного наследство. Стандарт C ++ относится к стандарту C для определения связанных функций.
Современный C ++ предлагает std::chrono
и тип std::chrono::time_point
. Проблема в том, что в настоящее время нет полезной функции для преобразования в календарь и из календаря: они будут поставляться только с C ++ 20.
Но если вы просто хотите сравнить две старые даты, предполагая, что в календаре нет несоответствий, вы можете просто сравнить год и, если он равен, месяц и если он равен дню. Я знаю, это звучит смешно, но это будет работать без проблем, независимо от того, какая у вас версия и реализация C ++.