Calendar Logic

Contents

Leap Year
Days in Month
First Day of Month



When Is It Leap Year?

In the sixteenth century, Pope Gregory had learned from the astronomy of his day that the earth orbited around the sun every 365 and 97/400th days.  If every fourth year is a leap year, this would make 100/400 of the years leap years, which is 3 too many.  To get 97, we omit 3 leap years out of every 400 year cycle.  Which 3 should that be?  Pope Gregory decided it should be the first three century years of every 400 year cycle.

Thus, of the following century years, the ones in red are not leap years:

100 200 300 400
500 600 700 800
900 1000 1100 1200
1300 1400 1500 1600
1700 1800 1900 2000
2100 2200 2300 2400 etc.
Logic:

Testing a year to see if it is a leap year

Is it divisible by 4?

Yes
Is it divisible by 100?
Yes
Is it divisible by 400?
Yes
(these
are leap
years)
No
No

(these are
leap years)

No

Thus, it's a leap year if

  • it is divisible by 4
  • it is not divisible by 100
  • unless it is divisible by 400
Sample code: (slide left/right to view)
 
Pascal C++
nested
Leap := false;
if Y mod 4 = 0 then
    if Y mod 100 <> 0 then
        Leap := true
    else
        if Y mod 400 = 0 then
            Leap := true;
bool Leap = false;
if (y % 4==0)
    if (y % 100!=0)
        Leap = true;
    else
        if (y % 400==0)
            Leap = true;
complex boolean
if (Year mod 4 = 0) 
    and ((Year mod 100 <> 0) 
    or (Year mod 400 = 0)) then
        Leap := true
else
        Leap := false;
if ( 
    (y % 4==0) &&
    ((y % 100!=0) || (y % 400==0))
   )
    Leap = true;
else
    Leap = false;
assignment of boolean expression
Pascal:
Leap := (Year mod 4 = 0) and ((Year mod 100 <> 0) or (Year mod 400 = 0));
C++:
Leap = (y % 4==0) && ((y % 100!=0) || (y % 400==0));

C++ function:
bool LeapYear(int y)
{
    return (y % 4==0) && ((y % 100!=0) || (y % 400==0));
}



How Many Days in That Month?

Every month in the Gregorian Calendar has a fixed number of days except February which has 29 days in a leap year and 28 days in a non-leap year.  Jan, Mar, May, Jul, Aug, Oct, Dec have 31 days.  Apr, Jun, Sep, Nov have 30 days.

Thus, a function with a simple switch statement works nicely:

C++ function:
int DaysInMonth(int m, int y)
// m is the month number (1,2,3,...12), y is the year number (four digits)
{
    switch (m)
    {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12: return 31;
        case 2: if (LeapYear(y))
                    return 29;
                else
                    return 28;
        default: return 30;
    }
}

Notice in the switch statement above, "break" is never needed because "return" exits not only the switch statement but also the entire function.



What Day of the Week Does A Month Begin On?

There are various methods of computing the day of the week, but the most straightforward method is simply to start with a known day, count the number of days, and then use modulo 7.  We can speed up the process by counting years, as follows:

Let 0 through 6 represent the days of the week Sunday through Saturday respectively.

In the Gregorian Calendar, January 1, 1583, was a Saturday, so we will start with 6.

int dow = 6; // dow is our "day of the week" variable

January 1 of a given year will advance one day of the week in the following year unless the given year was a leap year in which case January 1 will occur two week days later.  So, for all the years prior to the current year (y), we simply advance dow using a loop:

for (int i=1583; i<y; i++)
{
    dow++;
    if (LeapYear(i))
        dow ++;
}

Alternate version:

for (int i=1583; i<y; i++)
    dow += (1 + (int)(LeapYear(i)));

Second alternate version:

for (int i=1583; i<y; i++)
    dow += (LeapYear(i)) ? 2 : 1;

Now all we do is advance dow to the first day of the current month by adding in the days in the months prior to the current month (m):

for (i = 1; i<m; i++)
    dow += DaysInMonth(i,y);

Since weekdays repeat, our number dow is modular, which means we must use modulo 7 to get the actual number of the day of the week:

dow %= 7;

dow not equals a value, 0 through 6, which stands for the day of the week on which the current month begins.

Putting it altogether in a C++ function:

int FirstDayOfWeek(int m, int y)
{
    int i;
    int dow = 6;
    for (i=1583; i<y; i++)
        dow += (LeapYear(i)) ? 2 : 1;
    for (i=1; i<m; i++)
        dow += DaysInMonth(i,y);
    return dow % 7;
}