<chrono>
库允许日期静默地进入!ok()状态。例如:
#include <chrono>
#include <iostream>
int
main()
{
using namespace std;
using namespace chrono;
auto date = 2023y/October/31;
cout << date.ok() << '\n';
date += months{1};
cout << date.ok() << '\n';
}
字符串
输出量:
1
0
型
Demo.
我知道10月31日是一个有效的日期,而11月31日不是。但是为什么11月31日不是一个错误(Assert或抛出)?或者为什么它不像其他日期库那样快速返回到11月30日,或者滚动到12月1日?
难道让11月31日静静地存在不容易出错吗?
1条答案
按热度按时间ldxq2e6h1#
这个问题,在某种程度上,几乎可以自己回答:
或者为什么它不像其他日期库那样快速回到11月30日,或者滚动到12月1日?
因为没有任何一致的实践来决定应该发生什么,
<chrono>
把应该发生什么留给了客户端。因此,程序员可以想象和实现的任何事情都可以发生。例如,以下是如何快速返回到11月30日:
字符串
以下是如何进入12月:
型
前者在代码中的作用是显而易见的。但后者需要更多的解释:转换到
sys_days
只是将{year, month, day}
数据结构转换为{count_of_days}
数据结构。即使day字段溢出,也允许进行转换。这就像tm
和time_t
之间的C计时API。就像没有无效的
time_t
一样,也没有无效的sys_days
。它只是自1970-01-01以来(或之前)的天数计数。当您将该计数转换回{year, month, day}
时,它会导致有效的(.ok() == true
)year_month_day
。显然,程序员也可以声明一个错误:
型
这种行为的一个很酷的方面(在程序员添加更正之前)是日期算术遵循正常的算术规则:
型
也就是说,如果你加上一个月,然后减去一个月,你保证会得到相同的日期:
型
Demo.
有时正确的操作不是flag-error,roll-over或者snap-back,有时正确的操作是 * 忽略 * 无效的日期!
考虑这个问题:
我想找到某年
y
的所有日期,这些日期是该月的第5个星期五(因为那是聚会日或其他什么)。下面是一个非常有效的函数,它收集了一年中所有的第5个星期五:型
输出示例:
型
Demo.的
事实证明,这是一个不变量,每年将有4个月或5个月,其中将有5个星期五。所以我们可以有效地返回结果作为一对<array<year_month_day,5>,unsigned>,其中第二个成员总是4或5。
第一个任务就是用一堆
year_month_day
初始化数组。我随意选择了0y/0/0
作为一个好的初始化值。我喜欢这个值的什么?我喜欢的一点是它是!ok()
!如果我在.second == 4
时意外访问了.first[4]
,一个额外的安全性是,结果year_month_day
是!ok()
。因此,能够在没有Assert或异常的情况下构造这些!ok()
值非常重要只是因为这个原因(比如nan)。成本?没有。这些是编译时常量。接下来我遍历
y
年的每个月。首先要做的是构造1月的第5个星期五。然后将循环递增1个月,直到年份改变。现在,因为不是每个月都有第5个星期五,所以这可能不会导致有效的日期。但是在这个函数中,构造无效日期的正确响应不是Assert,也不是异常,也不是快速返回或滚动。正确的响应是忽略日期并返回到下一个月。如果它是有效日期,那么它会推到结果。
在这个程序的执行过程中,计算出了许多无效的日期。它们中没有一个代表错误。甚至在计算中使用了无效的日期(增加一个月)。但是因为算法是规则的,所以一切都正常。
所以总而言之,最好还是让聪明的程序员来处理无效日期的行为,因为聪明的程序员可以为他们的问题创建各种巧妙的解决方案,只要有足够的灵活性。