I've been working on web based projects built mainly with PHP and JavaScript, where I mostly use Zend Framework and jQuery. I am interested in any webpage optimizations techniques - for a faster web! Stoimen is a DZone MVB and is not an employee of DZone and has posted 96 posts at DZone. You can read more from them at their website. View Full User Profile

Algorithm of the Week: How to Determine the Day of the Week

04.24.2012
| 32309 views |
  • submit to reddit

Do you know what day of the week was the day you were born? Monday or maybe Saturday? Well, perhaps you know that. Everybody knows the day he’s born on, but do you know what day was the 31st of January in 1883? No? Well, there must be some method to determine any day in any century.

We know that 2012 started at Sunday. After we know that, it’s easy to determine what day is the 2nd of January. It should be Monday. But things get a little more complex if we try to guess some date distant from January the 1st. Indeed 1st of Jan was on Sunday, but what day is 9th of May the same year. This is far more difficult to say. Of course we can go with a brute force approach and count from 1/Jan till 9/May, but that is quite slow and error prone.


So what would we do if we had to code a program that answers this question? The easiest way is to use a library. Almost every major library has built-in functions that can answer what day is on a given date. Such are date() in PHP or getDate() in JavaScript. But the question remains: How these library functions know the answer and how can we code such library functions if our library doesn’t support such functionality?

There must be some algorithm to help us.

Overview

Because months have different number of days, and most of them aren’t divisible by 7 without a remainder, months begin on different days of the week. Thus, if January begins on Sunday, the month of February the same year will begin on Wednesday. Of course, in common years February has 28 days, which fortunately is divisible by 7 and thus February and March both begin on the same day, which is great, but isn’t true for leap years.

What Do We Know About the Calendar

First thing to know is that each week has exactly 7 days. We also know that a common year has 365 days, while a leap year has one day more – 366. Most of the months have 30 or 31 days, but February has only 28 days in common years and 29 in leap years.

Because 365 mod 7 = 1 in a common year each year begins exactly on the next day of the preceding year. Thus if 2011 started on Saturday, 2012 starts on Sunday. And yet again, that is because 2011 is not a leap year.



What else do we know? Because a week has exactly seven days only February (with its 28 days in a common year) is divisible by 7 (28 mod 7 = 0) and has exactly four weeks in it. Thus in a common year February and March start on a same day. Unfortunately that is not true about the other months.

All these things we know about the calendar are great, so we can make some conclusions. Although eleven of the months have either 30 or 31 days they don’t start on a same day, but some of the months do appear to start on a same day just because the number of days between them is divisible by 7 without a remainder.

Let’s take a look on some examples. For instance September has 30 days, as does November, while October, which is in between them has 31 days. Thus 30+30+31 makes 91. Fortunately 91 mod 7 = 0. So for each year September and December start on the same day (as they are after February they don’t depend on leap years). The same thing occurs to April and July and the good news is that in leap years even January starts on the same day as April and July.



Now we know that there are some relations between months. Thus, if we know somehow that the 13th of April is Monday, we’ll be sure that 13th of July is also Monday. Let’s see now a summary of these observations.



We can also refer to the following diagram.



For leap years there are other corresponding months. Let’s take a look at the following image.



Another way to get the same information is the following table.



We also know that leap years happen to occur once every four years. However, if there is a common year like the year 2001, which will be the next year that is common and starts and corresponds exactly on 2001? Because of leap years we can have a year starting on one of the seven days of the week and to be either leap or common. This means just 14 combinations.

Following these observations we can refer to the following table.



You can clearly see the pattern “6 4 2 0”

Here’s the month table.



Columns 2 and 3 differs only for January and February.

Clearly the day table is as follows:


Now let’s go back to the algorithm.

Using these tables and applying a simple formula, we can calculate what day was on some given date. Here are the steps of this algorithm.

  1. Get the number for the corresponding century from the centuries table;
  2. Get the last two digits from the year;
  3. Divide the number from step 2 by 4 and get it without the remainder;
  4. Get the month number from the month table;
  5. Sum the numbers from steps 1 to 4;
  6. Divide it by 7 and take the remainder;
  7. Find the result of step 6 in the days table;


Implementation

First let’s take a look at a simple and practical example of the example above and then the code. Let’s answer the question from the first paragraph of this post.

