diff --git a/__main__.py b/__main__.py index 53c2c43..61b23d8 100644 --- a/__main__.py +++ b/__main__.py @@ -1,5 +1,18 @@ +import doomsday.date as date +import doomsday.algorithm as algorithm + + def main() -> None: - print("Hello world") + """main function ask a date, if the date is incorrect ask again, + when the date is correct, + main function ask for the day of the date and return it to the user""" + + while True: + input_date: str = input( + "Please enter a date if you want to know it day on week") + if (date.is_valid_date(input_date)): + break + print(input_date + " is a " + algorithm.get_weekday_for_date(input_date)) main() diff --git a/doomsday/algorithm.py b/doomsday/algorithm.py index cf81d60..f5d3bc0 100644 --- a/doomsday/algorithm.py +++ b/doomsday/algorithm.py @@ -1,2 +1,96 @@ +from doomsday.date import is_leap, Date_part + +WEEKDAY = ("Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday") + +CHANGE_FOR_CENTURY = (2, 0, 5, 3) +ANCHOR_DAY_IN_MONTH = (10, 21, 0, 4, 9, 6, 11, 8, 5, 10, 7, 12) + +WEEK_SIZE = 7 +CENTURY = 100 + +"""YEAR = 0 +MONTH = 1 +DAY = 2""" + + def get_weekday_for_date(date: str) -> str: - return "Sunday" + """get_weekday_for_date need the anchor date + and then find the day of the date""" + + # We split again the date + list_date: list[str] = date.split('-') + + # We find the anchor day + anchor_day_week: int = get_anchor_day(int(list_date[Date_part.YEAR.value])) + + # With the month we find the anchor day in the month + anchor_day_in_date_month = find_anchor_day_in_date_month( + int(list_date[Date_part.MONTH.value]), + int(list_date[Date_part.YEAR.value])) + + # We return the str day in week + return get_day_in_week(anchor_day_week, + anchor_day_in_date_month, list_date) + + +def get_anchor_day(year: int) -> int: + """get_anchor_day find the anchor day with different operation""" + + # We keep only the two last number of the year + number_for_day: int = year % CENTURY + + # We transform this number + number_for_day = add_eleven_if_is_odd(number_for_day) + number_for_day //= 2 + number_for_day = add_eleven_if_is_odd(number_for_day) + number_for_day = get_difference_near_multiple_of_seven(number_for_day) + number_for_day = add_century_step( + number_for_day, year // CENTURY * CENTURY) % WEEK_SIZE + + # We find the day in a tab + return number_for_day + + +def add_eleven_if_is_odd(number: int) -> int: + """We add eleven if number is odd""" + return number if number % 2 == 0 else number + 11 + + +def get_difference_near_multiple_of_seven(number: int) -> int: + """We take difference between number and multiple of seven above""" + return get_multiple_of_seven_above(number)-number + + +def get_multiple_of_seven_above(number: int) -> int: + """We find the multiple of seven above the number""" + number = number // WEEK_SIZE * WEEK_SIZE + if number % WEEK_SIZE != 0: + number += WEEK_SIZE + return number + + +def add_century_step(number: int, century: int) -> int: + """we add a number different with the century""" + return number + CHANGE_FOR_CENTURY[(century // CENTURY) % + len(CHANGE_FOR_CENTURY)] + + +def find_anchor_day_in_date_month(month: int, year: int) -> int: + """anchor day in month who is in a tab""" + if (month < 3 and is_leap(year)): + return ANCHOR_DAY_IN_MONTH[month-1] + 1 + return ANCHOR_DAY_IN_MONTH[month-1] + + +def get_difference_in_day(current_day: int, anchor_day: int) -> int: + """get difference between anchor day and current day""" + return current_day - anchor_day + + +def get_day_in_week(anchor_day: int, anchor_day_in_date_month: int, + list_date: list[str]) -> str: + """get_day_in_week translate anchor to day we search with distance""" + distance_in_day = get_difference_in_day( + int(list_date[Date_part.DAY.value]), anchor_day_in_date_month) + return WEEKDAY[(anchor_day+distance_in_day) % WEEK_SIZE] diff --git a/doomsday/date.py b/doomsday/date.py index 0f8e737..10bfa8c 100644 --- a/doomsday/date.py +++ b/doomsday/date.py @@ -1,2 +1,114 @@ +from enum import Enum, unique, auto + + +@unique +class Error(Enum): + WRONG_FORMAT = auto() + NOT_NUMBER = auto() + DATE_NOT_EXIST = auto() + YEAR_BEFORE_1583 = auto() + + +@unique +class Last_day(Enum): + SPECIAL_MONTH = 28 + SPECIAL_MONTH_LEAP = 29 + MAX_MONTH = 31 + MIN_MONTH = 30 + + +@unique +class Date_part(Enum): + YEAR = 0 + MONTH = 1 + DAY = 2 + + def is_valid_date(date: str) -> bool: + """is_valid_date test conditions for have a correct date """ + + date_list: list[str] = date.split('-') + # Verify the format of the date 'YYYY-MM-DD' + if (len(date_list) != 3): + return show_error(Error.WRONG_FORMAT, date) + + # Verify that each part of the date is int + date_list_int: list[int] = [] + for element in date_list: + if (not element.isnumeric()): + return show_error( + Error.NOT_NUMBER, date_list[0]) + date_list_int.append(int(element)) + + # Verify that the date is possible Month(1-12) Day(1-lastday) + if (date_list_int[Date_part.MONTH.value] < 1 or + date_list_int[Date_part.MONTH.value] > 12): + return show_error( + Error.DATE_NOT_EXIST, date_list[Date_part.MONTH.value]) + + if (date_list_int[Date_part.DAY.value] < 1 or + date_list_int[Date_part.DAY.value] > + find_month_last_day(date_list_int[Date_part.MONTH.value], + date_list_int[Date_part.YEAR.value])): + return show_error( + Error.DATE_NOT_EXIST, date_list[Date_part.DAY.value]) + + # Verify that the year of the date is after 1583 + if (date_list_int[Date_part.YEAR.value] < 1583): + return show_error( + Error.YEAR_BEFORE_1583, date_list[Date_part.YEAR.value]) + return True + + +def show_error(error_code: Error, error_text: str) -> bool: + """create an error message with a code and a text""" + + if (error_code == Error.WRONG_FORMAT): + print('Erreur, ' + + error_text + + ' n est pas un bon format pour une date.' + + ' Le format demandé est YYYY-MM-DD.') + + if (error_code == Error.NOT_NUMBER): + print('Erreur, ' + + error_text + + ' n est pas un nombre.') + + if (error_code == Error.DATE_NOT_EXIST): + print('Erreur, ' + + error_text + + ' le mois ou le jour sont incorrects.') + + if (error_code == Error.YEAR_BEFORE_1583): + print('Erreur, ' + + error_text + + ' l année ne doit pas etre en dessous de 1583.') + + return False + + +def find_month_last_day(month_number: int, year: int) -> int: + """month_last_day take number of the month + and return the last day of month""" + + # Case 31 + if ((month_number % 2 == 1 and month_number <= 7) or + (month_number % 2 == 0 and month_number >= 8)): + return Last_day.MAX_MONTH.value + + # Case 28-29 + if (month_number == 2): + if (is_leap(year)): + return Last_day.SPECIAL_MONTH_LEAP.value + return Last_day.SPECIAL_MONTH.value + + # Case 30 + return Last_day.MIN_MONTH.value + + +def is_leap(year: int) -> bool: + """return if the year is leap""" + if year % 4 == 0 and ((not year % 100 == 0) or year % 400 == 0): + return True + return False