From 237884cd1c4613b5442b040a20f00e7b99110417 Mon Sep 17 00:00:00 2001 From: Matthew Wilkes Date: Fri, 19 Aug 2022 17:25:35 +0100 Subject: [PATCH] Add support for relative dates If a date is entered but it cannot be parsed by arrow, try parsing it with dateparser. This enables support for a great many (but sadly not all) relative date expressions. --- docs/user-guide/commands.md | 9 +++++--- requirements.txt | 1 + tests/test_cli.py | 46 +++++++++++++++++++++++++++++-------- watson/__init__.py | 7 ++++++ watson/cli.py | 16 ++++++++++--- 5 files changed, 64 insertions(+), 15 deletions(-) diff --git a/docs/user-guide/commands.md b/docs/user-guide/commands.md index 387639f1..459a5603 100644 --- a/docs/user-guide/commands.md +++ b/docs/user-guide/commands.md @@ -43,7 +43,8 @@ Else, print the total for each root project. By default, the time spent the last 7 days is printed. This timespan can be controlled with the `--from` and `--to` arguments. The dates -must have the format `YEAR-MONTH-DAY`, like: `2014-05-19`. +should have the format `YEAR-MONTH-DAY`, like: `2014-05-19`, but relative +formats like `two weeks ago` are also supported. You can limit the report to a project or a tag using the `--project` and `--tag` options. They can be specified several times each to add multiple @@ -234,7 +235,8 @@ Display each recorded session during the given timespan. By default, the sessions from the last 7 days are printed. This timespan can be controlled with the `--from` and `--to` arguments. The dates -must have the format `YEAR-MONTH-DAY`, like: `2014-05-19`. +should have the format `YEAR-MONTH-DAY`, like: `2014-05-19`, but relative +formats like `two weeks ago` are also supported. You can also use special shortcut options for easier timespan control: `--day` sets the log timespan to the current day (beginning at `00:00h`) @@ -457,7 +459,8 @@ Else, print the total for each root project. By default, the time spent the last 7 days is printed. This timespan can be controlled with the `--from` and `--to` arguments. The dates -must have the format `YEAR-MONTH-DAY`, like: `2014-05-19`. +should have the format `YEAR-MONTH-DAY`, like: `2014-05-19`, but relative +formats like `two weeks ago` are also supported. You can also use special shortcut options for easier timespan control: `--day` sets the report timespan to the current day (beginning at `00:00h`) diff --git a/requirements.txt b/requirements.txt index 4b859d1a..9cb817c1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ arrow>=1.0.0 click>=8.0 click-didyoumean colorama; sys_platform == "win32" +dateparser requests diff --git a/tests/test_cli.py b/tests/test_cli.py index 5c283171..5dafe11f 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -12,6 +12,8 @@ # Not all ISO-8601 compliant strings are recognized by arrow.get(str) VALID_DATES_DATA = [ ('2018', '2018-01-01 00:00:00'), # years + (' 2018', '2018-01-01 00:00:00'), + ('2018 ', '2018-01-01 00:00:00'), ('2018-04', '2018-04-01 00:00:00'), # calendar dates ('2018-04-10', '2018-04-10 00:00:00'), ('2018/04/10', '2018-04-10 00:00:00'), @@ -20,9 +22,12 @@ ('2018/4/10', '2018-04-10 00:00:00'), ('2018.4.10', '2018-04-10 00:00:00'), ('20180410', '2018-04-10 00:00:00'), + ('18-04-10', '2018-04-10 00:00:00'), + ('2018-04-10T', '2018-04-10 00:00:00'), ('2018-123', '2018-05-03 00:00:00'), # ordinal dates ('2018-04-10 12:30:43', '2018-04-10 12:30:43'), ('2018-04-10T12:30:43', '2018-04-10 12:30:43'), + ('2018-04-10T12:30:43.', '2018-04-10 12:30:43'), ('2018-04-10 12:30:43Z', '2018-04-10 12:30:43'), ('2018-04-10 12:30:43.1233', '2018-04-10 12:30:43'), ('2018-04-10 12:30:43+03:00', '2018-04-10 12:30:43'), @@ -44,27 +49,50 @@ .replace(hour=14, minute=5, second=0) .format('YYYY-MM-DD HH:mm:ss') ), + ( + '14:05:12.000', + arrow.now() + .replace(hour=14, minute=5, second=12, microsecond=0) + .format('YYYY-MM-DD HH:mm:ss') + ), + ( + '14.05', + arrow.now() + .replace(hour=14, minute=5, second=0) + .format('YYYY-MM-DD HH:mm:ss') + ), + ('2018-W08', '2018-02-19 00:00:00'), # week dates ('2018W08', '2018-02-19 00:00:00'), ('2018-W08-2', '2018-02-20 00:00:00'), ('2018W082', '2018-02-20 00:00:00'), + ('yesterday 9am', + arrow.now() + .shift(days=-1) + .replace(hour=9, minute=0, second=0) + .format('YYYY-MM-DD HH:mm:ss') + ), + ( + 'tomorrow 9am', + arrow.now() + .shift(days=1) + .replace(hour=9, minute=0, second=0) + .format('YYYY-MM-DD HH:mm:ss') + ), + ( + '1.5 hours ago', + arrow.now() + .shift(hours=-1.5) + .format('YYYY-MM-DD HH:mm:ss') + ), ] INVALID_DATES_DATA = [ - (' 2018'), - ('2018 '), ('201804'), - ('18-04-10'), ('180410'), # truncated representation not allowed ('hello 2018'), - ('yesterday'), - ('tomorrow'), - ('14:05:12.000'), # Times alone are not allowed ('140512.000'), ('140512'), - ('14.05'), - ('2018-04-10T'), - ('2018-04-10T12:30:43.'), ] VALID_TIMES_DATA = [ diff --git a/watson/__init__.py b/watson/__init__.py index 7b408dd9..37860e97 100644 --- a/watson/__init__.py +++ b/watson/__init__.py @@ -1,4 +1,11 @@ +import warnings + from .watson import __version__ # noqa from .watson import Watson, WatsonError __all__ = ['Watson', 'WatsonError'] + +warnings.filterwarnings( + "ignore", + message="The localize method is no longer necessary, as this time zone supports the fold attribute", +) diff --git a/watson/cli.py b/watson/cli.py index 377b76b5..60671b78 100644 --- a/watson/cli.py +++ b/watson/cli.py @@ -9,6 +9,7 @@ import arrow import click from click_didyoumean import DYMGroup +import dateparser import watson as _watson from .autocompletion import ( @@ -109,6 +110,8 @@ def convert(self, value, param, ctx) -> arrow: def _parse_multiformat(self, value) -> arrow: date = None + if isinstance(value, str): + value = value.strip() for fmt in (None, 'HH:mm:ss', 'HH:mm'): try: if fmt is None: @@ -123,6 +126,10 @@ def _parse_multiformat(self, value) -> arrow: break except (ValueError, TypeError): pass + if date is None: + date = dateparser.parse(value, settings={'DATE_ORDER': 'YMD'}) + if date: + date = arrow.get(date) return date @@ -555,7 +562,8 @@ def report(watson, current, from_, to, projects, tags, ignore_projects, By default, the time spent the last 7 days is printed. This timespan can be controlled with the `--from` and `--to` arguments. The dates - must have the format `YEAR-MONTH-DAY`, like: `2014-05-19`. + should have the format `YEAR-MONTH-DAY`, like: `2014-05-19`, but relative + formats like `two weeks ago` are also supported. You can also use special shortcut options for easier timespan control: `--day` sets the report timespan to the current day (beginning at `00:00h`) @@ -808,7 +816,8 @@ def aggregate(ctx, watson, current, from_, to, projects, tags, output_format, By default, the time spent the last 7 days is printed. This timespan can be controlled with the `--from` and `--to` arguments. The dates - must have the format `YEAR-MONTH-DAY`, like: `2014-05-19`. + should have the format `YEAR-MONTH-DAY`, like: `2014-05-19`, but relative + formats like `two weeks ago` are also supported. You can limit the report to a project or a tag using the `--project` and `--tag` options. They can be specified several times each to add multiple @@ -978,7 +987,8 @@ def log(watson, current, reverse, from_, to, projects, tags, ignore_projects, By default, the sessions from the last 7 days are printed. This timespan can be controlled with the `--from` and `--to` arguments. The dates - must have the format `YEAR-MONTH-DAY`, like: `2014-05-19`. + should have the format `YEAR-MONTH-DAY`, like: `2014-05-19`, but relative + formats like `two weeks ago` are also supported. You can also use special shortcut options for easier timespan control: `--day` sets the log timespan to the current day (beginning at `00:00h`)