ENOSUCHBLOG

Programming, philosophy, pedaling.


Mini-post: Dates That Don't Exist

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.