Как правильно добавить год к 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
, даже если они представляют один и тот же момент времени.