HOME EO | FR | EN

TAGNUMERILO

The original of this article is written in esperanto, the fair international language, in which tagnumerilo is composed of:

This english translation is by a non-native-english-speaker: take it easy, or kindly submit improvements c-;

If your internet browser forbids JavaScript, you won't be able to see or use the following JavaScript day numbering tool:

Why numbering days?

How many days are two calendar days apart?

Which date is that many days apart from this date?

Such simple questions seem harder to answer, because the time counting systems, i.e. calendars, appear irregular, full of exceptions. This is why calendar algorithms use tables for computing every date's day number, following a sequential numbering, after which the problem reduces to a simple arithmetic difference between two day numbers.

This article presents a tableless day numbering algorithm, and its inverse (from day number to date), for the current occidental Gregorian calendar, or its simpler Julian predecessor, that can also be used nowdays if only through two centuries. As both algorithms require only simple integer arithmetics, with only integer numbers and euclidian division (i.e. with integer quotient and remainder), they are very well suited for simple computing means, such as hand computation, or the cheapest (and most used) microcontrollers.

Calendar facts

The earth globe turns around its polar axis once a day vrt the sun, and around the sun once a year vrt far stars. The constant angle between these two axes causes the seasons, in synchrony with which try to be the so called "solar" calendars, such as the Gregorian and Julian ones. The present astronomical measure of one year is close to 365.2422 days.

The Julian calendar was initiated by the roman emperor Julius Caesar in 45 B.C. advised by egyptian astronoms. Its year is composed of 365 days, divided into the current 12 months, plus one "leap day", added on february the 29-th of every "leap year" multiple of four. This approximation of the year duration, meanly 365.25 day, excellent for the astronomical tools then available, however delays the Julian calendar by about 3 days every four centuries.

After sixteen centuries, the accumulated delay becoming too big, the Gregorian calendar was introduced by the catholic pope Gregor XIII in 1582. He first improved the approximation of the year duration, meanly 365.2425 days, by skipping the leap day of every year multiple of 100 but not of 400, i.e. of 3 leap days every four centuries, and moreover he resynchronized the calendar by skipping 10 days: the day following thursday 1582-10-04 was friday 1582-10-15.

The change, from the Julian calendar to the Gregorian, wasn't immediately adopted by all countries: first went the catholic ones (ex. France since monday 1582-12-20), then later the protestant (ex. England since thursday 1752-08-14), and even later the orthodox ones (ex. Russia since thursday 1918-02-14).

Tableless integer arithmetic algorithm

First, let's choose march 1st as starting point of the year period (and therefore let's number january=13 kaj february=14): to its end thus falls the occasional leap day. Moreover, a period of 5 months appears, of 31+30+31+30+31 = 153 days:
month (m): 3 4 5 6 7 8 9101112 1314
number of days: 313031303131303130313128
period: 153 153
(((m+1)*153)/5)-122 0316192122153184214245275306337

A bit of attention, to the successive multiples of 153/5 and their differences, easily reveals the formula of the fourth table line, which equals the sum of the numbers of days of the months between march 1st and the 1st day of month "m"; add to it the monthday of the date to obtain the number of days between march 1st and that date.

Between march 1st of a given base leap year (multiple of 400 for the Gregorian calendar, or of 4 for the Julian), and march 1st of the "y"-th following year, the number of days is (y*365)+(y/4)-(y/100)+(y/400) for the Gregorian calendar (or more simply (y*365)+(y/4) for the Julian one).

Then, here is the day numbering algorithm, with Y=year M=month D=day, and NG or NJ = day number under the Gregorian or Julian calendar:

In order to find the date weekday, divide N into weekly periods of 7 days (remarquably, the Gregorian four-century period of 146097 days is a multiple of 7); the remainder of the euclidian division equals:
remainder: 0123456
Julian (B=0): montuewedthufrisatsun
Gregorian: wedthufrisatsunmontue

Example: 2005-09-03, Y=2005 M=9 D=3, y=5 m=9, NJ=NG=2012=7*287+3=saturday.

Inverse algorithm

Firstly, divide the day number NG into four-century periods of 146097 days: NG=146097*Q+R (i.e. Q and R are the quotient and remainder of the euclidian division NG/146097). If R=146096, the date is the last leap day of the four-century period, february 29th of year Y=B+Q*400+400.

