Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion __main__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import doomsday.date as date
import doomsday.algorithm as algorithm


def main() -> None:
print("Hello world")
date_string: str = input("Date (format YYYY-MM-DD): ")

if (not date.is_valid_date(date_string)):
return

weekday_at_given_date: str = algorithm.get_weekday_for_date(date_string)
print(f"The date {date_string} is a {weekday_at_given_date}.")


main()
65 changes: 64 additions & 1 deletion doomsday/algorithm.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,65 @@
import math
import doomsday.date as date

CENTURY_OFFSETS = (2, 0, 5, 3)
DAY_NAMES = (
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
)


def get_weekday_for_date(date: str) -> str:
return "Sunday"
"""Returns the name of the day at a given date."""
date_split: list[str] = date.split("-")
year: int = int(date_split[0])
month: int = int(date_split[1])
day: int = int(date_split[2])

year_anchor_day: int = get_anchor_day_for_year(year)
month_anchor: int = get_anchor_for_month(month, year)

# Calculate the number of days between the month's anchor day and
# the wanted day.
day_offset: int = abs(day - month_anchor)
# Since the month's anchor day and the year's anchor day are the same
# we can add the year's anchor day index to get the final day index.
final_day_index = (year_anchor_day + day_offset) % 7

return DAY_NAMES[final_day_index]


def get_anchor_day_for_year(year: int) -> int:
"""Returns the anchor day of a given year starting at 0 for Sunday"""

# Apply the Odd + 11 algorithm
year_digits: int = year % 100
if (year_digits % 2 == 1):
year_digits += 11

year_digits = year_digits // 2
if (year_digits % 2 == 1):
year_digits += 11

# Calculate the century offset for the current year
multiple_of_7: int = math.ceil(year_digits / 7) * 7
century_index: int = (year // 100) % 4
century_offset = CENTURY_OFFSETS[century_index]

return multiple_of_7 - year_digits + century_offset


def get_anchor_for_month(month: int, year: int) -> int:
"""Returns the anchor day of a given month given the year"""

month_anchors = (
11 if date.is_leap_year(year) else 10,
22 if date.is_leap_year(year) else 21,
0, 4, 9, 6, 11, 8, 7, 10, 7, 12
)

return month_anchors[month - 1]
64 changes: 64 additions & 0 deletions doomsday/date.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,66 @@
DAYS_PER_MONTH = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)


def is_valid_date(date: str) -> bool:
"""
Returns whether a date is properly formatted to the YYYY-MM-DD format and
can be ran through the doomsday algorithm.
"""

split_date: list[str] = date.split("-")

# Check for correct date format
if (len(split_date) != 3 or "" in split_date):
print("Date must be formatted as YYYY-MM-DD")
return False

year: int = int(split_date[0])
month: int = int(split_date[1])
day: int = int(split_date[2])

if (not date_exists(year, month, day)):
return False

# Years below 1583 are not supported.
if (year < 1583):
print("For simplicity, years before 1583 are not supported.")
return False

return True


def date_exists(year: int, month: int, day: int) -> bool:
"""
Checks whether the given date can be ran into the doomsday algorithm.
Invalid dates are dates before 1583-01-01.
"""

# Check for valid month
if (month < 1 or month > 12):
print("Invalid month")
return False

# Check for valid day, accounting for leap years.
if (day < 1 or day > get_days_per_month(month, year)):
print("Invalid day number for the given date.")
return False

return True


def get_days_per_month(month: int, year: int) -> int:
"""Returns the number of day of the given month,
accounting for leap years"""

if (month == 2 and is_leap_year(year)):
return DAYS_PER_MONTH[month - 1] + 1
else:
return DAYS_PER_MONTH[month - 1]


def is_leap_year(year: int) -> bool:
"""Check if a year is a leap year.
Leap years are every year that are multiples of 4 but not multiples of 100
or years that are multiple of 400."""

return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0