Calculate the day of week of any date... in your head

If you want to really impress people at cocktail parties — and by impress I mean freak them out with your uncanny, weird, rainman-like borderline Asperger's-syndrome abilities — compute the day of week of any date in your head. With a little bit of memorization and some basic computational arithmetic skill, you can figure out that, for example, Jul. 4 1776 fell on a Thursday, or that your friend's birthday will fall on a Sunday three years from now.

I started working this out when my kids were little, as I was driving with them cross-country — my son kept asking me on what day of the week he was born. I knew the date of his birth, but I couldn't for the life of me remember what day it actually fell on. I remembered that it happend during the week, since I had to leave work when my wife told me she had gone into labor, but that's all I could come up with. Since I was in the car, with no (convenient) access to a calendar — and nothing particularly better to do with myself — I started trying to work it out in my head.

Although I didn't figure it out on that car trip, I worked out the basic algorithm that I'll present here, since it makes a sort of an interesting computational problem as well. Conceptually, it's a simple problem:

  1. Figure out how many days occurred between the target date and the current date.
  2. Divide this by 7 and compute the remainder R
  3. If the target date is in the past, count backwards from the current day of the week by R days; if in the future, count forwards.
Of course, this glosses over the computationally tricky problem of figuring out how many days passed between the target date and the current date. If you're going to compute this without pencil and paper, you need to simplify the problem a bit.

The first key observation is that there are 52 weeks in a year, but 365 days in a (non-leap) year. Since 52 * 7 = 364, that means that there's one "extra" day in each year. So, if today is Thursday, Mar. 15, 2012, then Mar. 15, 2013 will fall on a Friday. Mar. 15, 2014 will fall on a Saturday. Mar. 15, 2015 will fall on a Sunday, and so on. So, if you want to figure out what day a given date fell on (or falls on), first figure out what day that date will fall on this year, and then add or subtract one day for each intervening year.

Leap years make this a bit more complicated, since you have to add one day for each intervening leap year... but that's not impossible to work out in your head, either. Just add an extra day for each multiple of four that occurs between the current year and the target year. For instance, today is Monday, Jan. 9, 2012. What day did Jan. 9, 1941 fall on? Well, for starters, 2012 - 1941 = 71; you can figure this out in your head easily. How many leap years occured in the intervening 71 years? Well, 71 / 4 = 17, so there were 17 leap years in between. Now, add that 17 to 71 to get 88. Now it's a simple matter of figuring out the remainder of 88 / 7 to figure out how many days of the week to "subtract" from the current one to figure out what day Jan. 9, 1941 fell on. Dividing by 7, you get a remainder of 4 - so subtract four days from the current day of Monday to determine that Jan. 9, 1941 fell on a Thursday.

One thing to be aware of if you're computing dates way into the future or way in the past; years that are multiples of 100 are not leap years, even though they're evenly divisible by 4... unless they are divisible by 400. So you have to again subtract 1 for each multiple of 100 that occurs between the source and target year, excluding the multiples of 400: 2400, 1600, 1200, ... This won't be difficult unless you're trying to compute back to the dark ages, in which case you should probably be aware that the current leap year rules didn't go into effect until 1582. If you need to compute that far back, just get a calendar.

So, algorithmically speaking, the offset between the same day on different years is given by:

[ ( target year - source year ) + ( target year - source year ) / 4 ) ] % 7

One last adjustment: if the target or source year is a leap year — is evenly divisble by 4 — you have to add one day if the earlier date falls in January or February, and add one day if the later date falls after February. Obviously, you'll only ever have to adjust one in this case, so it's not too difficult to keep track of this. It's also easy to tell if a date falls on a leap year - if the last two digits are evenly divisible by 4, then it's a leap year. Since 76 is evenly divisible by 4, for instance, 1776 was a leap year (and so was 1876, 1976, and so will be 2076, etc.).

Now, if you want to extend that out to any arbitrary date, first figure out what day your chosen date will fall on in this year, and then perform the computation to figure out the difference. Unfortunately, figuring out what day a given date falls on in the current year is a bit harder than figuring out the difference between two dates in different years. It would be trivial if each month had exactly 30 days; then you could just compute the difference in months and multiply by 2 (since 30 % 7 = 2). The actual differences are given by the table below.

JanFebMarAprMayJunJulAugSepOctNovDec
Jan033614625035
Feb300351362402
Mar300351362402
Apr633025036146
May155203514624
Jun411530251361
Jul633052036146
Aug266315303513
Sep522641630250
Oct044163152035
Nov300426415302
Dec522641630520

Table 1: Day offsets for all months

If you can memorize that behemoth, you don't need my help computing the day that a given date falls on... is there a way to simplify this calculation?