Otherwise, secondly divide R into century periods of 36524 days: R=36524*C+c. The previous two first steps are only adequate for the Gregorian calendar; for the Julian, start here with c=NJ.

Thirdly, divide c into four-year periods of 1461 days: c=1461*q+r. If r=1460, the date is a leap day, february 29th of year Y=B+Q*400+C*100+q*4+4.

Otherwise, fourthly divide r into year periods of 365 days: r=365*i+d, and compute y=B+Q*400+C*100+q*4+i, and m=(((d+31)*5)/153)+2, and D=d-(((m+1)*153)/5)+123.

Finally, if m>12, compute Y=y+1 and M=m-12, otherwise Y=y and M=m. The date is Y=year M=month D=day.

Example: NJ=2012, q=1 r=551, i=1 d=186, y=2005 m=9 D=3, Y=2005 M=9, 2005-09-03.

Uses of the Julian algorithm

First, 16 to 19 centuries of history were dated under the Julian calendar. For example, during Zamenhof's life, russians were still using the Julian calendar (until its wednesday january 31st 1918, which was the Gregorian february 13rd), then 13 days late vrt the Gregorian, already in use by western countries for three centuries.

Second, microcontrollers compute less costly with 16 bits numbers, between 0 and 65535, i.e. the number of days of around 180 years. As between 1900-03-01 and 2100-02-28 the Gregorian and Julian leap years are identical, the simpler Julian algorithm may be used with for instance 1900 as base year (1900-03-01 = day number 0, until day number 65535 = 2079-05-05).

Codings

In JavaScript:
look at the beginning of the HTML source code of this page.
In C:
static int gregorian = 1; // 1:Gregorian 0:Julian
typedef struct{int day, month, year;} date; // 1..31, 1..12, ..2005..
int daynumber(date d) { // convert date into day number
  if(month<3){ d.year-=1; d.month+=12; } // move origin to march 1st
  return d.year*365 + d.year/4 + (gregorian ? -d.year/100 + d.year/400 : 0)
       + (d.month+1)*153/5-123 + d.day;
}
int weekday(int n) { // convert day number into:
  return (gregorian ? n+2 : n) % 7; // 0=monday .. 6=sunday
}  
void daydate(int n, date *d) { // convert day number into date
  int m, y=0;
  if(gregorian) {
    m = n/146097;  y += m*400;  n -= m*146097; // 400 years periods
    if(j==146096) { d->an=a+400; d->mois=2; d->jour=29; return; }
    m = n/36524;   y += m*100;  n -= m*36524;  // 100 years periods
  }
  m = n/1461;  y += m*4;  n -= m*1461; // 4 years periods
  if(n==1460) { d->year=y+4; d->month=2; d->day=29; return; }
  m = n/365;   y += m;    n -= m*365;  // 1 year periods
  m = (n+31)*5/153+2;     n -= (m+1)*153/5-123;
  if(m>12) { y+=1; m-=12; } // restore origin to january 1st
  d->year=y; d->month=m; d->day=n; return;
}
In Forth :
1 value gregorian  \ 1:Gregorian 0:Julian
: date>  \ d m y -- n ; convert date m/d/y into day number n
  >r  dup 3 < IF 12 + r> 1- >r THEN  \ move origin to march 1st
  1+ 153 * 5 / 123 -  +  \ -- n | y ; n days from 3/1/y au m/d/y
  r@ 4 / +  gregorian IF r@ 100 / - r@ 400 / + THEN  r> 365 * +
;
: >weekday  \ n -- w ; convert day number n into:
  gregorian IF 2 + THEN  7 mod  \ 0:monday .. 6:sunday
;
: >date  \ n -- d m y ; convert day number n into date m/d/y
  0 >r  gregorian IF
    146097 /mod 400 * r> + >r  \ 400 years periods
    146096 over = IF drop 29 2 r> 400 + exit THEN
    36524  /mod 100 * r> + >r  \ 100 years periods
  THEN  \ -- n | y
  1461 /mod 4 * r> + >r  \ 4 years periods
  1460 over = IF drop 29 2 r> 4 + exit THEN
  365  /mod     r> + >r  \ 1 year periods
  dup 31 + 5 * 153 / 2 +  tuck 1+ 153 * 5 / 123 - - swap  \ -- d m | y
  dup 12 > IF 12 - r> 1+ >r THEN  r>  \ -- d m y
;

Links

CL20080702