logo To Foot
© J R Stockton, ≥ 2008-04-26

The Calendrical Works
of Rektor Chr. Zeller :
The Day-of-Week and Easter Formulae
.

No-Frame * Framed Index * Frame This
Links within this site :-

Note : Elsewhere, I may continue to use my own Day-of-Week code, and Easter formulae taken from the Calendar FAQ.

Introduction

Each of Rektor Zeller's four calendrical papers deals with determining the Day-of-Week from the date, and the date of Easter Sunday from the year number, in the Julian and Gregorian calendars. They are all similar, and basically equivalent mathematically.

These pages address the questions "What did Zeller actually state?", "What expressions derive directly from his work?", "What else did he do?", etc. They present the papers in English (1882, 1883, 1885, 1886), and show images of the actual papers; and they give expressions suitable for computer use and provide tests of Zeller's results.

The term "Zeller's Congruence" refers to the Day-of-Week calculation, and is frequently used in connection with widely varying expressions (not all reliable) for determining the day of the week or the lengths, or accumulated length, of months. As such, it is treated separately on this page.

Zeller's Life

Rektor Christian Zeller (1822-99) was a protestant minister in Markgröningen (Württemberg, SW Germany; 20 km NW of Stuttgart). E.G.Richards, in his book "Mapping Time", states that Zeller was a director of a women's teacher training college and a school inspector.

Reported : Bibliographical data according to Poggendorff :- Christian Julius Johannes Zeller, 1822..1899.

In 1882, 1883, 1885, & 1886, Zeller published formulae for relating the Day-of-Week to the (A.D.) date, and for calculating the date of Easter Sunday, for both the Gregorian and the Julian Calendars.

Zeller Family Material

2004-04-18 ff. :- A Web search found § Zellertag 2001, then containing "Zum Zellertag laden wir in diesem Jahr nach Markgröningen ein. Dort war Christian Zeller (ZB 136) von 1868 bis 1898 der erste Rektor des Lehrerinnenseminars (heute Helene-Lange-Gymnasium)" and "15.00 Uhr ... Vortrag von Herbert Leube über Rektor Christian Zeller".

Some links on that page :-

2006-01-30 : Pages marked § are no longer there.

The presentation by Dr H Leube (a great-grandson of Zeller) was published in "Nachrichten des Martinszeller Verbands", Dec. 2001, p.8-17. He has kindly supplied me with a private (h w e) copy of his text to study, from which the following box of information is taken.

Julius Christian Johannes Zeller, born the 10th of 11 children in 1822, studied Mathematics and Geography in his youth, and later Theology. He was married twice, having 9 children and 36 grandchildren. In 1874, he became Director of the Seminary in Markgröningen, together with the girls' orphanage there.

In addition to his Date papers, it appears that he published papers about number theory :-

In 1882 he was admitted into the Société Mathématique de France. His later honours included the Order of Friedrich, First Class, and the Ritterkreuz of Württemberg. He retired in 1898, and died in the following Summer.

There is a family book: "Gerhard Zeller, Nachfahren der Familie Zeller aus Martinszell, Stuttgart 1995", price EUR 25 plus postage.

A family tree for the children of Zeller's son Heinrich, covering five earlier generations, has been seen at § Ahnentafel.

Wikipedia :-
• In German : Christian Zeller, Zellers Kongruenz.
• In English : Christian Zeller, Zeller's congruence.

Zeller's Date Papers

