Last active
September 15, 2017 11:47
-
-
Save BioGeek/4984858 to your computer and use it in GitHub Desktop.
Algorithm that returns the day of the week for a given date. Works for the Gregorian calendar which began on September 14, 1752 up till December 31, 2299.Based on the description found at http://www.kungfoomanchu.com/z-creations/mentalcalculationofdate.pdf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import datetime | |
import calendar | |
months = [6, 2, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4] | |
weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", | |
"Thursday", "Friday", "Saturday", "Sunday"] | |
centuries = {17: 5, | |
18: 3, | |
19: 1, | |
20: 0, | |
21: -2, | |
22: -4} | |
def day_of_week(day, month, year): | |
"""This algorithm is only valid for the Gregorian calendar, | |
which began on September 14, 1752, and up till December 31, 2299""" | |
if year <= 1752 and month <= 9 and day < 14: | |
raise RuntimeError("This algorithm is only valid for the Gregorian " + \ | |
"calendar which began on September 14, 1752.") | |
if year >= 2300: | |
raise RuntimeError("This algorithm is only valid for the Gregorian " + \ | |
"calendar up till December 31, 2299.") | |
# Take multiples of 28 from the the last 2 digits of the year | |
y = divmod(year, 100)[1] % 28 | |
# Add a quarter of the nearest multiple of 4 below the number, | |
y += divmod(y, 4)[0] | |
# Take away 7 or multiples of 7. This leaves us the year code | |
y = y % 7 | |
# The code for the month from the table above | |
m = months[month - 1] | |
# If it is a leap year AND the month is January or February, subtract 1 | |
if is_leap_year(year) and month in [1,2]: | |
m -= 1 | |
# Take away 7 or multiples of 7 from the day | |
d = day % 7 | |
# Add the codes for the year, the month and the day | |
result = y + m + d | |
# Add 1 if the date is in the 1900s | |
result += centuries[divmod(year, 100)[0]] | |
# Take away 7 or multiples of 7 | |
result = result % 7 | |
# The final number indicates day of the week | |
return weekdays[result] | |
def is_leap_year(year): | |
# Leap years are the years evenly divisible by 4 | |
# unless it ends in 00 and is a multiple of 400 | |
if not year % 400: | |
return True | |
elif not year % 100: | |
return False | |
elif not year % 4: | |
return True | |
return False | |
# alternate solution using builtin functions | |
def day_of_week_builtin(day, month, year): | |
""" See http://stackoverflow.com/a/14941764/50065 by mgilson""" | |
c = calendar.weekday(year, month, day) | |
return calendar.day_name[c] | |
def date_generator(day, month, year): | |
"""Convenience function that yields (day, month, year) tuples""" | |
d = datetime.date(year, month, day) | |
while True: | |
d += datetime.timedelta(days=1) | |
yield d.day, d.month, d.year | |
if __name__ == '__main__': | |
# checking all days from the beginning of the Gregorian | |
# calendar till 2300 | |
d = dategenerator(14, 9, 1752) | |
for day, month, year in d: | |
if year == 2300: | |
break | |
assert day_of_week(day, month, year) == day_of_week_builtin(day, month, year) | |
# checking my date of birth | |
print day_of_week(19, 5, 1978) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment