$ cat what-day-was-may-28-2011.md

What day of the week was May 28, 2011?

Lionel Messi celebrates his goal in the 2011 Champions League final at Wembley as a Manchester United player looks away

on May 28, 2011, barcelona beat manchester united 3–1 at wembley stadium to win the champions league final — a match that made playing against lionel messi look almost unfair.

but i have a different question about that night: what day of the week was May 28, 2011?

was it monday? friday? my first guess was sunday.

by the end of this article, you’ll be able to answer that question — not just for this match, but for almost any date — using a few simple techniques.

how calendar works ?

the most famous calendar people are using for their birthdays is the international standard solar dating system called the gregorian calendar. it was created by Pope Gregory XIII to replace the older Julian calendar established by Julius Caesar in 46 BCE.

a normal year has 365 days. divide that by 7 and you get 52 weeks with one day left. a leap year has 366 days, so there are two days left. these small leftovers are why dates move around the week.

and since february likes to be different, it gets the extra day in a leap year. a year divisible by 4 is usually a leap year, but if it is divisible by 100 it must also be divisible by 400. so 1900 was not a leap year, but 2000 was. keep this rule somewhere in your head. we will need it.

leap-years.md

the divisible-by-400 part looks arbitrary. it is not. it is the size of a mistake, being paid back.

Julius Caesar’s calendar assumed the year is exactly 365.25 days. it is closer to 365.2422. the calendar runs long by about 11 minutes a year, which nobody notices, and by a full day every 128 years, which eventually somebody does.

by the 1500s the drift had reached ten days. easter was sliding toward summer, which is a serious problem if scheduling easter is your job.

so the repair came in two halves. first, delete the ten days that were already lost: in october 1582, the 4th was followed directly by the 15th. second, stop it happening again by skipping three leap days every four centuries — which is all the divisible-by-100-unless-400 rule is doing. that leaves 97 leap days per 400 years instead of 100, an average year of 365.2425 days, and an error of about one day every three thousand years. somebody else’s problem.

here is the part that matters for this article. when those ten days were deleted, nobody touched the week. october 4, 1582 was a thursday. october 15, 1582 was a friday. the date jumped ten days and the weekday moved one.

that chain has never been broken, anywhere, since. it is the only reason a rule invented in 1973 can reach back and tell you about 1582 at all.

what does this have to do with messi ?

nothing yet. but think about this. if a date is a monday, what day is it 7 days later ? still monday. 14 days later ? monday again.

so we do not need to count every single day. we can throw away complete weeks and only count what is left. 19 days ahead is the same weekday shift as 5 days ahead, because the other 14 days are two complete weeks.

how you can do it

the mathematician John Conway came up with a method called the doomsday rule. dramatic name for something you can use to find out what day your birthday was.

the idea is that some easy dates always share the same weekday within a year. that shared weekday is the year’s doomsday. it can be monday in one year and a different day in another.

start with these: april 4, june 6, august 8, october 10 and december 12. nice and easy. 4/4, 6/6, 8/8, 10/10 and 12/12.

for the other months, remember may 9 and september 5, then july 11 and november 7. the numbers swap places.

march 14 is another one. for february use its last day, either the 28th or 29th. january is the one to watch: january 3 in a normal year, january 4 in a leap year. you can check the full list here.

why do these dates stick together ? count from april 4 to june 6. it is 63 days. exactly 9 weeks. the same idea connects the other dates.

anchors.md

counting from one anchor to the next shows you two of them agree. there is a way to see all twelve at once.

number the days of a normal year — january 1 is day 1, december 31 is day 365 — and write down where each anchor lands.

january 3      day 3      3 =  0 weeks + 3
february 28    day 59    59 =  8 weeks + 3
march 14       day 73    73 = 10 weeks + 3
april 4        day 94    94 = 13 weeks + 3
may 9          day 129  129 = 18 weeks + 3
june 6         day 157  157 = 22 weeks + 3
july 11        day 192  192 = 27 weeks + 3
august 8       day 220  220 = 31 weeks + 3
september 5    day 248  248 = 35 weeks + 3
october 10     day 283  283 = 40 weeks + 3
november 7     day 311  311 = 44 weeks + 3
december 12    day 346  346 = 49 weeks + 3

every single one is a whole number of weeks plus 3. that is the entire trick, sitting in one column.

so the anchors are not twelve facts to memorise. they are twelve names for the same weekday, and the dates were reverse-engineered to make that true — Conway went looking for the most forgettable dates in each month that would land on it.

in a leap year everything from february 29 onward slides one day later, so that column reads 4 instead of 3, and january’s anchor moves from the 3rd to the 4th to keep step. that is the only reason january and february carry their own rule. every month after them is already downstream of the inserted day and never notices it.

and since you have to hold the odd months somewhere, use Conway’s line for them: i work from 9 to 5 at the 7-11. may 9, september 5, july 11, november 7. march 14 is pi day. february is best thought of as march 0 — the day before march 1, which is its last day no matter what the year is doing.

okay, but how do we find the year’s doomsday ?

first give the weekdays numbers: sunday = 0, monday = 1, tuesday = 2, wednesday = 3, thursday = 4, friday = 5 and saturday = 6.