References

  1. "Die Grundaufgaben der Kalenderrechnung auf neue und vereinfachte Weise gelöst", Zeller, Chr., Württembergische Vierteljahrshefte für Landesgeschichte, Jahrgang V (1882), pp. 313-314. - in German.
  2. "Problema duplex Calendarii fundamentale;" Par M. Ch. Zeller, Bulletin de la Société Mathématique de France, vol.11, pp.59-61, (Séance du 16 mars 1883) - in Latin.
  3. "Kalender-Formeln" von Rektor Chr. Zeller, Mathematisch-naturwissenschaftliche Mitteilungen des mathematisch-naturwissenschaftlichen Vereins in Württemberg, ser. 1, 1 (1885), 54-58. - in German.
  4. "Kalender-Formeln" von Chr. Zeller, Acta Mathematica, vol.9 (1886-7), pp.131-6. (Nov 1886) - in German. There was a transcription in HTML at Polysyllabic (#).

As far as I know, Rektor Zeller did not publish anything related to date or time other than the papers listed above.

Credits

Sebastian Koppehel of the University of Hamburg kindly provided me, by E-mail in April 2002, with a translation of the Day-of-Week section of Zeller's Acta Mathematica paper of 1886, and his comment on it.

I have received, through the good offices of Dr DM Herbert of Imperial College London, copies of the 1883 and 1886 Zeller papers cited above; and likewise, through Dr D R de Lacey of Cambridge, a copy of the 1885 paper.

Tony Brook of London SW20 gave me a translation of the 1883 paper, in spite of it not being in the sort of Latin that Caesar would have used.

Robert H. van Gent of the University of Utrecht sent me a copy of the 1882 paper; Philip Newton of Hamburg helped with the difficult paragraphs.

Anthony Holley scanned copies of the printed pages for me.

Principles & Practice

Algorithms

Day-of-Week

The Day-of-Week is in essence the remainder obtained by dividing the Day-Count by seven :-

  1. Day-Count rises with Day-of-Month
  2. For March-based years, Zeller observed that Day-Count rises near-linearly with Month, and the length of February becomes irrelevant,
  3. For March-based years, Day-Count rises by 365 per year, with correcting terms for each applicable Leap Year Rule depending only on the year
  4. If only Day-of-Week is needed, multiples of seven can be omitted throughout.

For Rule 2, Zeller used a factor of 26/10, equivalent to 30.6 days per month, presumably the simplest to work with. It appears that 367/12 = 28 + 31/12 = 30.583 days per month, which might be considered more logical*, can also be used.

* : If February were an "ordinary short" month, the year would have 367 days.

Easter Sunday

The Easter calculations are presumably derived, somehow, from the data auxiliary to the Papal Bull of 1582; I have found the Bull, and maybe the auxiliary data, on the Web. See in my The Date of Easter Sunday. Zeller's Easter functions, as implemented here, seem to be fast by comparison with some more recent versions.

Modulo Warning

For both Day-of-Week and Easter, the Modulo-N (remainder on division by a constant) operation is needed. Computers tend not to give the desired answer when the variable argiment is negative. This can affect Week and Century, but is easily avoided or corrected.

See Likely Errors in Implementations below.

Performance

Subject to the modulo warning above, and apart from the simplification noted for 1886 II.B.2, I believe Zeller's algorithms to be completely accurate for all years from A.D. 1; and for earlier years if the year number is treated thoughtfully.

Earlier years can be safely accommodated by adding a sufficient multiple of the appropriate calendar repeat interval - 28 or 400 years for the Julian and Gregorian secular calendars, and 532 or 5700000 years including Easter.

For computer use, the Day-of-Week algorithm can be slightly improved.

On Zeller's Published Date Papers

Overview

The 1885 and 1886 papers are, basically, expansions of the 1883 one, which is itself a version of that of 1882.

In each of these four papers, the first section deals with determining the Day-of-Week from the Date, and the second section deals with determining the date of Easter Sunday from the Year; in each case, for both the Julian and the Gregorian Calendars and for any year A.D.

Zeller's methods were, naturally, intended for intelligent implementation; he did not need to be as careful about negative numbers as a programmer should be.

Translations of the 1882, 1883, 1885, 1886 Papers

For reasons of size and structure, the translations of the papers themselves are now, with their specific Notes and Tests, on separate Web pages (each of which links to a page of images of the original printed pages :-

It is impossible to reproduce in HTML the exact layout of the originals; but they have been used as a guide.

Common Notes

Zeller used "century" for the year number truncated by two digits; I use "centade" elsewhere.

Day-of-Week

Zeller numbered the days of the week from Sunday=1 to Saturday=7 (or 0). This differs from ISO 8601, which uses only Monday=1 to Sunday=7. Convert to ISO by ((W+5) mod 7) + 1 ; the 5 can be added within the main expression.

Zeller nowhere mentioned that in his numbering Saturday, the seventh day of his week, is represented by 0.

Zeller explicitly recognised the alternative of addition in the formulae of 1882 (J G), 1883 (G), 1885 (J G), but not of 1886.

Julian
Gregorian

Easter

The date of Easter is that of Easter Sunday.

Julian

2007-05-15 : I have discovered that a mod-7 effect similar to Gregorian paragraph 2 below can affect Julian (it was noted as possible); correction applied today; discussion to be improved when time permits.

Gregorian

Probably 1583-1700, 1700-1900, 1900-2200 should be 1583-1699, 1700-1899, 1900-2199. The first Easter celebrated under the Gregorian rules was that of 1583.

The last term in the first expression in the third instruction contains a factor of -2; in order to avoid occasional mod-7 error in computing the remainder d, change it to +5.

For the special cases in the third instruction, a>10 is potentially discrepant with a<10 in the corresponding Example. Testing with a≥10 shows that the difference is significant, but only in the years 675, 827, 7515, 7610, 7762 ..., and that a>10 is correct. The Examples are all for 1886, where a=5.

Likely Errors in Implementations

Both for Day-of-Week and for Easter, the remainder-after-division operation (modulo) will be needed. Care should be taken to consider the possibility of its argument being negative, since computers tend not to give the desired answer in that case.

This applies to splitting YYYY into CCYY, for negative years; and in other parts of the work, for some input values.

Zeller expected that expressions would be evaluated manually. It seems, in the 1882 and 1883 papers, that he was aware that a negative term might make the argument of a sevens-remainder operation in a Day-of-Week calculation go negative; but he would have expected this to be dealt with sensibly, by first adding a sufficient multiple of seven.

Unfortunately, the usual computer implementation of the modulus operator does not work that way; it treats (-A) mod B as -(A mod B). For my views on this, see, for example, my discussions for JavaScript and Pascal.

More details may be found on the pages for the individual papers.

The simplest correction is to ensure that the argument never goes negative, by replacing a negative term with an equivalent positive one, after Zeller himself. Alternatively, if the result is negative then add the divisor.

Test boxes in the pages for the papers demonstrate the effect in the Day-of-Week formulae; the Easter code is corrected.

Incautious computer implementations are liable, therefore, to error; some are known to have been wrong. Programmers may not have thought to test the necessary dates.

Day-of-Week

It happens that the modulo-7 error occurs for a fraction of dates, in March onwards, early in each "century", Saturdays excepted; simple testing before Y2k may have missed it. For example, an Internet Draft has been seen to need correction (and has received it).

Risks Digest, 22.18 #10, refers.

Sites seen with Negative mod Expressions

2005-06-04 : Page http://www.computerbase.de/lexikon/Zellers_Kongruenz used -2*Jh, cites this page; but I see no warning in it. It looks to be a copy of an earlier version of ...

2005-06-05 : Page http://de.wikipedia.org/wiki/Zellers_Kongruenz recognises the problem but deals with it in an inferior manner - "Ist das Ergebnis negativ (je nach verwendeter Modulo-Funktion), so addiert man 7 hinzu, so dass eine positive Zahl entsteht." - it would be better to change -2*Jh to +5*Jh.

2005-11-15 : The more recent English version has the citation and the error, without correction.

2006-02-04 : Two substantially identical Wikipedia pages entitled Zeller's congruence display (algebraically) the dangerous form. Although there is a note on the problem, the obvious correction (as shown by Zeller in 1882) is not indicated. They give a pseudo-code Day-of-Week algorithm which uses a table indexed by month, and which is therefore not Zeller's.

2006-06-18 : The dangerous form is still shown algebraically, but with notes on the correction needed, in both the English and the German pages.

Easter Sunday

A modulo problem can also arise in the Easter work; 2002 is an example.

Implementation of Zeller's Formulae

The algorithms of each paper are implemented, in JavaScript, in include file zell-inc.js and are shown at JavaScript Include Files.

In Day-of-Week, "Xdow" results are for the expressions given directly by Zeller, and "Xpro" results are protected against "modulo 7" error.

For Easter Sunday, results are protected where that has been found necessary.

Testing

There are manual tests, Julian and Gregorian, at the end of each "Paper" page.

Watch out for special cases such as, but not necessarily limited to, the modulo 7 trap. The tests on this page use versions that avoid the trap.

For A.D. 1-32000, results can be checked by my program mjd_date.

Best is a full check over the repeat interval for the calendar, combined with a check that the year-part of the implementation itself then repeats.

The Gregorian testers below get very slow, or fail, with complete output for large ranges (a JavaScript limitation).

Julian testing is left for those who need it; see in JavaScript Date and Time 8 : Enhancing the Object for adding Julian Calendar methods to the Date Object.

The test code here is limited by the integer range of the JavaScript Number type (253 ~ 9×1015), and for Day-of-Week by the range of the JavaScript Date Object (±108 days ~ ±275000 years).

Testing Day-of-Week

The variations of Day-of-Week with year, month, and day are substantially independent, though the February-March transition needs particular attention. If the days of a chosen month are correct, and the first days of each month of a chosen year are correct, and the first days of March of each year of a moderate set including all year types and the early 2000s are correct, then the implementation is probably about right.

Julian

Include both Julian types (e.g. 1580, 1581); the calendar repeats after each 28 years.

Gregorian

Include all four Gregorian types (e.g. 2000, 2004, 2005, 2100); the calendar repeats after each 400 years.

My greg-cal.txt gives the Day-of-Week for every day in 1970-2030; my greg-1mo.txt gives the Day-of-Week for the first day of every Gregorian month in 1600-2800.

Check of Gregorian Day-of-Week Formulae

A full check, needing 400 years, has been done.

Years : -      

Enter a small year range and press Test. The 'Good' column is the result from a Date object. Unless the box is checked (slow), only days with discrepancies would be shown, marked by '****' in the 'Err?' column.

The dated columns use the corresponding functions Gpro####(), in file zell-inc.js .

Testing Easter

Julian

A full check would need 532 years.

Gregorian

My estr-tbl.txt gives the date of Gregorian Easter Sunday for 1900-2150.

Check of Gregorian Easter Formulae

A full check, needing 5,700,000 years, has been done.

Years : -
    as "Day-of-March"

Enter a modest year range and press Test. Unless the box is checked (slow), only years with discrepancies are shown. Discrepancy would be marked by '****' in the 'Err?' column.

The dated columns use the ZEG####() functions for the corresponding paper, in file zell-inc.js .

The 'EGRP' column is the result from the independent, tested function EGREaster(Yr) in file estr-inc.js which is E.G.Richards' Algorithm P.



P4 3GHz XP sp2 IE6 : 5 seconds.

On individual Papers by Zeller :- 1882, 1883, 1885, 1886.



Material Related to Zeller's Congruence

This section mostly contains material derived from Zeller's Day-of-Week work. See also Date and Day Count and Week Number Calculation.

On Zeller's Congruence

This represents the remainder of my original Zeller Algorithm material, substantially dating from before I saw the Papers; thus it is based mainly on popular report. The only mention of month-lengths that I can see in Zeller's papers is in 1886.I.3, and in corresponding parts of those of 1885 & 1882.

Overview

Complications in the Day-of-Week calculation are minimised by starting the day and month counts with March, thus not needing the length of February itself.

The Day-of-Week formulae appear useful for programmable calculators; but in a program for a true computer one can also use table lookup.

The resulting Day-of-Week number may need to be transformed, e.g. for compatibility with ISO 8601, where the week starts with Monday = 1.

Principle

In essence, the Congruence embodies the fact that the total number of days in excess of four weeks per month from the beginning of March up to but not including Month M is given by :-
  for March as M=0,  S := ((13*M+2) div 5) ,
  for March as M=1,  S := ((13*M+4) div 5) - 3 ,
  for March as M=3,  S := ((13*M+3) div 5) - 8 ,   which Zeller himself used.
These give, for the beginnings of the months from March to February,
  0 3 5, 8 10 13, 16 18 21, 23 26 29 days.

Zeller's method proper does not, however, calculate the total number of days explicitly.

Zeller did not, I think, state that the sums of the last decimal digits of the months are given by similar formulae, but with 3*M.

JavaScript Test

This is a basic test of Zeller-type expressions for the accumulated days in excess of four weeks per month.

Zeller's Congruence in Pascal and Delphi

The following is a direct implementation in (Borland) Pascal of Zeller's Acta Mathematica (1886) method, by SK & myself; it is configured to fit in with my dateprox.pas, where it now resides :-

function EchtZeller(const Cal : Calendar ; J, m, q : word) : byte { ISO } ;
var K, h : word ;
begin
  if m < 3 then begin Inc(m, 12) ; Dec(J) end ;
  K := J mod 100 ; J := J div 100 ;
  h := q + (m+1)*26 div 10 + K + K div 4 ;
  case Cal of
    Julian    : h := h +    5    + 6*J ; { Z had   -J; +6*J avoids negative }
    Gregorian : h := h + J div 4 + 5*J ; { Z had -2*J; +5*J avoids negative }
    else RunError(245) end ;
  { Now adjust to ISO 8601} EchtZeller := Succ((h+5) mod 7) ;
  end {EchtZeller} ;

It has been tested from AD 0001-01-01 to AD 32100-12-31, on the Julian and Gregorian calendars, in Borland Pascal 7 and Delphi 3.

Zeller's algorithms can be optimised; see the JavaScript above, which for Gregorian Day-Count gives this Pascal/Delphi (tested by programs/zel_cmjd.pas and to be developed in programs/mjd_date.pas) :-

function YMDzuCMJD(Y, M, D : longint) : longint { Gregorian } ;
  begin { Inc(Y, 7e5) ; Dec(Result, 7e5*365.2425) ;; 153 = 13 + 5*28 }
  if M<3 then begin Inc(M, 12) ; Dec(Y) end ;
  YMDzuCMJD := -678973 + D + (M*153-2) div 5 +
    Y*365 + Y div 4 - Y div 100 + Y div 400 end {YMDzuCMJD} ;
( Slightly faster with Y*1461 div 4 ? )
( Speed depends on compiler and variable types )

Other Implementations for Day-of-Week

The full Zeller formula for the day of the week (N : Sun=0..Sat=6) of the first of the month seems, in Pascal terms, to be equivalent to :-
  M := 1 + (Month + 9) mod 12 ; if M>10 then Dec(Year) ;
  C := Year div 100 ; D := Year mod 100 ;
  N := ((13*M-1) div 5 + D + D div 4 + C div 4 + 5*C + 1) mod 7 ; ?
  N := ((13*M-1) div 5 + D + D div 4 + C div 4 + 5*C) mod 7 ; ?

(verified; in dateprox.pas). Some implementations may be over-simplified and of limited range? Versions with -2*C may give a negative intermediate.

Ordinal Date

See in Date Object Information.

Mike Keith's Day-of-Week Congruence

In Calculating the day of the week, Mike Keith has given, as the shortest C Day-of-Week expression (45 characters) :-

(d+=m<3?y--:y-2,23*m/9+d+4+y/4-y/100+y/400)%7 // Sun = 0

It is not Zeller's Congruence, but apparently Mike Keith's, given in Journal of Recreational Mathematics, Vol. 22, No.4, 1990, p.280. It gives Sun=0..Sat=6.

Note that the values of y, d are not preserved; and that one can derive code for daycount.

In news:comp.lang.javascript, 'rh' has put that into JavaScript :-

Test one day per month in 2000-2400     Scroll the code to see the test code.

Reductions

August 2006 : Linus Flannagan has reduced it by one character :-

(d+=m<3?y--:y-2,23*m/9+d+4+y/4+y/100*25/4)%7

Mike Keith then reduced it by one more character, and produced yet another form :-

((m<3?y--:y-2)+23*m/9+4+d+y/4+y/100*25/4)%7
((m<3?4+y--:y+2)+23*m/9+d+y/4+y/100*25/4)%7

   

As Math.floor(x/y) can be replaced by ((x/y)|0) these cannot be the shortest or fastest JavaScript code.

ISO 8601 Weeks

Those give Day-of-Week as Sun=0 to Sat=6; but international usage is Mon=1 to Sun=7; reduce the argument of %7 by 1 and then add 1.

Day-of-Week & Day-Count for Computers

Zeller only dealt with Day-of-Week, but extension to Day-Count is easy. Day-Count is more useful, and can easily be converted to Day-of-Week :-

DoW := 1 + (DC+K) mod 7 { K depends on origin ; beware DC+K<0 } ;

Zeller, with manual evaluation in mind, made arithmetic easier by splitting the year number, but a computer has no need of this.

The Gregorian rules are exhibited more clearly below; the scheme is not new, but can be found for example in "Mapping Time" by E.G.Richards. As in Zeller's own formulae, division is integer division.

The code is implemented in JavaScript here. It uses the integer bitwise OR operator; here (a/b)|0 is equivalent to a div b in Pascal.

For dates before 0000-03-01, one can add a multiple of 400 (Gregorian), 28 (Julian), or 2800 (both) years to y and for Day-Count adjust the constant term correspondingly.

For dates before 0000-03-01, or in the far future, replace the 32-bit integer operations.

Gregorian Day-of-Week

Years have 52 weeks plus 1 or 2 days; so successive years start one day of the week later, and another day later after a leap year.

if (m<3) { m = m+12 ; y = y-1 } // just as Zeller
DoW = (2 + d + (13*m-2)\5 + y + y\4 - y\100 + y\400) % 7 } // Sun = 0
// the operator \ is integer division, div.

For positive y it cannot generate a negative (or unreasonably large) intermediate.

The replacement of part of the code line marked <20060127 saves a little time; and |0 is faster than floor .

 
   

Gregorian Day Count

Years have 365 or 366 days. The month term is 28 days bigger.

The constant is chosen to give CMJD, with base date CMJD=0 being A.D. 1858-11-17 local time.

  if (m<3) { m = m+12 ; y = y-1 } // just as Zeller
  DNo = -678973 + d + (153*m-2)/5 + 365*y + y/4 - y/100 + y/400



P4 3GHz XP sp2 IE6 : 6 seconds.

YMDzuCMJD(y, m, d) was much faster than new Date(y, m', d) in MSIE4

and is of similar speed to Date.UTC(y, m', d)


It seems that in Opera 9.21 (UK), but not FireFox and MSIE, function LongTest wrongly indicated failure because the assignments of new Date(2001, 0, K) did not always give successive dates near BST changes from 2038. Adding 6 hours fixed it.

Julian Equivalents

For the Julian Calendar, omit the terms containing 100 and 400. For Day-of-Week, omit also the initial 2 term; for Day-Count, subtract 2 from the constant term. Check those.

Day-count to Date

The reverse calculation can be done in various ways; I seek a style-matching version. See in Date and Day Count.

See also
Calendar Weeks, Week Number Calculation, The Date of Easter Sunday,
Date and Day Count, Date Miscellany I, Date Miscellany II; and Time Miscellany.
Home Page
Mail: no HTML
© Dr J R Stockton, near London, UK.
All Rights Reserved.
These pages are tested mainly with MS IE 6 and W3's Tidy.
This site, http://www.merlyn.demon.co.uk/, is maintained by me.
Head.