闰年计算

60

为了找到闰年,为什么年份必须不能被100整除且能被400整除?

我知道年份必须能被4整除。请解释一下算法。


1
说句实话,日历计算非常困难,特别是如果你必须涉及闰秒。如果你正在构建一个库,请考虑获取一个现成的。祝你好运! :) - Greg D
7
这不是一个算法,而是天文事实,根据人类的粒度进行调整。 - PhiLho
1
https://www.mathsisfun.com/leap-years.html 提供了一个清晰的解释。 - Nan Xiao
3
我投票关闭此问题,因为它与编程无关。 - BJ Myers
27个回答

111

一年的长度大约是365.242196天,因此我们必须减去大约四分之一天才能适配:

365.242196 - 0.25 = 364.992196(每四年加一天):但糟糕的是,现在它太小了!让我们少加一天来修正一百年中的一次(即每四百年)。

364.992196 + 0,01 = 365.002196 (哎呦,有点大了,不过还是加上那一天吧,一般每四百年加一次)。

365.002196 - 1/400 = 364.999696

现在基本没问题了,只需要偶尔调整闰秒就行。

(注意:在这一步骤之后不再应用更正的原因是因为一年的长度也会变化!这就是为什么闰秒是最灵活的解决方案,例如可以看这里

我想就是这样啦。


5
您可以提及到,微小的差异确实很重要,因为旧有的儒略历没有这些修正,到了16世纪时,儒略历与天文事件如至点和复活节日期已经相差超过10天。 - Michael Borgwardt
5
有趣的是,现行闰年计算系统的原始来源是一份名为《Inter gravissimas》的文件,由格列高利十三世于1582年发布。 - Nick Moore
闰秒是为了解决一个无关的问题:一天并不完全是24小时,如果没有闰秒,中午会在很长时间后变成半夜。而闰年则是为了解决一年并不完全是365天的问题,如果没有修正,北半球的12月将会在夏天。 - Hans Olsson
为什么他们不重新定义秒,这样计算会更精确呢?这样做是否仍然可以让小时数与当前大致相同,而无需使用闰年呢? - Ced

93

维基百科上有一个算法用于确定闰年:

function isLeapYear (year):
    if ((year modulo 4 is 0) and (year modulo 100 is not 0))
    or (year modulo 400 is 0)
        then true
    else false

有关闰年的许多信息都可以在维基百科关于闰年的页面上找到,其中包括不同日历的详细信息。


我刚刚测试了一下,它可以正确地从http://kalender-365.de/leap-years.php的闰年列表中确定1900-2200年的闰年和非闰年。 - GlenPeterson
非常感谢这个算法。它完美地运行! - nuccio
@Kevin 我不同意。在我看来,你似乎在争论算法的细节,而这些细节并不那么重要。至少它提供了许多有关其他日历等方面的有用信息。 - Georg Schölly
@GeorgSchölly 我理解你的观点...我指的是一个糟糕的编程资源。维基百科上发布的算法未经审核,可能会发生变化,并且有时完全不正确。我认为算法的细节非常重要。闰年错误已经导致了许多系统崩溃,造成了数百万甚至数千万美元的损失。(https://en.wikipedia.org/wiki/Leap_year_bug) 是的,我刚引用了维基百科,哈!哈!无论如何,闰年代码很少被测试,日期/时间错误可能对系统造成毁灭性的影响。我是一个细节控。干杯! - Kevin P. Rice
当考虑闰年错误时,您必须考虑比闰年算法更多的因素。例如,新闻阅读器“trn”在1990年代有一个闰年错误 - 它将年份设置正确,但在年初添加了一天。 - Hans Olsson

17

一般而言,计算闰年的算法如下...

如果年份能够被4整除但不能被100整除,则为闰年。如果年份既能被4整除又能被100整除,则不是闰年,除非它也能被400整除。

因此,像1996年、1992年、1988年等年份都是闰年,因为它们能够被4整除但不能被100整除。对于世纪年,400规则很重要。因此,尽管世纪年1900年、1800年和1700年仍然能够被4整除,但它们恰好能够被100整除,而且它们不能进一步被400整除,所以它们不是闰年。


12

这已足以检查一年是否为闰年。

if( (year%400==0 || year%100!=0) &&(year%4==0))
    cout<<"It is a leap year";
else
    cout<<"It is not a leap year";

为什么需要用100进行模数检查 - Guit Adharsh

8

a) 一年有365.242199天。

b) 如果每年都是365天,那么在100年中我们将会损失24.2199天。这就是为什么在每个世纪末,我们要加上24天(每4年一次,除了能被100整除的年份)。