we also need a starting number for the century. for 1800–1899 use 5, for 1900–1999 use 3, for 2000–2099 use 2, and for 2100–2199 use 0. these century anchors repeat every 400 years, so 1600–1699 uses the same number as 2000–2099.

centuries.md

those four numbers can be derived rather than memorised, and the derivation explains why the cycle is 400 years long instead of 100.

a century holding 24 leap days is 36524 days. divide by 7 and you get 5217 weeks with 5 days left over, so an ordinary century drags the anchor 5 weekdays forward. the one step that crosses a 400-year leap year — the 1900s into the 2000s, which contains february 29, 2000 — is a day longer and drags 6.

five, five, five, six. that is 21 days. exactly three weeks, so the anchor lands back where it started and the four numbers begin again.

underneath all of it is a single number. 400 gregorian years are 146097 days, and 146097 is 20871 sevens with nothing left over. four centuries is a whole number of weeks — which means the calendar does not merely repeat its dates every 400 years, it repeats its weekdays too. 2026 and 1626 have identical calendars. so will 2426.

that exactness has a famous side effect. one full cycle contains 4800 months, so 4800 dates that are the 13th, and if you count which weekday they fall on they refuse to come out even:

friday      688
sunday      687
wednesday   687
monday      685
tuesday     685
thursday    684
saturday    684

the 13th lands on a friday more often than on any other day of the week. the calendar is very slightly superstitious.

here is a simple way to do the calculation. take the last two digits of the year, add the number of complete groups of 4 in those digits, then add the century’s starting number. divide the total by 7 and keep only the remainder.

let’s use 2011. the last two digits are 11. there are 2 complete groups of 4 in 11. our century starts with 2.

11 + 2 + 2 = 15.

remove 14, which is two complete weeks, and we have 1 left. 1 means monday. so the doomsday for 2011 is monday.

if you are wondering where the calculation comes from, each ordinary year shifts the doomsday by one weekday, and each leap year adds another shift. the groups of 4 count those extra shifts within the century. the century anchor handles where we started.

year-formula.md

the version above is the one that explains itself. Conway’s own version was built for speed, because he genuinely raced it — he kept a program on his computer that quizzed him with a random date every time he logged in, and got his average answer down to a couple of seconds.

that version is called odd+11, and it trades counting groups of four for halving, which your head does faster.

take the last two digits. if the number is odd, add 11. halve it. if what you get is odd, add 11 again. drop sevens from what is left, subtract that from 7, and add the century anchor.

2011: 11 is odd, so 22. half of 22 is 11. odd again, so 22. dropping sevens from 22 leaves 1. seven minus one is 6. plus the century’s 2 is 8, drop a seven, 1. monday.

2024: 24 is even, leave it. half is 12, still even, leave it. dropping sevens from 12 leaves 5. seven minus five is 2. plus 2 is 4. thursday.

both land where the article’s method lands, as they have to. it is not fewer steps — it is easier steps. halving 98 is something you do without thinking. working out how many fours fit inside 87 is not.

and one practical note, since this is the only part of the whole method that changes during a year: you do it once and then you are done until january. 2026 is a saturday. after that: 2027 sunday, 2028 tuesday — leap years jump two — 2029 wednesday, 2030 thursday. learn the current one and every date anyone asks you about for the next twelve months is a short walk from the nearest anchor.

back to wembley

we now know that may 9, 2011 was a monday. may 16 was also monday. may 23 was also monday.

five more days gets us to may 28. tuesday, wednesday, thursday, friday, saturday.

so the answer is saturday. barcelona won the final on a saturday. my sunday guess was not even close enough to get a point.

one more before you go

what about february 29, 2024 ? take 24, add 6, then add 2 for the century. that gives us 32. remove 28 and we have 4, which is thursday.

2024 is a leap year, so february 29 is itself a doomsday. the answer is thursday. no extra counting needed.

one small detail: this method uses the gregorian calendar. for old historical dates, check which calendar the country was using at the time before applying it.

let the computer do it: monday first in C#

now suppose you want to put this in a program. another approach is to count the days from january 1, year 1, which is a monday when we extend the gregorian calendar backward. this is a calendar convention, not a claim that people used this calendar back then.

give that starting date the number 0. count the complete years before our date, add their leap days, then add the days already passed in the current year. divide by 7 and keep the remainder. monday is 0, tuesday is 1, and so on until sunday is 6.

so here monday is the first day in our numbering. in the doomsday explanation above, sunday was 0. the weekday stays the same; only its number changes.

the before array below stores the days before each month in a normal year. january has 0 days before it, february has 31, march has 59. if the date is after february in a leap year, we add one more.

using System;

public class WeekdayExample
{
    // Monday = 0 through Sunday = 6.
    public static int MondayFirst(int year, int month, int day)
    {
        // Validate the date before using it as an array index.
        DateTime date = new DateTime(year, month, day);
        int[] before = {0,31,59,90,120,151,181,212,243,273,304,334};
        int previous = date.Year - 1;
        int days = 365 * previous + previous / 4
                 - previous / 100 + previous / 400;
        days += before[month - 1] + day - 1;
        if (month > 2 && DateTime.IsLeapYear(year)) days++;
        return days % 7;
    }

