简体   繁体   English

一个月的最后一天?

[英]Last day of a month?

This example is a way to calculate the last day of a month using date library.这个例子是一种使用日期库计算一个月的最后一天的方法。 Is there a simpler solution to this goal?这个目标有更简单的解决方案吗?

this idea not work: lastDay = firstDay + months{1} - days{1};这个想法不起作用:lastDay = firstDay +months{1} - days{1};

#include <date/date.h>
#include <iostream>

using namespace std;
using namespace date;

int main() {
sys_days firstDay, lastDay;
string d1;

d1 = "2018-12-01";
istringstream in1{d1};
in1 >> parse ("%F", firstDay);

sys_days d2 = firstDay;
auto ymd = year_month_day{d2};
unsigned j = unsigned (ymd.month());
unsigned i = j;
if (i == 12)
    i = 0;
while (true) {
    d2 = d2 + days{1};
    ymd = year_month_day{d2};
    j = unsigned (ymd.month());
    if (j == i + 1)
        break;
}
lastDay = d2 - days{1};

cout << lastDay << '\n'; // 2018-12-31
return 0;
}

Just create your own last_day_of_month method instead:只需创建您自己的 last_day_of_month 方法:

#include <chrono>

template <class Int>
constexpr
bool
is_leap(Int y) noexcept
{
    return  y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
}

constexpr
unsigned
last_day_of_month_common_year(unsigned m) noexcept
{
    constexpr unsigned char a[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    return a[m - 1];
}

template <class Int>
constexpr
unsigned
last_day_of_month(Int y, unsigned m) noexcept
{
    return m != 2 || !is_leap(y) ? last_day_of_month_common_year(m) : 29u;
}

int main()
{
    auto dayofmonth = last_day_of_month(2018, 6);
}

Here's an easier way:这是一个更简单的方法:

#include "date/date.h"
#include <iostream>

date::year_month_day
lastDay(date::year_month_day ymd)
{
    return ymd.year()/ymd.month()/date::last;
}

int
main()
{
    using namespace date;
    std::cout << lastDay(2018_y/12/01) << '\n';  // 2018-12-31
}

last can be used as the "day specifier" in any year/month/day expression to mean "last day of the year/month pair. This will create a type year_month_day_last which is implicitly convertible to year_month_day . It also has a day() getter: last可以在任何year/month/day表达式中用作“日期说明符”,表示“ year/month对的最后一天。这将创建一个类型year_month_day_last ,它可以隐式转换为year_month_day 。它还有一个day()吸气剂:

https://howardhinnant.github.io/date/date.html#year_month_day_last https://howardhinnant.github.io/date/date.html#year_month_day_last

An answer for those strongly interested in higher performance .对于那些对更高性能非常感兴趣的人的答案 Ideally this bit twiddling would be implemented by widely used libraries (eg, C/C++/Java/Python standard libraries) and we would simply call higher level APIs.理想情况下,这个小问题将由广泛使用的库(例如,C/C++/Java/Python 标准库)实现,我们将简单地调用更高级别的 API。 I hope this post inspires library writers to implement the code below which I came up with:我希望这篇文章能激励图书馆作者实现我在下面提出的代码:

unsigned char last_day_of_month(int year, unsigned char month) {
  return month != 2 ? ((month ^ (month >> 3))) | 30 :
    is_leap_year(year) ? 29 : 28;
}

As everybody knows February is special and we need to check whether year is leap or not.众所周知,二月是特殊的,我们需要检查year是否为闰年。 I assume we already have a very efficient implementation of is_leap_year (this is another surprisingly whole story ).我假设我们已经有一个非常有效的is_leap_year实现(这是另一个令人惊讶的完整故事)。 For month == 2 the code should be clear.对于month == 2 ,代码应该很清楚。

Let's focus on the cryptic part ((month ^ (month >> 3))) | 30让我们专注于神秘的部分((month ^ (month >> 3))) | 30 ((month ^ (month >> 3))) | 30 which is evaluated when month != 2 . ((month ^ (month >> 3))) | 30 month != 2时被评估。 In this case, the result is either 30 or 31 , or in another words, b + 30 , where b is either 0 or 1 .在这种情况下,结果是3031 ,或者换句话说, b + 30 ,其中b01 We further split the reasoning into two sub-cases depending on whether month < 8 or not:我们进一步将推理分为两个子案例,具体取决于month < 8与否:

  1. If month is in {1, 3, 4, 5, 6, 7};如果month在 {1, 3, 4, 5, 6, 7};

Months with 31 days ( b == 1 ) in this set are 1 (January), 3 (March), 5 (May) and 7 (June), that is, odd numbers.此集合中具有31天 ( b == 1 ) 的月份为1 (一月)、 3 (三月)、 5 (五月)和7 (六月),即奇数 Conversely, even months have 30 days ( b == 0 ).相反,即使是月份也有30天( b == 0 )。

  1. If month is in {8, 9, 10, 11, 12};如果month在 {8, 9, 10, 11, 12};

Now, it's the other way around: months in this set with 31 days ( b == 1 ) are 8 (August), 10 (October) and 12 (December) thus even , whereas odd months have 30 days ( b == 0 ).现在,情况正好相反:这组31天 ( b == 1 ) 中的月份是8 (八月)、 10 (十月) 和12 (十二月) 因此偶数,而奇数月份有30天 ( b == 0 )。

We conclude that for any month (but 2 ), its length depends on two things: its parity and whether it's >= 8 or not.我们得出结论,对于任何month (但2 ),其长度取决于两件事:它的奇偶校验以及它是否>= 8 These properties are determined by two bits of month : the 0-th for parity and the 3-rd for >= 8 .这些属性由month的两位决定:奇偶校验的第 0 位和>= 8第 3 位。 We just need to check whether those two bits are different (and get b == 1 ) or not (and get b == 0 ).我们只需要检查这两个位是否不同(并获得b == 1 )或不不同(并获得b == 0 )。 This can be achieved through a XOR ( ^ ) between these bits, that is, b = ((month >> 0) & 1) ^ ((month >> 3) & 1) which simplifies to b = (month ^ (month >> 3)) & 1 .这可以通过这些位之间的 XOR ( ^ ) 来实现,即b = ((month >> 0) & 1) ^ ((month >> 3) & 1)简化为b = (month ^ (month >> 3)) & 1 . Since 30 is even we have b + 30 = b | 30因为30是偶数,所以我们有b + 30 = b | 30 b + 30 = b | 30 . b + 30 = b | 30 . (Performance-wise choosing between + or | doesn't seem to matter.) (在+|之间进行性能明智的选择似乎无关紧要。)

