diff --git a/__main__.py b/__main__.py index 53c2c43..7b5d703 100644 --- a/__main__.py +++ b/__main__.py @@ -1,5 +1,15 @@ +from doomsday.date import * +from doomsday.algorithm import * + + def main() -> None: - print("Hello world") + + while True: + date = input("Veuillez saisir une date au format YYYY-MM-DD: ") + + if is_valid_date(date): + print(f"Le {date} est un {get_weekday_for_date(date)}.") + break main() diff --git a/doomsday/algorithm.py b/doomsday/algorithm.py index cf81d60..6a2d29e 100644 --- a/doomsday/algorithm.py +++ b/doomsday/algorithm.py @@ -1,2 +1,39 @@ +from doomsday.algorithm_helper_function import * +from doomsday.does_date_exist import split_date, is_leap_year + + +MONTHS_ANCHOR: list[int] = [10, 21, 0, 4, 9, 6, 11, 8, 5, 10, 7, 12] +WEEKDAYS: list[str] = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", + "Friday", "Saturday"] + + def get_weekday_for_date(date: str) -> str: - return "Sunday" + """Return the weekday from a date + + date has to be of valid format and exist""" + + splitted_date: list[int] = split_date(date) + + year: int = splitted_date[0] + month: int = splitted_date[1] + day: int = splitted_date[2] + + # Finds the doomsday of a given year + doomsday: int = get_year_doomsday(year) + + # Retreive the anchor day of a given month + anchor_day: int = MONTHS_ANCHOR[month - 1] + + # During a leap year the anchor for January and February changes + # January: 10 -> 11 February: 21 -> 22 + if is_leap_year(year) and (month == 1 or month == 2): + anchor_day += 1 + + # Get the difference between the anchor day and the choosen day + diff_with_anchor: int = day - anchor_day + + # Adds the doomsday to our diff as an offset and apply mod 7 + # to get our final answer + weekday: int = (diff_with_anchor + doomsday) % 7 + + return WEEKDAYS[weekday] diff --git a/doomsday/algorithm_helper_function.py b/doomsday/algorithm_helper_function.py new file mode 100644 index 0000000..abbc0e0 --- /dev/null +++ b/doomsday/algorithm_helper_function.py @@ -0,0 +1,40 @@ +def get_century_doomsday_offset(year: int) -> int: + """Returns the doomsday offset for a given century""" + + # List of the offsets and exemples + # 2000 2 + # 1700/2100 0 + # 1800/2200 5 + # 1900/2300 3 + CENTURY_OFFSETS: list[int] = [2, 0, 5, 3] + + century: int = year // 100 + + return CENTURY_OFFSETS[century % 4] + + +def get_year_doomsday(year: int) -> int: + """Return the doomsday for a given year""" + + century_offset: int = get_century_doomsday_offset(year) + + # The algorithm to find the doomsday for a year: + # 1: Get the last two digits of the year + last_2_digit: int = year % 100 + + # 2: If the number is odd add 11 + if last_2_digit % 2 == 1: + last_2_digit += 11 + + # 3: Divide the number by 2 + last_2_digit = last_2_digit // 2 + + # 4: If the number is odd add 11 + if last_2_digit % 2 == 1: + last_2_digit += 11 + + # 5: Find the difference with the next multiple of 7 + diff_with_next_multiple_of_seven: int = (7 - (last_2_digit % 7)) % 7 + + # 6: Add the century offset and apply mod 7, you have the doomsday + return (diff_with_next_multiple_of_seven + century_offset) % 7 diff --git a/doomsday/date.py b/doomsday/date.py index 0f8e737..72316af 100644 --- a/doomsday/date.py +++ b/doomsday/date.py @@ -1,2 +1,27 @@ +import re +from doomsday.does_date_exist import * + + def is_valid_date(date: str) -> bool: + """Verify if the date parameter is valid + + First the function verify the format, + then it verify if the date exist + """ + + if not is_format_valid(date): + print("Le format de la date n'est pas YYYY-MM-DD!") + return False + + if not does_date_exist(date): + print("La date n'existe pas!") + return False + return True + + +def is_format_valid(date: str) -> bool: + """Verify if date follows the format YYYY-MM-DD""" + format_match = re.fullmatch(r"\d{4}[-]\d{1,2}[-]\d{1,2}", date) + + return format_match is not None diff --git a/doomsday/does_date_exist.py b/doomsday/does_date_exist.py new file mode 100644 index 0000000..8727013 --- /dev/null +++ b/doomsday/does_date_exist.py @@ -0,0 +1,58 @@ +LAST_DAY_OF_MONTH: list[int] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + + +def does_date_exist(date: str) -> bool: + """Verify if the date exist + + date has to be of valid format (YYYY-MM-DD)""" + + splitted_date: list[int] = split_date(date) + + year: int = splitted_date[0] + month: int = splitted_date[1] + day: int = splitted_date[2] + + # We support only dates starting from 1583 + if year < 1583: + print("L'année saisit n'est pas supportée.") + print("Veuillez saisir au date à partir de 1583-01-01") + return False + + if not (1 <= month <= 12): + print("Le mois saisit n'est pas valide.") + return False + + if day <= 0: + print("Le jour saisit n'est pas valide.") + return False + + if day > LAST_DAY_OF_MONTH[month-1]: + # If we are in february during a leap year we have 29 days + if month == 2 and is_leap_year(year) and day <= 29: + return True + + print("Le jour saisit n'est pas valide.") + return False + + return True + + +def is_leap_year(year: int) -> bool: + """Returns true if year is bisextile""" + divisable_by_4 = year % 4 == 0 + divisable_by_100 = year % 100 == 0 + divisable_by_400 = year % 400 == 0 + return divisable_by_4 and (not divisable_by_100 or divisable_by_400) + + +def split_date(date: str) -> list[int]: + """Splits a date into a list of ints + + date has to be of valid format (YYYY-MM-DD)""" + # Split the date in three parts using the '-' character + splitted_date_str: list[str] = date.split("-") + + # Convert the splitted date to ints + splitted_date_int: list[int] = list(map(int, splitted_date_str)) + + return splitted_date_int