不存在的日期 (2015)
Dates That Don't Exist (2015)

原始链接: https://blog.yossarian.net/2015/06/09/Dates-That-Dont-Exist

1582年,天主教世界从儒略历过渡到格里高利历,以纠正长久以来的历法偏差。为使两个系统对齐,10月5日至10月14日这十天被正式从历史中删除。因此,在格里高利历中,这些日期并不存在。 本文探讨了现代编程语言如何处理这些“不存在”的日期。Ruby 能正确拒绝创建该时段的格里高利历日期,但 Python 和 Perl 均未能将其标记为无效,允许程序员实例化这些不可能存在的日期。 作者以此案例强调了日期和时间实现中精确性的重要性。他指出,虽然 `ncal` 等工具试图考虑不同地区采用格里高利历的历史差异,但对于软件开发者而言,实现完美的历史准确性仍然是一项复杂的挑战。

本次讨论探讨了因从儒略历转向格里历所导致的“不存在”日期这一复杂问题。核心技术结论是:大多数编程库使用**前推格里历(proleptic Gregorian calendar)**,即默认将现行的格里历规则无限向过去延伸。这种做法简化了计算,但对历史学家和数据库工程师而言却产生了逻辑上的不一致,因为它忽略了各国实施历法改革的具体“过渡日期”(例如天主教欧洲为 1582 年,英国及美国为 1752 年)。 辩论中的关键点包括: * **表示误差:** 简单地跳过日期(正如格里历改革所做的那样)会使日期运算变得困难。有人建议使用在特定历史节点拼接儒略历和格里历规则的“复合历法”。 * **“纪元”解决方案:** 专家强调,内部日期时间处理应依赖明确的数字时间戳(如 Unix 时间戳),仅在面向用户的输出时才使用特定历法的逻辑。 * **背景至关重要:** 由于全球各地的采纳日期不同,历史准确性不仅取决于日期本身,还需要了解文档的地理和法律背景。 综上所述,尽管前推格里历是现代软件的标准,但将其应用于历史事件时,它仍然是一个“虚构”的框架。
相关文章

原文
Mini-post: Dates That Don't Exist

Programming, philosophy, pedaling.


Jun 9, 2015     Tags: programming    

This post is at least a year old.

Calendars and Missing Dates

In 1582, under the inter gravissimas papal bull, the Catholic world transitioned from the Julian Calendar to the Gregorian Calendar. This new calendar shortened the length of the year from 365.25 days to 365.2425, a reduction of just 0.002%.

One side effect of this is the modern leap year system, in which every fourth and 400th year is a leap year, but no other year divisible by 100 is. For example, this means that 1900 was a normal year, 1904 was a leap year, and 2000 was also a leap year.

Another side effect (and the topic of today’s post) was that the Julian and Gregorian calendars no longer agreed on the date. Calculating each from 1 to to 1582 AD, the Julian calendar had slowly accumulated “drift” relative to the Gregorian calendar, lagging eleven days behind it.

To correct for this drift, ten days had to be removed from the 1582 year, converting the entire system from Julian to Gregorian. Selecting an appropriate 10-day span took nearly 20 years (owing in no small part to the Catholic church’s reluctance to skip any holidays and desire to correct the Easter drift), but eventually the span of October 5 to October 14 was chosen.

The end result? On the fourth of October, 1582, citizens of the Catholic world* went to sleep and woke up ten days later, on the fifteenth of October, with a new calendar.

In essence, then, those ten days in 1582 never happened and simply do not exist within the Gregorian calendar system used almost universally in the West today.

So, how well do programming languages handle this range of dates?

Ruby

Right off the bat, Ruby does the right thing - it simply does not allow a DateTime with an impossible Gregorian date to be created:

1
2
3
4
5
6
7
irb(main):001:0> require 'date'
=> true
irb(main):002:0> DateTime.new(1582, 10, 5) # October 5, 1582
ArgumentError: invalid date
	from (irb):2:in `new'
	from (irb):2
	from /usr/bin/irb:12:in `<main>'

This can be traced to the datetime_s_civil function in ext/date/date_core.c:

1
2
3
4
if (!valid_gregorian_p(y, m, d,
		       &nth, &ry,
		       &rm, &rd))
    rb_raise(rb_eArgError, "invalid date");

Python

Python’s datetime module, despite claiming to represent a date object in “the current Gregorian calendar,” sadly does not do so:

1
2
3
4
5
6
7
8
Python 2.7.6 (default, Mar 22 2014, 22:59:56)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from datetime import date
>>> date(1582, 10, 5) # October 5, 1582
datetime.date(1582, 10, 5)
>>> date(1582, 10, 5).ctime() # October 5, 1582
'Tue Oct  5 00:00:00 1582'

This is the case for both Python 2 and Python 3.

Perl

Perl has no standard analog to Ruby’s DateTime or Python’s date, so I opted for the common DateTime module from the CPAN instead. Unfortunately, like Python, a proper error message for impossible Gregorian dates is notably absent:

1
2
3
4
5
6
7
#!/usr/bin/env perl

use DateTime;

my $dt = DateTime->new(year => 1582, month => 10, day => 5);

print $dt->ymd('/'), "\n"

This example runs without error (which is the error) on Perl 5.18, with DateTime 1.19.

Summary

Despite the fact that dates between the 5th and 14th of October in 1582 are impossible to represent on the Gregorian calendar, two out of the three languages tested allow programmers to create “Gregorian” dates for those days.

This is certainly no Y2K or Unix epoch problem, but it’s a good indicator of how far we’ve come with respect to plain and simple correctness in date and time implementations. Even if it means adding an extra check to make sure that nobody creates a date within a 10-day range nearly 500 years ago.

I hope you’ve enjoyed this quick little exploration into calendars and their intricacies. If you’re still interested, there are a ton of cool mathematical formulas and historical justifications for the current calendar system (Zeller’s Congruence, Pre-Julian Calendars) worth taking a look at.

Happy Hacking!

- William

Afternotes:

* As one might suspect, the non-Catholic churches (and religions) were less than inclined to obey an official papal bull. As a result, although the “official” calendar reform took place in the October of 1582, many protestant churches continued to use the Julian calendar well into the 18th century. The Eastern Orthodox church even continued well after that, only switching to the Gregorian calendar in 1929. These later switches required their own 10+ day removals in order to synchronize the Julian and Gregorian calendars.

P.S.:

On a whim, I checked ncal(1)’s Gregorian correctness. Interestingly enough, it may be the most correct of all. Instead of assuming that the Catholic adjustment is the most correct one, it takes a country code with the -s flag and attempts to determine when that country performed their adjustment.

For example, Italy (ncal -s IT 10 1582):

1
2
3
4
5
6
7
8
    October 1582
Su    17 24 31
Mo  1 18 25
Tu  2 19 26
We  3 20 27
Th  4 21 28
Fr 15 22 29
Sa 16 23 30

…and Great Britain (ncal -s GB 9 1752):

1
2
3
4
5
6
7
8
    September 1752
Su    17 24
Mo    18 25
Tu  1 19 26
We  2 20 27
Th 14 21 28
Fr 15 22 29
Sa 16 23 30

Although this likely takes a great deal of work, it’s by far the most correct of all.

Addendum 2026-06-30: John Costello points out that ncal(1) is still not perfectly correct, since it misses Sweden’s calendar shifts in the 1700s. Here’s a verbatim Gist of his analysis; thanks John!


联系我们 contact @ memedata.com