c) 然而,每个世纪我们还是会损失0.21299天。所以在四个世纪中,我们将会损失0.8796天。这就是为什么在每四个世纪末,我们会加上1天(每四个世纪中,我们会算一个闰年)。

d) 但是这意味着我们在四个世纪中会损失-0.1204天(我们要向前走)。因此,在8个四百年周期(3200年)中我们不会算一个闰年。

e) 这也意味着在3200年中,我们将会损失0.0368天。所以在24x3200年(=76800年)中,我们将会损失0.8832天。这就是为什么我们需要算一个闰年。

等等……(到那时我们可能已经毁掉了这个星球,所以这也无关紧要了)。

然而,我不明白的是为什么我们不是每500年算一个闰年而是每400年。这样我们会更快地逼近正确的时间(每500年我们将会损失2.3小时)。


8

这是一个简单的实现维基百科算法,使用JavaScript三元运算符:

isLeapYear = (year % 100 === 0) ? (year % 400 === 0) : (year % 4 === 0);

2
这比 if 语句效率低。即使一个比较就足够了,在每种情况下你都强制进行两次比较。在正态分布数据集上执行的计算将有75%的概率无法通过 % 4 测试,因此你应该首先进行测试,如果为假则立即退出。 - Anthony
var isleap = !(year%4) && (!!(year%100) || !(year%400));or if you prefer:var isleap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); - Quinn Dirks
安东尼,你的观察非常敏锐。在这个答案中引用了你的观点,性能测试表明,在那种情况下,你的方法比其他方法快2.1倍:https://dev59.com/HMX5oIgBc1ULPQZF4Wzv - Duncan Babbage

7
我相信维基百科可以比我更好地解释这个问题,但基本上是因为如果每四年添加一天,我们会超过太阳,因为它绕太阳的时间少于365.25天,所以我们通过不在不能被400整除的年份上添加闰日来进行补偿,例如1900年。
希望有所帮助。

5

如果输入的年份是闰年,则返回true。

基本的现代代码:

  If year mod 4 = 0, then leap year
  if year mod 100 then normal year
  if year mod 400 then leap year
  else normal year

今天的规则始于公元1582年,朱利安历法规则每4年开始于公元前46年,但在公元10年之前并不连贯,这是由凯撒宣布的。然而,在此之前的某些年份,他们会每隔3年添加一些闰年:

因此,闰年是公元前45年、42年、39年、36年、33年、30年、27年、24年、21年、18年、15年、12年、9年、8年和12年。在公元前45年之前不会添加闰年。

由于存在...2BC 1BC 1AD 2AD...的计算问题,所以不存在公元0年。

function isLeapYear(year: Integer): Boolean;
begin
  result := false;
  if year > 1582 then // Todays calendar rule was started in year 1582 
    result := ((year mod 4 = 0) and (not(year mod 100 = 0))) or (year mod 400 = 0)
  else if year > 10 then // Between year 10 and year 1582 every 4th year was a leap year 
    result := year mod 4 = 0
  else //Between year -45 and year 10 only certain years was leap year, every 3rd year but the entire time
    case year of
      -45, -42, -39, -36, -33, -30, -27, -24, -21, -18, -15, -12, -9:
        result := true;
    end;
end;

3

@PeterM http://web.archive.org/web/20100114210735/http://marauder.millersville.edu/~bikenaga/numbertheory/calendar/calendar.html - Jacques René Mesrine
1
顺便说一下,看起来谷歌的开发人员太酷了,不需要阅读维基百科:https://github.com/android/platform_frameworks_base/blob/4b1a8f46d6ec55796bf77fd8921a5a242a219278/core/java/android/widget/SimpleMonthView.java#L848 结果是:https://code.google.com/p/android/issues/detail?id=200371 :) - JustAMartin
在一个小小的转折中,这篇文章现在在Google的搜索排名中非常高,哈哈。 - hiljusti

1

如果我们再向前迈进一步,不会更好吗? 假设每3200年为非闰年, 那么一年的长度将会变得

364.999696 + 1/3200 = 364.999696 + .0003125 = 365.0000085

之后大约120000年后将需要进行调整。


由Sandeep Agrawal提供,再次提出以下建议:- 对于3000年,取3200年作为非闰年(因为3000年本身不是闰年,无法被400整除)通过将3200年作为非闰年进行计算:364.999696 + 1/3200 = 364.999696 + .0003125 = 365.0000085,现在几乎不需要任何进一步的调整。(大约12000年后) - Sandeep Agrawal
经过12000年(假设没有闰年),计算如下: - Sandeep Agrawal
这不是目前闰年计算的方法,因此这是对原问题的错误回答。 - pts

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接