The expression ((month ^ (month >> 3)) & 1) | 30表达式((month ^ (month >> 3)) & 1) | 30 ((month ^ (month >> 3)) & 1) | 30 can be further simplified. ((month ^ (month >> 3)) & 1) | 30可以进一步简化。 (Thanks to Dr. Matthias Kretz for giving me the following suggestion.) Indeed, 30 in binary is 0b11110 and, assuming month <= 12 , for x = (month ^ (month >> 3)) , the only bit of x | 30 (感谢 Matthias Kretz 博士给我以下建议。)实际上,二进制中的300b11110并且,假设month <= 12 ,对于x = (month ^ (month >> 3))x | 30的唯一位x | 30 that depends on x is the rightmost so that (x & 1) | 30 == x | 30依赖于x x | 30是最右边的,所以(x & 1) | 30 == x | 30 (x & 1) | 30 == x | 30 (x & 1) | 30 == x | 30 meaning that & 1 can be dropped. (x & 1) | 30 == x | 30意味着& 1可以被删除。 This yields the implementation above.这产生了上面的实现。

I benchmarked the above against an implementation which uses a look-up array.我针对使用查找数组的实现对上述内容进行了基准测试 Mine seems to be ~10% faster.我的似乎快了大约 10%。 This doesn't seem much but notice that my implementation has no memory footprint (everything should be done in registers) and therefore, does not compete for L1 cache lines with other parts of the program (reducing cache thrashing).这看起来并不多,但请注意我的实现没有内存占用(一切都应该在寄存器中完成),因此不会与程序的其他部分竞争 L1 缓存行(减少缓存抖动)。 This is probably not reflected in "benchmark lab conditions" where the only thing the tight loop does is calculating the last day of the month.这可能没有反映在“基准实验室条件”中,其中紧密循环所做的唯一事情就是计算该月的最后一天。 Production conditions are likely to be different and the performance boost to be greater.生产条件可能会有所不同,性能提升会更大。

Note:笔记:

As some have mentioned there are some historical quirks that make years prior to 1582 meaningless anywhere in the world.正如一些人所提到的,有一些历史怪癖使 1582 年之前的年份在世界任何地方都变得毫无意义。 Worse, different countries adopted the Gregorian calendar at different times so they were out of sync for centuries.更糟糕的是,不同的国家在不同的时间采用了公历,因此它们几个世纪以来都不同步。 Therefore, from the historical perspective, most computer implementations (including the above) are wrong.因此,从历史的角度来看,大多数计算机实现(包括上述)都是错误的。 However, pragmatically, most of them simply extrapolate the Gregorian calendar in its current form as if it has always existed which yields the so called proleptic Gregorian calendar .然而,从实用的角度来看,他们中的大多数只是简单地推断出当前形式的公历,就好像它一直存在一样,从而产生了所谓的预测公历 I emphasize: the proleptic Gregorian calendar isn't historically accurate but it serves perfectly well most applications .我强调:预兆公历在历史上并不准确,但它在大多数应用程序中都能很好地服务

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM