zellers congruence Algorithm

The Zeller's Congruence Algorithm is a mathematical formula designed to calculate the day of the week for any given date. Developed by Christian Zeller in 1887, this algorithm has been widely used in various calendar systems, including the Gregorian and Julian calendars, to determine the day of the week corresponding to a specific date. The algorithm is based on the observation that the day of the week progresses in a regular cycle, with a specific pattern repeating every 400 years in the Gregorian calendar. By utilizing this pattern, the Zeller's Congruence Algorithm can accurately calculate the day of the week for any date within the supported calendar systems. The algorithm works by assigning numerical values to the year, month, and day of the date being queried, and then using modular arithmetic to compute the corresponding day of the week. The procedure involves dividing the year into centuries, years within the century, and months, and then applying specific mathematical operations to these components to arrive at a final numerical value that represents the day of the week. In the Gregorian calendar, this final value ranges from 0 to 6, with each number corresponding to a specific day, from Saturday to Friday. The Zeller's Congruence Algorithm is efficient and reliable, making it a popular choice for calendar-related applications and an important tool in the field of computational mathematics.
import datetime
import argparse


def zeller(date_input: str) -> str:

    """
    Zellers Congruence Algorithm
    Find the day of the week for nearly any Gregorian or Julian calendar date

    >>> zeller('01-31-2010')
    'Your date 01-31-2010, is a Sunday!'

    Validate out of range month
    >>> zeller('13-31-2010')
    Traceback (most recent call last):
    ...
    ValueError: Month must be between 1 - 12
    >>> zeller('.2-31-2010')
    Traceback (most recent call last):
    ...
    ValueError: invalid literal for int() with base 10: '.2'

    Validate out of range date:
    >>> zeller('01-33-2010')
    Traceback (most recent call last):
        ...
    ValueError: Date must be between 1 - 31
    >>> zeller('01-.4-2010')
    Traceback (most recent call last):
        ...
    ValueError: invalid literal for int() with base 10: '.4'

    Validate second separator:
    >>> zeller('01-31*2010')
    Traceback (most recent call last):
        ...
    ValueError: Date separator must be '-' or '/'

    Validate first separator:
    >>> zeller('01^31-2010')
    Traceback (most recent call last):
        ...
    ValueError: Date separator must be '-' or '/'

    Validate out of range year:
    >>> zeller('01-31-8999')
    Traceback (most recent call last):
        ...
    ValueError: Year out of range. There has to be some sort of limit...right?

    Test null input:
    >>> zeller()
    Traceback (most recent call last):
        ...
    TypeError: zeller() missing 1 required positional argument: 'date_input'

    Test length of date_input:
    >>> zeller('')
    Traceback (most recent call last):
        ...
    ValueError: Must be 10 characters long
    >>> zeller('01-31-19082939')
    Traceback (most recent call last):
        ...
    ValueError: Must be 10 characters long
"""

    # Days of the week for response
    days = {
        "0": "Sunday",
        "1": "Monday",
        "2": "Tuesday",
        "3": "Wednesday",
        "4": "Thursday",
        "5": "Friday",
        "6": "Saturday",
    }

    convert_datetime_days = {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 0}

    # Validate
    if not 0 < len(date_input) < 11:
        raise ValueError("Must be 10 characters long")

    # Get month
    m: int = int(date_input[0] + date_input[1])
    # Validate
    if not 0 < m < 13:
        raise ValueError("Month must be between 1 - 12")

    sep_1: str = date_input[2]
    # Validate
    if sep_1 not in ["-", "/"]:
        raise ValueError("Date separator must be '-' or '/'")

    # Get day
    d: int = int(date_input[3] + date_input[4])
    # Validate
    if not 0 < d < 32:
        raise ValueError("Date must be between 1 - 31")

    # Get second separator
    sep_2: str = date_input[5]
    # Validate
    if sep_2 not in ["-", "/"]:
        raise ValueError("Date separator must be '-' or '/'")

    # Get year
    y: int = int(date_input[6] + date_input[7] + date_input[8] + date_input[9])
    # Arbitrary year range
    if not 45 < y < 8500:
        raise ValueError(
            "Year out of range. There has to be some sort of limit...right?"
        )

    # Get datetime obj for validation
    dt_ck = datetime.date(int(y), int(m), int(d))

    # Start math
    if m <= 2:
        y = y - 1
        m = m + 12
    # maths var
    c: int = int(str(y)[:2])
    k: int = int(str(y)[2:])
    t: int = int(2.6 * m - 5.39)
    u: int = int(c / 4)
    v: int = int(k / 4)
    x: int = int(d + k)
    z: int = int(t + u + v + x)
    w: int = int(z - (2 * c))
    f: int = round(w % 7)
    # End math

    # Validate math
    if f != convert_datetime_days[dt_ck.weekday()]:
        raise AssertionError("The date was evaluated incorrectly. Contact developer.")

    # Response
    response: str = f"Your date {date_input}, is a {days[str(f)]}!"
    return response


if __name__ == "__main__":
    import doctest

    doctest.testmod()
    parser = argparse.ArgumentParser(
        description=(
            "Find out what day of the week nearly any date is or was. Enter "
            "date as a string in the mm-dd-yyyy or mm/dd/yyyy format"
        )
    )
    parser.add_argument(
        "date_input", type=str, help="Date as a string (mm-dd-yyyy or mm/dd/yyyy)"
    )
    args = parser.parse_args()
    zeller(args.date_input)

LANGUAGE:

DARK MODE: