Jun 9, 2015 Tags: programming
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?
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’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 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.
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.