Как правильно добавить год к COledateTime с учетом високосных лет?

У меня есть такой код:

COleDateTime datStart = COleDateTime::GetCurrentTime(), datEnd;

// Update end date (one year later)
datEnd.SetDateTime(datStart.GetYear() + 1,
    datStart.GetMonth(),
    datStart.GetDay(),
    datStart.GetHour(),
    datStart.GetMinute(),
    datStart.GetSecond());

Приведенный выше код не удался вчера (29 февраля 2020 г.), потому что datEndпривело к 29 февраля 2021 г., что не соответствует действительности.

Как правильно прибавить год к datStart с учетом високосных лет?

C# имеет:

DateTime theDate = DateTime.Now;
DateTime yearInTheFuture = theDate.AddYears(1);

Что эквивалентно MFC/C++?

Одна из возможностей:

COleDateTimeSpan spnYear;
spnYear.SetDateTimeSpan(365, 0, 0, 0);

datEnd = datStart + spnYear;

Но он по-прежнему потенциально ошибочен, поскольку в високосных годах 366 дней. Итак, каков правильный путь?

Я вижу похожий вопрос:

C++ добавить 1 год на сегодняшний день

Это подразумевает использование boost. У меня есть импульс в моем проекте, хотя я никогда не использовал его для манипуляций с датами. Интересно, можно ли это использовать сCOleDateTime объект?

С помощью boost:

boost::gregorian::date dStart{ datStart.GetYear(), datStart.GetMonth(), datStart.GetDay() };
boost::gregorian::date dEnd = dStart + boost::gregorian::years(1);

datEnd.SetDateTime(
    dEnd.year(),
    dEnd.month(),
    dEnd.day(),
    datStart.GetMonth(),
    datStart.GetMinute(),
    datStart.GetSecond());

Не компилируется.

4>D:\My Programs\2019\MeetSchedAssist\Meeting Schedule Assistant\PublishersDatabaseDlg.cpp(354,49): error C2398: Element '1': conversion from 'int' to 'boost::gregorian::date::year_type' requires a narrowing conversion
4>D:\My Programs\2019\MeetSchedAssist\Meeting Schedule Assistant\PublishersDatabaseDlg.cpp(354,70): error C2398: Element '2': conversion from 'int' to 'boost::gregorian::date::month_type' requires a narrowing conversion
4>D:\My Programs\2019\MeetSchedAssist\Meeting Schedule Assistant\PublishersDatabaseDlg.cpp(354,89): error C2398: Element '3': conversion from 'int' to 'boost::gregorian::date::day_type' requires a narrowing conversion

1 ответ

Решение

COleDateTime это обертка вокруг (ужасно удивительно) DATE тип. Внутри он хранит 64-битное значение с плавающей запятой, где целые числа представляют дни с 30 декабря 1899 года (полночь). Дробная часть представляет время суток (например,0.25 означает 6 утра, а 0.5полдень). Каждое значение с плавающей запятой является допустимым значением и представляет собой уникальный1 момент времени.

Добавление года к COleDateTime значение составляет добавление стоимости 365.0(или любое другое значение, соответствующее вашим потребностям) к его внутреннему представлению. Это можно сделать, открыв m_dt член напрямую

COleDateTime datEnd{ datStart.m_dt + 365.0 };

или используя COleDateTimeSpan

datEnd = datStart + COleDateTimeSpan{ 365.0 };
// or
datEnd = datStart + COleDateTimeSpan{ 365, 0, 0, 0 };

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

COleDateTime datStart{ 2020, 2, 29, 0, 0, 0 };
COleDateTime datEnd{ datStart + COleDateTimeSpan{ 365.0 } };

создаст момент времени, который представляет 28 февраля 2021 г. (обратите внимание, что если вы добавите 366.0 дней, это вернется 1 марта 2021 г.).


Первоначальное решение, которое пытается построить COleDateTimeпри выполнении арифметических действий с отдельными компонентами даты не удается во время строительства. Конструктор проверяет свой ввод. Когда прошло(2021, 2, 29, 0, 0, 0) он определяет недопустимую дату и устанавливает m_status к invalid.


+1 Это не совсем правильно. Значения в диапазоне(-1 .. 0]отображаются на тот же момент времени, что и их абсолютные значения. Обратите внимание, что напримерCOleDateTime{-0.25} == COleDateTime{0.25} оценивает false, даже если они представляют один и тот же момент времени.

Другие вопросы по тегам