    public static void Main()
    {
        string[] names = {"monday","tuesday","wednesday","thursday",
                          "friday","saturday","sunday"};
        Console.WriteLine(names[MondayFirst(2011, 5, 28)]); // saturday
        Console.WriteLine(names[MondayFirst(2024, 2, 29)]); // thursday
    }
}

for may 28, 2011, this counts 734284 days after our starting monday. the remainder after dividing by 7 is 5. with monday at 0, 5 means saturday. same answer, different route.

if you only need the answer in a real C# application, DateTime.DayOfWeek already does the work. convert its sunday-first numbering with ((int)date.DayOfWeek + 6) % 7. writing the calculation ourselves here helps us see why it works.

both methods in C

let’s put them next to each other. monday_first counts elapsed days. doomsday follows the anchors we used in our heads. both functions below return monday = 0 through sunday = 6, so we can compare their answers directly.

the century formula gives the same anchors listed earlier. the double remainder in doomsday handles dates before a month’s anchor, because a negative value % 7 can stay negative in C. the final + 6 changes sunday-first numbering into monday-first numbering.

modulo.md

% in C is not the modulo of mathematics. it is whatever is left over after a division that truncates toward zero, and once a negative number shows up those two stop agreeing.

the remainder is not really choosing anything. the language has to keep this true:

(a / b) * b + (a % b) == a

-11 / 7 truncates to -1. so -11 % 7 is forced to be -4, because -1 * 7 + -4 is the only way back to -11. the division made the decision and the remainder went along with it.

it was not even guaranteed for a long time. C89 left the sign implementation-defined whenever an operand was negative, so identical code could hand you -4 on one compiler and 3 on another. C99 finally pinned it to truncation.

Python went the other way on purpose. its division floors toward negative infinity, so -11 // 7 is -2 and -11 % 7 is 3 — already the answer you wanted, no guard needed.

which sounds like Python simply wins, until you look at what each language does when the bug slips through. in C, an index of -4 reads memory that was never yours: a crash, or garbage, but something is visibly wrong. in Python, names[-4] is perfectly legal — it counts backwards from the end of the list and hands you a real weekday, spelled correctly, printed confidently, and wrong.

the crash is the kinder failure.

these C functions assume a valid date with a year from 1 to 9999. validate user input before calling them. the C# example uses DateTime to reject invalid dates.

#include <stdio.h>

int leap(int y) {
    return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
}

/* Both methods expect a valid Gregorian date, years 1..9999.
   Both return Monday = 0 through Sunday = 6. */
int monday_first(int y, int m, int d) {
    const int before[] = {0,31,59,90,120,151,181,212,243,273,304,334};
    int previous = y - 1;
    int days = 365 * previous + previous / 4
             - previous / 100 + previous / 400;
    days += before[m - 1] + d - 1;
    if (m > 2) days += leap(y);
    return days % 7;
}

int doomsday(int y, int m, int d) {
    int dates[] = {3,28,14,4,9,6,11,8,5,10,7,12};
    int century = y / 100;
    int yy = y % 100;
    int anchor = (5 * (century % 4) + 2) % 7;
    int doom = (anchor + yy + yy / 4) % 7;
    if (leap(y)) { dates[0] = 4; dates[1] = 29; }
    int sunday_first = ((doom + d - dates[m - 1]) % 7 + 7) % 7;
    return (sunday_first + 6) % 7;
}

int main(void) {
    const char *names[] = {"monday","tuesday","wednesday","thursday",
                           "friday","saturday","sunday"};
    const int examples[][3] = {{2011,5,28}, {2024,2,29}, {1900,3,1}};
    for (int i = 0; i < 3; i++) {
        int y = examples[i][0], m = examples[i][1], d = examples[i][2];
        printf("%04d-%02d-%02d: %s / %s\n", y, m, d,
               names[monday_first(y,m,d)], names[doomsday(y,m,d)]);
    }
    return 0;
}

the output is:

2011-05-28: saturday / saturday
2024-02-29: thursday / thursday
1900-03-01: thursday / thursday

that last example is useful because 1900 was not a leap year. february only had 28 days. it is an easy detail to miss if your leap-year check only divides by 4.

now try your own birthday. find the year’s doomsday, choose a nearby date from the list, then move forward or backward. when you are done, check your answer in the terminal at the end of this post. it shows every step, so you can see exactly where you went wrong.

it will probably feel slow the first few times. that is fine. once the dates become familiar, most of the work is just adding small numbers and throwing away weeks.

and next time someone brings up that final, you can tell them it was a saturday. whether a manchester united fan wants to remember it is a different question.

doomsday.sh check your answer

Worked out a date in your head? Run it here and compare each step with yours.

$ doomsday 2011-05-28

century
20xx → 2
year
11 + 2 + 2 = 15 → 15 mod 7 = 1 → monday
anchor
may 9 is a doomsday → monday
distance
28 − 9 = 19 days → drop 2 weeks, 5 days left
answer
monday + 5 → saturday