某月的天数

@Daniate  August 26, 2018

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

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

先给出最终的代码:

#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 天数
1 31
2 28或29
3 31
4 30
5 31
6 30
7 31
8 31
9 30
10 31
11 30
12 31

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

month 天数
1 31 -> 31
2 28或29 -> 30
3 31 -> 31
4 30 -> 30
5 31 -> 31
6 30 -> 30
7 31 -> 31
8 31 -> 31
9 30 -> 30
10 31 -> 31
11 30 -> 30
12 31 -> 31

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

再调整:

month 天数 month 天数
1 31 -> 31 8 31 -> 31
2 28或29 -> 30 9 30 -> 30
3 31 -> 31 10 31 -> 31
4 30 -> 30 11 30 -> 30
5 31 -> 31 12 31 -> 31
6 30 -> 30
7 31 -> 31

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

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

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

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

month month % 7 month month % 7
1 1 8 1
2 2 9 2
3 3 10 3
4 4 11 4
5 5 12 5
6 6
7 0

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

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

month (month - 1) % 7 天数 month (month - 1) % 7 天数
1 0 31 8 0 31
2 1 30 9 1 30
3 2 31 10 2 31
4 3 30 11 3 30
5 4 31 12 4 31
6 5 30
7 6 31

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

month (month - 1) % 7 % 2 天数 month (month - 1) % 7 % 2 天数
1 0 31 8 0 31
2 1 30 9 1 30
3 0 31 10 0 31
4 1 30 11 1 30
5 0 31 12 0 31
6 1 30
7 0 31

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

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

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


评论已关闭