某月的天数

@Daniate  August 26, 2018

本文首发于Daniate的个人网站,文章链接:https://daniate.com/archives/139/

计算某年某月份中的天数,对于多数人来说,都是很简单的事情。但能否把程序写得足够简洁,就是另外一回事了。

先给出最终的代码:

#include <stdbool.h>
#include <assert.h>

_Bool isLeapYear(int year) {
    return (0 == year % 4 && 0 != year % 100) || (0 == year % 400);
}

int numberOfDaysInMonth(int month, int year) {
    assert(0 < month && month < 13);
    return (2 == month) ? (28 + isLeapYear(year)) : (31 - (month - 1) % 7 % 2);
}

如果你已经知道上述代码是如何得来的,就不必再往下看了。

为什么可以这样写?

先来看一下这个表格:

month天数
131
228或29
331
430
531
630
731
831
930
1031
1130
1231

然后对这个表格中的数据进行调整:

month天数
131 -> 31
228或29 -> 30
331 -> 31
430 -> 30
531 -> 31
630 -> 30
731 -> 31
831 -> 31
930 -> 30
1031 -> 31
1130 -> 30
1231 -> 31

也就是将2月份的天数,调整为了30天

再调整:

month天数month天数
131 -> 31831 -> 31
228或29 -> 30930 -> 30
331 -> 311031 -> 31
430 -> 301130 -> 30
531 -> 311231 -> 31
630 -> 30
731 -> 31

这样,是不是看出点儿规律了?

规律:天数31与30是交替出现的(1到7月,8到12月),也就是说,存在ABABAB这样的模式。此处,暂且认为2月份的天数就是30天,最后会对其进行特殊处理。

现在,对月份进行取模,也让其出现上述规律,这样,最后就可以简单地通过加减运算得到正确的结果。

由于是每隔7个数,就会重现规律,因此需要先对7进行取模:

monthmonth % 7monthmonth % 7
1181
2292
33103
44114
55125
66
70

到这里,会发现,对于6、7月,month % 7的结果都是偶数,为了最终得到ABABAB这样的规律,取模的结果必须是奇数偶数交替出现才可以。

因此,需要调整一下,将month % 7调整为(month - 1) % 7

month(month - 1) % 7天数month(month - 1) % 7天数
10318031
21309130
323110231
433011330
543112431
6530
7631

再用(month - 1) % 7的结果对2取模:

month(month - 1) % 7 % 2天数month(month - 1) % 7 % 2天数
10318031
21309130
303110031
413011130
503112031
6130
7031

这样,就可以使用31 - (month - 1) % 7 % 2计算出某月的天数。

对2月份进行处理后,最终就是(2 == month) ? (28 + isLeapYear(year)) : (31 - (month - 1) % 7 % 2)

知识共享许可协议
本作品由Daniate采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。


评论已关闭