将闰年考虑在内,计算一个月中天数的通用公式

  • 本文关键字:中天 一个 闰年 计算 calendar
  • 更新时间 :
  • 英文 :


有人知道像这样计算一个月天数的数学公式吗

28 + (x + Math.floor(x/8)) % 2 + 2 % x + 2 * Math.floor(1/x);

但哪一个也考虑闰年呢?它还应该考虑到,公历每400年省略3个闰日,这是其闰周期的长度。

在表达式中添加术语(m == 2) * leapyear(yyyy)来确定闰年二月的正确天数并不困难。这个C代码展示了一种实现方法:

#include <stdio.h>
#include <stdbool.h>
static inline bool leapyear(int yy)
{
    if (yy %   4 != 0) return false;
    if (yy % 100 != 0) return true;
    if (yy % 400 != 0) return false;
    return true;
}
static inline int old_dim(int mm)
{
    return (28 + (mm + (mm/8)) % 2 + 2 % mm + 2 * (1/mm));
}
static inline int new_dim(int mm, int yyyy)
{
    return (28 + (mm + (mm/8)) % 2 + 2 % mm + 2 * (1/mm) + ((mm == 2) * leapyear(yyyy)));
}
int main(void)
{
    /*28 + (x + Math.floor(x/8)) % 2 + 2 % x + 2 * Math.floor(1/x);*/
    for (int mm = 1; mm <= 12; mm++)
        printf("mm = %2d, DIM = %2dn", mm, old_dim(mm));
    for (int yyyy = 1900; yyyy < 2101; yyyy += 5)
    {
        for (int mm = 1; mm <= 12; mm++)
            printf("yyyy = %4d, mm = %2d: DIM = %2dn", yyyy, mm, new_dim(mm, yyyy));
    }
    return 0;
}

mm = 2的输出(从完整输出中过滤)为:

yyyy = 1900, mm =  2: DIM = 28
yyyy = 1905, mm =  2: DIM = 28
yyyy = 1910, mm =  2: DIM = 28
yyyy = 1915, mm =  2: DIM = 28
yyyy = 1920, mm =  2: DIM = 29
yyyy = 1925, mm =  2: DIM = 28
yyyy = 1930, mm =  2: DIM = 28
yyyy = 1935, mm =  2: DIM = 28
yyyy = 1940, mm =  2: DIM = 29
yyyy = 1945, mm =  2: DIM = 28
yyyy = 1950, mm =  2: DIM = 28
yyyy = 1955, mm =  2: DIM = 28
yyyy = 1960, mm =  2: DIM = 29
yyyy = 1965, mm =  2: DIM = 28
yyyy = 1970, mm =  2: DIM = 28
yyyy = 1975, mm =  2: DIM = 28
yyyy = 1980, mm =  2: DIM = 29
yyyy = 1985, mm =  2: DIM = 28
yyyy = 1990, mm =  2: DIM = 28
yyyy = 1995, mm =  2: DIM = 28
yyyy = 2000, mm =  2: DIM = 29
yyyy = 2005, mm =  2: DIM = 28
yyyy = 2010, mm =  2: DIM = 28
yyyy = 2015, mm =  2: DIM = 28
yyyy = 2020, mm =  2: DIM = 29
yyyy = 2025, mm =  2: DIM = 28
yyyy = 2030, mm =  2: DIM = 28
yyyy = 2035, mm =  2: DIM = 28
yyyy = 2040, mm =  2: DIM = 29
yyyy = 2045, mm =  2: DIM = 28
yyyy = 2050, mm =  2: DIM = 28
yyyy = 2055, mm =  2: DIM = 28
yyyy = 2060, mm =  2: DIM = 29
yyyy = 2065, mm =  2: DIM = 28
yyyy = 2070, mm =  2: DIM = 28
yyyy = 2075, mm =  2: DIM = 28
yyyy = 2080, mm =  2: DIM = 29
yyyy = 2085, mm =  2: DIM = 28
yyyy = 2090, mm =  2: DIM = 28
yyyy = 2095, mm =  2: DIM = 28
yyyy = 2100, mm =  2: DIM = 28

这正确地将1900年和2100年视为非闰年,而将2000年视为闰年。

yyyy = 1900, mm =  1: DIM = 31
yyyy = 1900, mm =  2: DIM = 28
yyyy = 1900, mm =  3: DIM = 31
yyyy = 1900, mm =  4: DIM = 30
yyyy = 1900, mm =  5: DIM = 31
yyyy = 1900, mm =  6: DIM = 30
yyyy = 1900, mm =  7: DIM = 31
yyyy = 1900, mm =  8: DIM = 31
yyyy = 1900, mm =  9: DIM = 30
yyyy = 1900, mm = 10: DIM = 31
yyyy = 1900, mm = 11: DIM = 30
yyyy = 1900, mm = 12: DIM = 31
…
yyyy = 2000, mm =  1: DIM = 31
yyyy = 2000, mm =  2: DIM = 29
yyyy = 2000, mm =  3: DIM = 31
yyyy = 2000, mm =  4: DIM = 30
yyyy = 2000, mm =  5: DIM = 31
yyyy = 2000, mm =  6: DIM = 30
yyyy = 2000, mm =  7: DIM = 31
yyyy = 2000, mm =  8: DIM = 31
yyyy = 2000, mm =  9: DIM = 30
yyyy = 2000, mm = 10: DIM = 31
yyyy = 2000, mm = 11: DIM = 30
yyyy = 2000, mm = 12: DIM = 31

简化该公式,跳过硬编码常量,并消除2个输入值之外的额外临时变量:

确保它们是通过-值传递的而不是通过引用传递的

function month2numdays(__, _) {
    # __| month-# [1-12]
    #  _| (opt.) leap flag - numerically non-zero or string
    #      beginning w/ "L" of either case are treated as TRUE 
    return 
    (_ = (_ = !!+_ || _ ~ "^[Ll]") + _ == (__ = int(__))) + 
    (_+= (_^= _) + _)^_ + _^(__ != --_) + (__ + (_*_*_ <= __)) % _
}

^幂运算非逐位异或


1  ____  31   2  ____  28   3  ____  31   4  ____  30
5  ____  31   6  ____  30   7  ____  31   8  ____  31
9  ____  30  10  ____  31  11  ____  30  12  ____  31
1  Leap  31   2  Leap  29   3  Leap  31   4  Leap  30
5  Leap  31   6  Leap  30   7  Leap  31   8  Leap  31
9  Leap  30  10  Leap  31  11  Leap  30  12  Leap  31

统一方法利用30 := 3 + (27 := 3^3),任何事物的0-th power(即布尔条件评估为FALSE)都是1,消除减法并将其修剪为仅1个modulo %操作

相关内容

最新更新