What day was on January 31st, 1883?

  1. Take a look at the centuries table: for 1800 – 1899 this is 2.
  2. Get the last two digits from the year: 83.
  3. Divide 83 by 4 without a remainder: 83/4 = 20
  4. Get the month number from the month table: Jan = 0.
  5. Sum the numbers from steps 1 to 4: 2 + 83 + 20 + 0 = 105.
  6. Divide it by 7 and take the remainder: 105 mod 7 = 0
  7. Find the result of step 6 in the days table: Sunday = 0.


The following code in PHP implements the algorithm above.

function get_century_code($century)
{
	// XVIII
	if (1700 <= $century && $century <= 1799)
		return 4;
 
	// XIX
	if (1800 <= $century && $century <= 1899)
		return 2;
 
	// XX
	if (1900 <= $century && $century <= 1999)
		return 0;
 
	// XXI
	if (2000 <= $century && $century <= 2099)
		return 6;
 
	// XXII
	if (2100 <= $century && $century <= 2199)
		return 4;
 
	// XXIII
	if (2200 <= $century && $century <= 2299)
		return 2;
 
	// XXIV
	if (2300 <= $century && $century <= 2399)
		return 0;
 
	// XXV
	if (2400 <= $century && $century <= 2499)
		return 6;
 
	// XXVI
	if (2500 <= $century && $century <= 2599)
		return 4;
 
	// XXVII
	if (2600 <= $century && $century <= 2699)
		return 2;
}
 
/**
 * Get the day of a given date
 * 
 * @param $date
 */
function get_day_from_date($date) 
{
	$months = array(
		1 => 0,		// January
		2 => 3,		// February
		3 => 3,		// March
		4 => 6,		// April
		5 => 1,		// May
		6 => 4,		// June
		7 => 6,		// July
		8 => 2,		// August
		9 => 5,		// September
		10 => 0,	// October
		11 => 3,	// November
		12 => 5,	// December
	);
 
	$days = array(
		0 => 'Sunday',
		1 => 'Monday',
		2 => 'Tuesday',
		3 => 'Wednesday',
		4 => 'Thursday',
		5 => 'Friday',
		6 => 'Saturday',
	);
 
	// calculate the date
	$dateParts = explode('-', $date);
	$century = substr($dateParts[2], 0, 2);
	$year = substr($dateParts[2], 2);
 
	// 1. Get the number for the corresponding century from the centuries table
	$a = get_century_code($dateParts[2]);
 
	// 2. Get the last two digits from the year
	$b = $year;
 
	// 3. Divide the number from step 2 by 4 and get it without the remainder
	$c = floor($year / 4);
 
	// 4. Get the month number from the month table
	$d = $months[$dateParts[1]];
 
	// 5. Sum the numbers from steps 1 to 4
	$e = $a + $b + $c + $d;
 
	// 6. Divide it by 7 and take the remainder
	$f = $e % 7;
 
	// 7. Find the result of step 6 in the days table
	return $days[$f];
}
 
// Sunday
echo get_day_from_date('31-1-1883');


Application

This algorithm can be applied in many different cases although most of the libraries have built-in functions that can do that. The only problem besides that is that there are much more efficient algorithms that don’t need additional space (tables) of data. However this algorithm isn’t difficult to implement and it gives a good outlook of some facts in the calendar.

 

Published at DZone with permission of Stoimen Popov, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Vivek Singh replied on Wed, 2012/04/25 - 1:18am

Thanks for this post. How did you come up with tables for months and centuries ?

Der Meister replied on Wed, 2012/04/25 - 2:37am

Of course you can implement it in the most complex way possible, but the following few lines will do the same for years 1700 to at least 4000 and are much easier to understand (in Java):

	public static int getWeekday(int y, int m, int d) {
		// 1700-1-1 = 5 (Friday)
		int[] mo = new int[] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; // monthly offset
		int af = m > 2 ? 0 : 1; // after february
		// every fourth year is a leap year, unless it is divisible by 100 unless it is divisible by 400
		int w = 5 + (y-1700)*365 + (y-1700-af)/4 - (y-1700-af)/100 + (y-1600-af)/400 + mo[m-1] + (d-1);
		return w % 7;
	}

 (mo would normally be a static field)

Eryl Talbot replied on Wed, 2012/04/25 - 10:12am

thanks for the post.

 

David Ljuba replied on Mon, 2013/01/21 - 4:34am in response to: Vivek Singh

This is table-based perpetual calendar algorithm


Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.