diff --git a/__main__.py b/__main__.py index 53c2c43..0edf0f9 100644 --- a/__main__.py +++ b/__main__.py @@ -1,5 +1,30 @@ +from doomsday.date import is_valid_date +from doomsday.algorithm import get_weekday_for_date + + def main() -> None: - print("Hello world") + """Main function asking for a date and displaying its day""" + + date: str = ask_for_valid_date() + + display_weekday_for_date(date, get_weekday_for_date(date)) + + +def ask_for_valid_date() -> str: + """Ask for a date until a valid one is given""" + + given_date: str = "" + + while True: + given_date = input("Your date (YYYY-MM-dd):\n") + if is_valid_date(given_date): + return given_date + + +def display_weekday_for_date(date: str, day: str) -> None: + """Display a sentence saying a date and its day""" + + print(f"{date} is a {day}") main() diff --git a/doomsday/algorithm.py b/doomsday/algorithm.py index cf81d60..3c952f1 100644 --- a/doomsday/algorithm.py +++ b/doomsday/algorithm.py @@ -1,2 +1,75 @@ +from doomsday.date import is_leap_year + +DAYS = ( + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday" +) + +CENTURY_ANCHOR = ( + 3, 2, 0, 5 +) + + def get_weekday_for_date(date: str) -> str: - return "Sunday" + """Return the name of the day corresponding to the given date""" + + year, month, day = (value for value in date.split("-")) + anchor_day: int = get_anchor_day(year) + + return get_weekday_by_anchor_day( + int(year), int(month), int(day), anchor_day) + + +def get_anchor_day(year: str) -> int: + """Return the anchor day for the given year""" + + # Get the last two numbers in the year + anchor_day: int = int(year[-2:len(year)]) + century: int = int(year[0:-2]) + 1 + + if anchor_day % 2 != 0: + anchor_day += 11 + + anchor_day //= 2 + + if anchor_day % 2 != 0: + anchor_day += 11 + + # Find the lowest multiple of seven greater than the value we have, + # and keep the difference between this multiple and our value + anchor_day %= 7 + anchor_day -= 7 + anchor_day = -anchor_day + + anchor_day += CENTURY_ANCHOR[(century % 4)] + + return anchor_day % 7 + + +def get_weekday_by_anchor_day( + year: int, month: int, day: int, anchor_day: int) -> str: + """Return the weekday of the given MM-dd + with the anchor day for the year""" + + is_leap_year_value: bool = is_leap_year(year) + + doomsdays = [ + 3 if not is_leap_year_value else 4, + 28 if not is_leap_year_value else 29, + 14, 4, 9, 6, 11, 8, 5, 10, 7, 12 + ] + + doomsday: int = doomsdays[month - 1] + + if doomsday == day: + return DAYS[anchor_day] + + if day > doomsday: + return DAYS[(anchor_day + (day - doomsday)) % 7] + + return DAYS[(anchor_day - (doomsday - day)) % 7] diff --git a/doomsday/date.py b/doomsday/date.py index 0f8e737..db2a75a 100644 --- a/doomsday/date.py +++ b/doomsday/date.py @@ -1,2 +1,61 @@ +DAYS_PER_MONTH = ( + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +) + + def is_valid_date(date: str) -> bool: + """Check if a date is valid. Which means beign 3 numbers separated by "-", + and that the year, month and day given exist in the Gregorian calendar""" + + if not is_valid_format_date(date): + return False + + year, month, day = (int(value) for value in date.split("-")) + + if year < 1583: + print("Years begin from 1583") + return False + + if (1 > month) or (month > 12): + print("Month goes from 1 to 12") + return False + + days_in_this_month: int = get_days_in_month(year, month) + + if (1 > day) or (day > days_in_this_month): + print("For this month, day goes from 1 to ", days_in_this_month) + return False + return True + + +def is_valid_format_date(date: str) -> bool: + """Verify if a given date is 3 numbers separated by "-" """ + + splited_date: list[str] = date.split("-") + + if len(splited_date) != 3: + print("Date format should be YYYY-MM-dd") + return False + + for part in splited_date: + if not part.isdigit(): + print("A date must be composed with numbers") + return False + + return True + + +def get_days_in_month(year: int, month: int) -> int: + """Return the number of days in a month, depending on year""" + + if month == 2: + return 28 if not is_leap_year(year) else 29 + + return DAYS_PER_MONTH[month-1] + + +def is_leap_year(year: int) -> bool: + """Verify if a year is a leap year or not""" + + return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)