From 5bc1024475719d8b0df76fe2ceb09ea6f1b06bb4 Mon Sep 17 00:00:00 2001 From: Epholys Date: Sat, 30 Jan 2021 18:13:56 +0100 Subject: [PATCH] Add option to select an alternative date format: dd-mm[-yyyy] --- .coverage | Bin 53248 -> 61440 bytes README.md | 2 ++ main.py | 4 +++- manifest.json | 17 ++++++++++++++++ tests/test_parser.py | 31 +++++++++++++++++++++++++++- ultz/parser.py | 47 ++++++++++++++++++++++++++++++++----------- ultz/ultz.py | 5 +++-- 7 files changed, 90 insertions(+), 16 deletions(-) diff --git a/.coverage b/.coverage index c55ca032a85dd9e9e2d6c7a82008b58f5795ead1..06fafa95b44953d437fa1c794b8d680bc60f226d 100644 GIT binary patch literal 61440 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 n|Nm#rU1hzb0#X5~fK)&#AQg}bNCl( None: self.assertEqual(parsed, expected) + @freeze_time("2222-02-22 22:22") + def test_ddmm(self) -> None: + dd: int = 22 + mm: int = 2 + + expected = dt.date(dt.date.today().year, mm, dd) + + user_input: str = f"{dd:02}-{mm:02}" + parsed = parser.parse_date(user_input, "ALT") + + self.assertEqual(parsed, expected) + + @freeze_time("1991-11-19 19:19") + def test_alt(self) -> None: + dd: int = 19 + mm: int = 11 + yyyy: int = 1991 + + expected = dt.date(yyyy, mm, dd) + + user_input: str = f"{dd:02}-{mm:02}-{yyyy:04}" + parsed = parser.parse_date(user_input, "ALT") + + self.assertEqual(parsed, expected) + def test_incorrect(self) -> None: - parsed = parser.parse_date("15-19") + parsed = parser.parse_date("15-02") + self.assertIsNone(parsed) + + def test_incorrect_alt(self) -> None: + parsed = parser.parse_date("02-15", "ALT") self.assertIsNone(parsed) diff --git a/ultz/parser.py b/ultz/parser.py index 32140a4..6886cec 100644 --- a/ultz/parser.py +++ b/ultz/parser.py @@ -10,26 +10,36 @@ from typing import Optional, Tuple -def parse_date(expr: str) -> Optional[dt.date]: +def parse_date(expr: str, form: str = "ISO") -> Optional[dt.date]: """Parse a string to a date. - Two format are supported: + Four format are supported: - - ISO 8601 format ``yyyy-mm-dd`` (like 2019-05-13). - - A shorter ``mm-dd`` format (like 11-24) that sets the year to the current one. + - If ``form`` is "ISO" (or everything other than "ALT"), the ISO 8601 format is used: + - Complete format: ``yyyy-mm-dd`` (like 2019-05-13). + - Shortened format: ``mm-dd`` format (like 11-24) that sets the year to the current one. + - Else, when ``form`` is "ALT", the following format is used: + - Complete format: ``dd-mm-yyyy`` (like 13-05-2019). + - Shortened format: ``dd-mm`` format (like 24-11) that sets the year to the current one. :param expr: The date to parse. + :param form: The format of the date to parse :returns: The date if ``expr`` was correctly passed, ``None`` otherwise """ - # Try to find a date in "mm-dd" format first - # (This is not supported by ISO 8601 as it requires "yyyy-" first) + print("=============== {} ===========".format(form)) + alternative = form == "ALT" + + # Try to find a date in the short format first month_day = expr.split("-") if len(month_day) == 2: try: year = dt.date.today().year month = (int)(month_day[0]) day = (int)(month_day[1]) + if alternative: + # Reverse + month, day = day, month date = dt.date(year, month, day) return date except ValueError: # pragma: nocover # Test the next format @@ -37,7 +47,18 @@ def parse_date(expr: str) -> Optional[dt.date]: # Then try the complete date try: - date = dt.date.fromisoformat(expr) + if not alternative: + date = dt.date.fromisoformat(expr) + return date + + # else: + day_month_year = expr.split("-") + if len(day_month_year) != 3: + raise ValueError + day = (int)(month_day[0]) + month = (int)(month_day[1]) + year = (int)(month_day[2]) + date = dt.date(year, month, day) return date except ValueError: # pragma: nocover pass @@ -64,13 +85,14 @@ def parse_time(expr: str) -> Optional[dt.time]: return None -def parse_datetime(datetime_expr: str) -> Optional[dt.datetime]: +def parse_datetime(datetime_expr: str, form: str = "ISO") -> Optional[dt.datetime]: """Parse a string into a full datetime The format supported is the combination of :func:`parse_date` and func:`parse_time`, in the format `date time`, date and time being optional if the other is present. :param expr: The datetime to parse. + :param form: The format for parsing the date part. :returns: The datetime if ``expr`` was correctly parsed, ``None`` otherwise """ @@ -89,7 +111,7 @@ def parse_datetime(datetime_expr: str) -> Optional[dt.datetime]: date_str = time_str = datetime_split[0] # Parse both of them - date = parse_date(date_str) + date = parse_date(date_str, form) time = parse_time(time_str) # None of them were correctly parsed @@ -133,7 +155,7 @@ class ExprCode(Enum): _ParsingResult = Tuple[ExprCode, Optional[str], Optional[dt.datetime]] -def parse_expression(expr: Optional[str]) -> _ParsingResult: +def parse_expression(expr: Optional[str], form: str = "ISO") -> _ParsingResult: """Parse an expression querying a timezone and optionally date. The expression is one of the follow formats: @@ -143,6 +165,7 @@ def parse_expression(expr: Optional[str]) -> _ParsingResult: * ``timezone at datetime`` :param expr: The expression to parse. + :param form: The format for parsing the date. :returns: - A return code indicating if the expression was correctly parsed and if\ so the format - A raw ``str`` timezone if applicable, ``None`` otherwise @@ -167,13 +190,13 @@ def parse_expression(expr: Optional[str]) -> _ParsingResult: if len_in == 2 and len_at == 1: # [time] in [location] location = split_in[1] - date = parse_datetime(split_in[0]) + date = parse_datetime(split_in[0], form) return ExprCode.TZ_DATEIN, location, date if len_in == 1 and len_at == 2: # [location] at [time] location = split_at[0] - date = parse_datetime(split_at[1]) + date = parse_datetime(split_at[1], form) return ExprCode.TZ_DATEAT, location, date return ExprCode.ERR, None, None diff --git a/ultz/ultz.py b/ultz/ultz.py index 3c0b358..c263c6d 100644 --- a/ultz/ultz.py +++ b/ultz/ultz.py @@ -127,7 +127,7 @@ def format_datetime(datetime: dt.datetime) -> str: return datetime.strftime("%Y-%m-%d %H:%M") -def process_input(text_input: Optional[str]) -> Tuple[str, str, str]: +def process_input(text_input: Optional[str], form: str = "ISO") -> Tuple[str, str, str]: """Process an expression for timezone conversion. The expression must be one of the following format: @@ -137,6 +137,7 @@ def process_input(text_input: Optional[str]) -> Tuple[str, str, str]: - ``timezone at datetime``: Query the time here, at ``datetime`` in ``timezone`` :param text_input: The expression to parse and interpret. + :param form: The format for parsing the date. :returns: - If ``text_input`` is correct, the datetime result. Otherwise, a descriptive error message. - If ``text_input`` is correct, a description of the result. Otherwise, @@ -146,7 +147,7 @@ def process_input(text_input: Optional[str]) -> Tuple[str, str, str]: """ - code, where, when = parse_expression(text_input) + code, where, when = parse_expression(text_input, form) _logger.debug("parse returned: where=%s, when=%s, code=%s", where, when, code) if code == ExprCode.ERR: