Fun with the Gregorian Calendar

Without a proper method it’s not really possible to calculate the day of the week of a day of the year. A trivial proper method is to look it up into a calendar, be it analogical or digital. Of course you can do that, but it’s not nearly as fun as the method I’m going to describe here.

First and foremost, we know that a year is 365 days, which in turn is 52 weeks plus one extra day. So, if it was easy, we could infer that today one year in the future has the next day of the week from today’s. For example, today is 2013-04-27 and is Saturday, so 2014-04-27 should be Sunday, which is. Analogously it goes the other way around, 2012-04-27 should be Friday, which is. OK, so a proper method could be simply to either add as many days as there are years in the future or subtract as many days as there are years in the past to get from today to the desired date.

Sadly, it’s not that easy, even if we stay into the limits of the Gregorian Calendar. In fact it’s much more difficult because of leap years, happening at a rate of one each four years… approximately. The real succinct formula for finding out if a given year is a leap year is as follows:

A year is a leap year if it's divisible by 400 or it's divisible by 4 but not by 100.

For example, year 2000 was a leap year, but 1900 was not, and 2012 was, but 2013 is not.

The next important bit is to find out how many leap years there are between any two dates. We could easily find it if we already knew how many leap years there are between the 1st day of March of the years of those two dates. In fact,

leap_years(date1, date2) = leap_years(date1, march1st(year1)) + leap_years(march1st(year1), march1st(year2)) + leap_years(march1st(year2), date2)

For example, if date1 was 1800-01-10 and date2 was 2000-10-01, then

leap_years(1800-01-10, 2000-10-01) = leap_years(1800-01-10, 1800-03-01) + leap_years(1800-03-01, 2000-03-01) + leap_years(2000-03-01, 2000-10-01)

and in this case it would be

leap_years(1800-01-10, 2000-10-01) = 0 + x + 0 = x

Both the first and the last term are 0 because (first) 1800-01-10 < 1800-03-01 but 1800 is not a leap year and (last) 2000 is a leap year but 2000-03-01 < 2000-10-01.

To find out how many leap years there are between the 1st day of march of any two years, we can use a little trick, derived from the formula about how to identify a leap year. It is a trick because what follows only works for time intervals and if both limits are after the year 1582, when the Gregorian Calendar was first adopted by catholics.

leap_years(march1st_year1, march1st_year2) = fake_leap_years(year2) - fake_leap_years(year1)

fake_leap_years(y) = y:4 - y:100 + y:400, where : stands for the integer division

Following up with our example, we’d have

leap_years(1800-01-10, 2000-10-01) =
= leap_years(1800-03-01, 2000-03-01) = 
= fake_leap_years(2000) - fake_leap_years(1800) = 
= (500 - 20 + 5) - (450 - 18 + 4)  =
= 485 - 436 =
= 49

If Monday = 1, Tuesday = 2, …, Saturday = 6, Sunday = 0; and today is 2013-04-27 which is a Saturday (T = 6) and we want to know which day of the week (W) was on 2011-08-30; then here is how we can proceed.

  1. 2013-04-27 >>> 2013-08-30
    1. 2013-04-27 >>> 2013-04-01
      1. A: offset of Apr 27, 2013 from Apr 1, 2013
        = 27 % 7 = 6, because 27 = 3 * 7 + 6
      2. B: day of week of Apr 1, 2013
        = T – A
        = 6 – 6 = 0 = Sunday
        (always use a minus here because the 1st is always before any other day)
    2. 2013-04-01 >>> 2013-08-01
      1. C: # days in between Apr 1, 2013 and Aug 1, 2013
        = # days of Apr + # days of May + # days of Jun + # days of Jul
        = 30 + 31 + 30 + 31 = 122
      2. D: offset of Aug 1, 2013 from Apr 1, 2013
        = C % 7 = 122 % 7 = 3, because 122 = 17 * 7 + 3
      3. E: day of week of Aug 1, 2013
        = B + D
        = 0 + 3 = 3 = Wednesday
        (use a minus or a plus depending on wether the month to get to is before or after)
    3. 2013-08-01 >>> 2013-08-30
      1. F: offset of Aug 30, 2013 from Aug 1, 2013
        = 30 % 7 = 2, because 30 = 4 * 7 + 2
      2. G: day of week of Aug 30, 2013
        = E + F
        = 3 + 2 = 5 = Friday
        (always use a plus here because the 1st is always before any other day)
  2. 2011-08-30 >>> 2013-08-30
    1. H: # leap years in between Aug 30, 2011 and Aug 30, 2013
      => leap_years(2011-08-30, 2013-08-30)
      = leap_years(2011-08-30, 2011-03-01) + leap_years(2011-03-01, 2013-03-01) + leap_years(2013-03-01, 2013-08-30)
      = -0 + fake_leap_years(2013) – fake_leap_years(2011) + 0
      = (503 – 20 + 5) – (502 – 20 + 5)
      = 1
    2. J: # years in between Aug 30, 2011 and Aug 30, 2013
      = 2013 – 2011 = 2
    3. K: offset of Aug 30, 2013 from Aug 30, 2011
      = H + J
      = 1 + 2 = 3
    4. W: day of week of Aug 30, 2011
      => day of week of Aug 30, 2013 = day of week of Aug 30, 2011 + offset of Aug 30, 2013 from Aug 30, 2011
      => G = W + K
      => W = G – K = 5 – 3 = 2 = Tuesday