Well, actually, it's easier to just simplify the problem. One thing that makes this computation difficult is that I'm trying to compute the number of days between today and the target date. But I don't actually need to do that - all I'm really interested in is the day that the target date falls on. If I can remember what day January 1st falls on in this year (Sunday, in 2012), then I can just figure the offset between that day and the date that I'm interested in. Now I only have to remember the very first row of table 1.

Still - that's 12 pretty randomly distributed numbers... not easy to memorize. Remember earlier when I mentioned that this would be easy if every month had 30 days? Then I could just multiply the ordinal of the target month by 2 and that would give me my offset, mod 7. Although months have varying numbers of days, it's much easier to memorize how wrong this calculation is than it is to memorize the actual differences themselves.

JanFebMarAprMayJunJulAugSepOctNovDec
01-1001123344

Table 2: differences between various months.

Now, given a date in the current year, multiply its (zero-based) month ordinal by 2 and then add the number in table 2, which is easy to memorize. This gives you the "month offset" - and since your reference date is Jan. 1, the day offset is just the day of the month. Add these two numbers, compute the remainder modulo 7, and you've got your offset to figure out what day a given date falls in in the current year. Don't forget to account for leap year if the current year is one, too.

So let's say you want to figure out what day Sep. 25, 2012 falls on, knowing that Jan. 1 2012 falls on a Sunday. 8 * 2 = 16 + 3 (from table 2) = 19 + 1 since 2012 is a leap year = 20, which leaves a remainder of 6 modulo 7. Therefore, Sep. 1 of 2012 must fall on a Saturday. The 25th is 24 days after that; 24 leaves a remainder of 3 mod 7, so Sep. 25 of 2012 must then fall on a Tuesday. Just don't forget to subtract 1 from the month and day to account for the fact that all of these computation are "zero-based". If you're a C or Java programmer, you shouldn't have any trouble keeping track of that.

In fact, it's not even necessary to keep track of what day January 1st falls on in the current year - all you need is a known reference date, and you can compute everything relative to that date, accounting for the year as shown above. I just remember that Jan. 1, 2000 was a Saturday, and I can compute everything else forward from there. Using the year 2000 as a reference year makes my year calculations easy to keep track of, since it's easy to subtract from.

So, let's put all of this together. What day will Jul. 4, 2019 fall on? M = 6, D = 3, Y = 19. The year offset is 19 + ( 19 / 4 ) = 23 % 7 = 2, which you can figure out in your head easily. Add one since the "source year" of 2000 was a leap year to get 3. The month offset is (6*2)+1=13%7=6 and the day offset is, of course, D=3. 6+3+3=12; 12%7=4, so Jul. 4 2019 is five days more than the reference date of Saturday, Jan. 1, 2000: Thursday, Jul. 4 2019.

Here it is in code:

static int adjustment[] = { 0, 1, -1, 0, 0, 1, 1, 2, 3, 3, 4, 4 };

static int day_offset( int y, int m, int d )
{
  int year_offset, month_offset, day_offset;

  // Add 1 if y > 2000 to account for the fact that 2000 was a leap year.
  year_offset = ( y - 2000 ) + ( ( y - 2000 ) / 4 ) + ( y > 2000 );
  // Account for wrongly computed leap years
  year_offset -= ( y - 2000 ) / 100;

  // Add back 1 year if the target year is a leap year but the target
  // day is after the 29th (in other words, the leap day hasn't happened
  // yet).
  if ( y % 100 )
  {
    year_offset += ( y < 2000 ) && !( y % 4 ) && ( m > 2 );
    year_offset -= ( y > 2000 ) && !( y % 4 ) && ( m < 3 );
  }

  month_offset = ( ( m - 1 ) * 2 ) + adjustment[ m - 1 ];
  day_offset = d - 1;

  return ( year_offset + month_offset + day_offset ) % 7;
}
This isn't necessarily the most efficient way to do this on a computer, but encapsulates an algorithm that, with a bit of practice, you can memorize and repeat in your head next time you need to know if Christmas will fall on a weekend this year or not.

Add a comment:

Completely off-topic or spam comments will be removed at the discretion of the moderator.

You may preserve formatting (e.g. a code sample) by indenting with four spaces preceding the formatted line(s)

Name: Name is required
Email (will not be displayed publicly):
Comment:
Comment is required
My Book

I'm the author of the book "Implementing SSL/TLS Using Cryptography and PKI". Like the title says, this is a from-the-ground-up examination of the SSL protocol that provides security, integrity and privacy to most application-level internet protocols, most notably HTTP. I include the source code to a complete working SSL implementation, including the most popular cryptographic algorithms (DES, 3DES, RC4, AES, RSA, DSA, Diffie-Hellman, HMAC, MD5, SHA-1, SHA-256, and ECC), and show how they all fit together to provide transport-layer security.

My Picture

Joshua Davies

Past Posts