Как сравнить две переменные tm (из ctime) - PullRequest
1 голос
/ 03 ноября 2019

Я только начал изучать C ++.

Я использую Windows 7 Ultimate x64 с версией Visual Studio:

Microsoft Visual Studio Enterprise 2017 
Version 15.9.12
VisualStudio.15.Release/15.9.12+28307.665
Installed Version: Enterprise

Visual C++ 2017   00369-90013-89248-AA631
Microsoft Visual C++ 2017

Visual Studio Tools for CMake   1.0
Visual Studio Tools for CMake

Я пытаюсь сравнить эти две даты:

1582 15 октября19 июня 2009 г. 18: 00

Потому что я хочу знать, будет ли дата позже 1582 15 октября.

Для этого у меня есть этот кусок кода:

#include <ctime>
tm date = { 0, 0, 18, 19, 5, 109, 0, 0, 0 };
tm gregorian = { 0, 0, 0, 15, 9, (1582 - 1900), 0, 0, 0};

double aux = std::difftime(std::mktime(&date), std::mktime(&gregorian));

Но aux равен 0.0. Я думаю, что он должен отличаться от нуля.

Я делаю что-то не так или результат правильный?

1 Ответ

0 голосов
/ 04 ноября 2019

Как только вы работаете с более старыми датами, вы должны быть осторожны с функциями даты и времени, которые вы используете. Старше может начаться уже накануне 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 ++.

...