Skip to content

Commit

Permalink
Add option to select an alternative date format: dd-mm[-yyyy]
Browse files Browse the repository at this point in the history
  • Loading branch information
Epholys committed Jan 30, 2021
1 parent d598258 commit 5bc1024
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 16 deletions.
Binary file modified .coverage
Binary file not shown.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ The datetime must be one of the following format:
- (**TIME**:) `HH[:MM[:SS]]`. For example: `12:03:54` for this day, at 12h03m54s.
- `DATE TIME`. For example `09-03 13:23`

There is also an option in the menu which change the **DATE** format to the alternative: `dd-mm-[yyyy]`.

The timezone must either be one of the official timezone from the [tz database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones), or a shorthand. The shorthand are defined in the [tz-shorthands](./ultz/tz-shorthands.csv) file, and was generated so that the last part of the official timezone is enough. For example, `Paris` is a shorthand for `Europe/Paris`.

A full example would be `tz Tokyo at 15:30`, which will returns the time here, at 15:30 in Tokyo.
Expand Down
4 changes: 3 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ def on_event(self, event, extension):
if not expr:
return DoNothingAction()

result, description, icon = process_input(expr)
result, description, icon = process_input(
expr, extension.preferences["date-format"]
)

item = ExtensionResultItem(icon=icon, name=result, description=description)

Expand Down
17 changes: 17 additions & 0 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@
"name": "Timezone",
"description": "Keyword to launch the extension",
"default_value": "tz"
},
{
"id": "date-format",
"type": "select",
"name": "Date format",
"description": "Format for the date",
"options": [
{
"value": "ISO",
"text": "ISO format: [YYYY-]MM-DD"
},
{
"value": "ALT",
"text": "Alternative format: DD-MM[-YYYY]"
}
],
"default_value": "ISO"
}
]
}
31 changes: 30 additions & 1 deletion tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,37 @@ def test_iso(self) -> 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)


Expand Down
47 changes: 35 additions & 12 deletions ultz/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,55 @@
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
pass

# 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
Expand All @@ -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
"""

Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -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
5 changes: 3 additions & 2 deletions ultz/ultz.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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,
Expand All @@ -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:
Expand Down

0 comments on commit 5bc1024

Please sign in to comment.