【问题标题】:What is the right way to add a year to COledateTime taking into account leap years?考虑到闰年,向 COledateTime 添加一年的正确方法是什么?
【发布时间】:2020-03-04 01:47:06
【问题描述】:

我有这个代码:

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());

上述代码昨天(2020 年 2 月 29 日)失败,因为 datEnd 导致 2021 年 2 月 29 日无效。

考虑到闰年,在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++ add 1 year to date

这意味着使用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

【问题讨论】:

  • 你想要的结果是什么?至于您在使用 boost 时遇到的错误:编译器抱怨缩小转换。这是由于使用了大括号初始化。将年/月/日值显式转换为适当的类型,或使用带括号的直接初始化 (dStart( datStart.GetYear(), ... );)。
  • 恐怕我对boost不是很熟悉。更重要的问题仍然是:在任何给定时间点增加 1 年时,您期望的结果是什么?实现是(一旦该问题得到回答),很可能是微不足道的。而且你也不需要提升,使用 C++'std::chrono 库。
  • 可以理解,但是对您来说一年是什么?总是 365 天吗?默认为 365 天,如果范围包括 2 月 29 日,则为 366 天? 365.25 天? 365.2425 天?还有什么?
  • 查看我的comment here 关于 boost 库的信息。我很久以前就开始为约会做准备了。 @IInspectable 说 DATE 可怕是温和的。 :)
  • @lak:你不需要提升。 std::chrono 自 C++11 以来一直是 C++ 的一部分。使用 C++20,库终于完成了,添加了缺少的部分:日历、时区、解析和格式化。有问题的代码很简单:auto const datStart{chrono::system_clock::now()}; auto const datEnd{datStart + years{1}};(在 C++20 中),或在 C++11 中... +24h * 365

标签: c++ mfc coledatetime


【解决方案1】:

COleDateTimeDATE 类型的包装器(非常令人惊讶)。它在内部存储一个 64 位浮点值,其中整数表示自 1899 年 12 月 30 日(午夜)以来的天数。小数部分表示一天中的时间(例如,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 } };

将生成一个表示 2021 年 2 月 28 日的时间点(请注意,如果您添加 366.0 天,这将返回 2021 年 3 月 1 日)。


尝试通过对各个日期组件进行算术来构造COleDateTime 的初始解决方案在构造过程中失败。构造函数验证其输入。当通过(2021, 2, 29, 0, 0, 0) 时,它确定一个无效的日期,并将m_status 设置为invalid


1这并不完全正确。 (-1 .. 0] 范围内的值映射到与其绝对值相同的时间点。请注意,例如COleDateTime{-0.25} == COleDateTime{0.25} 的计算结果为 false,即使它们代表相同的时间点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-15
    • 1970-01-01
    • 2020-01-17
    • 2020-10-22
    • 1970-01-01
    • 2022-10-16
    • 2021-10-17
    相关资源
    最近更新 更多