Loren on the Art of MATLAB

Turn ideas into MATLAB

Calendars and Leap Years

I'm pleased to introduce Ned Gulley as this week's guest blogger. Ned is a software developer with an amateur's interest in astronomy. He was motivated by a recent discussion of numerical precision in this space to put together this piece on the precision of calendars.


If you pile up enough of them, small errors can turn into big problems. For instance: how long is a year? More specifically, if you ignored the leap day every four years, how long would it take to notice something was wrong?

We measure time according to the motion of three different bodies in the Solar system: sun, moon, and earth. The rotation of the earth around its own axis relative to the sun is the basis for our day, the rotation of the earth around the sun defines the year, and the rotation of the moon around the earth relative to the sun defines the month (at least it did originally). These motions are essentially unrelated, but we try to fit them all into a single calendar anyway.

Historically we have defined the day as the amount of time from noon to noon. This is relatively easy to measure. We could measure the year this same way: the amount of time from, say, solstice to solstice. But we don't do this. Instead we try to compute it by counting the number of days. This causes all kinds of problems.

We wish that the number of days in a year was a nice round number, but it isn't, and there's no reason to expect it to be. It's as if you were to pick two numbers at random and expect one to cleanly divide the other. Over the centuries, we've managed to "fix up" the calendar of days with leap days so it fits a solar year. Lunar calendars are even more of a mess and require great exertions to keep on track with a solar year. The Hebrew calendar is lunar, and it periodically inserts extra months to make up the shortfall. The Islamic calendar is also lunar, but it does not attempt to stay synchronized with the solar year. Thus the month of Ramadan, for example, moves slowly through the seasons with each passing year, sometimes occurring in the summer and sometimes in the winter.

The Egyptian calendar

So how many days are in a year? It's not exactly 365, but it's close. Does the difference matter? Let's investigate. The length of a seasonal year is 365.24219878 days (more or less). Consider a primitive calendar exactly 365 days long with no leap years. Such a calendar was actually used by the ancient Egyptians.

seasYearLen = 365.24219878;
seasYearRem = seasYearLen - 365;

n = 0:1:2000;

% The Egyptian calendar falls behind by the remainder every year
egyptDev = -n*seasYearRem;

ylabel('accumulated error in days')
title('Deviation between calendar and season')

line([0 n(end)],-seasYearLen*[1 1],'Color','red')
crossover = seasYearLen/seasYearRem;
annotation(gcf,'textarrow',[0.7036 0.7161],[0.549 0.3595],...
    'String',{'Accumulated error','equals one year!'});

Without the leap year correction, every four years the calendar year falls behind the seasons by one day. After 754 years, the seasons would be 180 degrees out of phase with the calendar. The first day of autumn in the northern hemisphere would be March 21st instead of September 21st. After 1508 years, the calendar would go all the way around the seasons and would once again be synchronized with the sun.

The Julian calendar

n = 0:10000;
egyptDev = -n*seasYearRem;
egyptHandle = plot(n,egyptDev);
ylabel('accumulated error in days')
axis([0 200 -5 5])

julDev = egyptDev + floor(n/4);
julHandle = line(n,julDev,'Color','red');
line( ...
    [0 length(n) NaN 0 length(n)], ...
    [1 1 NaN -1 -1], ...
    'LineStyle',':', ...
    'Color',0.4*[1 1 1])
legend([egyptHandle julHandle], ...
    {'Egyptian','Julian'}, ...

Adding a leap year day every four years helps, but as you can see there is still some significant drift. After 200 years we're already a day out of synch, but in the other direction. We've made the calendar year longer than it ought to be.

The Gregorian calendar

The Gregorian calendar, named after calendar reformer Pope Gregory XIII, solves this problem by leaving out three Julian leap-days every 400 years. Algorithmically, if the year is divisible by 100, avoid the leap-day UNLESS the year is divisible by 400.

gregintDev = julDev - floor(n/100);

gregDev = gregintDev + floor(n/400);
% line(n, gregintDev,'Color','magenta')
gregHandle = line(n, gregDev,'Color','green');
axis([0 600 -20 20])
legend([egyptHandle julHandle gregHandle], ...
    {'Egyptian','Julian','Gregorian'}, ...

Zoom in and you can see that after 600 years or so we have drifted by a day.

axis([0 600 -5 5])

The modern calendar

The modern calendar does one final tweak on this: if the year is divisible by 4000, leave out the leap-day despite the Gregorian recommendation above.

modernDev = gregDev - floor(n/4000);
modernHandle = line(n, modernDev,'Color','black');
axis([0 10000 -20 20])

legend([egyptHandle julHandle gregHandle modernHandle], ...
    {'Egyptian','Julian','Gregorian','Modern'}, ...

To account for variation beyond this, we insert leap seconds, so we should be in good shape for a long time to come.

Published with MATLAB® 7.3

  • print
  • send email


댓글을 남기려면 링크 를 클릭하여 MathWorks 계정에 로그인하거나 계정을 새로 만드십시오.