From 61b832990f522af5581e43d2b9b83988228ef8a4 Mon Sep 17 00:00:00 2001 From: discopatrick Date: Tue, 25 Apr 2017 01:05:46 +0100 Subject: [PATCH 01/19] =?UTF-8?q?Add=20a=20date=20query=20precision=20of?= =?UTF-8?q?=20=E2=80=98hour=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- beets/dbcore/query.py | 6 ++++-- test/test_datequery.py | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/beets/dbcore/query.py b/beets/dbcore/query.py index 8ce4dac41b..764ed04021 100644 --- a/beets/dbcore/query.py +++ b/beets/dbcore/query.py @@ -533,8 +533,8 @@ class Period(object): instants of time during January 2014. """ - precisions = ('year', 'month', 'day') - date_formats = ('%Y', '%Y-%m', '%Y-%m-%d') + precisions = ('year', 'month', 'day', 'hour') + date_formats = ('%Y', '%Y-%m', '%Y-%m-%d', '%Y-%m-%dT%H') def __init__(self, date, precision): """Create a period with the given date (a `datetime` object) and @@ -582,6 +582,8 @@ def open_right_endpoint(self): return date.replace(year=date.year + 1, month=1) elif 'day' == precision: return date + timedelta(days=1) + elif 'hour' == precision: + return date + timedelta(hours=1) else: raise ValueError(u'unhandled precision {0}'.format(precision)) diff --git a/test/test_datequery.py b/test/test_datequery.py index e81544aaad..d670d329ba 100644 --- a/test/test_datequery.py +++ b/test/test_datequery.py @@ -58,6 +58,10 @@ def test_month_precision_intervals(self): self.assertExcludes('1999-12..2000-02', '1999-11-30T23:59:59') self.assertExcludes('1999-12..2000-02', '2000-03-01T00:00:00') + def test_hour_precision_intervals(self): + self.assertContains('2000-01-01T12..2000-01-01T13', + '2000-01-01T12:30:00') + def test_unbounded_endpoints(self): self.assertContains('..', date=datetime.max) self.assertContains('..', date=datetime.min) From 5f2c47ec187eedb1d251e3b7f6b2d3a97c1f47f5 Mon Sep 17 00:00:00 2001 From: discopatrick Date: Tue, 25 Apr 2017 01:16:50 +0100 Subject: [PATCH 02/19] Test further hour precision intervals --- test/test_datequery.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/test_datequery.py b/test/test_datequery.py index d670d329ba..12ce224c49 100644 --- a/test/test_datequery.py +++ b/test/test_datequery.py @@ -59,8 +59,20 @@ def test_month_precision_intervals(self): self.assertExcludes('1999-12..2000-02', '2000-03-01T00:00:00') def test_hour_precision_intervals(self): + self.assertExcludes('2000-01-01T12..2000-01-01T13', + '2000-01-01T11:59:59') + self.assertContains('2000-01-01T12..2000-01-01T13', + '2000-01-01T12:00:00') self.assertContains('2000-01-01T12..2000-01-01T13', '2000-01-01T12:30:00') + self.assertContains('2000-01-01T12..2000-01-01T13', + '2000-01-01T13:30:00') + self.assertContains('2000-01-01T12..2000-01-01T13', + '2000-01-01T13:59:59') + self.assertExcludes('2000-01-01T12..2000-01-01T13', + '2000-01-01T14:00:00') + self.assertExcludes('2000-01-01T12..2000-01-01T13', + '2000-01-01T14:30:00') def test_unbounded_endpoints(self): self.assertContains('..', date=datetime.max) From ba324df0d1f17e4ffb7de97a580a0fe33610097b Mon Sep 17 00:00:00 2001 From: discopatrick Date: Tue, 25 Apr 2017 01:37:57 +0100 Subject: [PATCH 03/19] =?UTF-8?q?Add=20a=20date=20query=20precision=20of?= =?UTF-8?q?=20=E2=80=98minute=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- beets/dbcore/query.py | 6 ++++-- test/test_datequery.py | 12 ++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/beets/dbcore/query.py b/beets/dbcore/query.py index 764ed04021..da466a6ba0 100644 --- a/beets/dbcore/query.py +++ b/beets/dbcore/query.py @@ -533,8 +533,8 @@ class Period(object): instants of time during January 2014. """ - precisions = ('year', 'month', 'day', 'hour') - date_formats = ('%Y', '%Y-%m', '%Y-%m-%d', '%Y-%m-%dT%H') + precisions = ('year', 'month', 'day', 'hour', 'minute') + date_formats = ('%Y', '%Y-%m', '%Y-%m-%d', '%Y-%m-%dT%H', '%Y-%m-%dT%H:%M') def __init__(self, date, precision): """Create a period with the given date (a `datetime` object) and @@ -584,6 +584,8 @@ def open_right_endpoint(self): return date + timedelta(days=1) elif 'hour' == precision: return date + timedelta(hours=1) + elif 'minute' == precision: + return date + timedelta(minutes=1) else: raise ValueError(u'unhandled precision {0}'.format(precision)) diff --git a/test/test_datequery.py b/test/test_datequery.py index 12ce224c49..ea1974c411 100644 --- a/test/test_datequery.py +++ b/test/test_datequery.py @@ -74,6 +74,18 @@ def test_hour_precision_intervals(self): self.assertExcludes('2000-01-01T12..2000-01-01T13', '2000-01-01T14:30:00') + def test_minute_precision_intervals(self): + self.assertExcludes('2000-01-01T12:30..2000-01-01T12:31', + '2000-01-01T12:29:59') + self.assertContains('2000-01-01T12:30..2000-01-01T12:31', + '2000-01-01T12:30:00') + self.assertContains('2000-01-01T12:30..2000-01-01T12:31', + '2000-01-01T12:30:30') + self.assertContains('2000-01-01T12:30..2000-01-01T12:31', + '2000-01-01T12:30:59') + self.assertExcludes('2000-01-01T12:30..2000-01-01T12:31', + '2000-01-01T12:31:00') + def test_unbounded_endpoints(self): self.assertContains('..', date=datetime.max) self.assertContains('..', date=datetime.min) From b8e1c5675e21a6605c5770ae46f38057b8b54701 Mon Sep 17 00:00:00 2001 From: discopatrick Date: Tue, 25 Apr 2017 02:25:50 +0100 Subject: [PATCH 04/19] Fix tests --- test/test_datequery.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_datequery.py b/test/test_datequery.py index ea1974c411..b1398201c2 100644 --- a/test/test_datequery.py +++ b/test/test_datequery.py @@ -82,9 +82,9 @@ def test_minute_precision_intervals(self): self.assertContains('2000-01-01T12:30..2000-01-01T12:31', '2000-01-01T12:30:30') self.assertContains('2000-01-01T12:30..2000-01-01T12:31', - '2000-01-01T12:30:59') + '2000-01-01T12:31:59') self.assertExcludes('2000-01-01T12:30..2000-01-01T12:31', - '2000-01-01T12:31:00') + '2000-01-01T12:32:00') def test_unbounded_endpoints(self): self.assertContains('..', date=datetime.max) From 05f0072363ede939ca023ba6f1ad7ab2afde5105 Mon Sep 17 00:00:00 2001 From: discopatrick Date: Wed, 26 Apr 2017 23:46:17 +0100 Subject: [PATCH 05/19] Update docstring --- beets/dbcore/query.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/beets/dbcore/query.py b/beets/dbcore/query.py index da466a6ba0..7b07284083 100644 --- a/beets/dbcore/query.py +++ b/beets/dbcore/query.py @@ -538,7 +538,8 @@ class Period(object): def __init__(self, date, precision): """Create a period with the given date (a `datetime` object) and - precision (a string, one of "year", "month", or "day"). + precision (a string, one of "year", "month", "day", "hour", "minute", or + "second"). """ if precision not in Period.precisions: raise ValueError(u'Invalid precision {0}'.format(precision)) From 6a71504545c154cc453b7f24f9b0328a140163fa Mon Sep 17 00:00:00 2001 From: discopatrick Date: Wed, 26 Apr 2017 23:53:51 +0100 Subject: [PATCH 06/19] Allow multiple date formats for each precision We want to allow datetime queries to be entered in multiple formats, e.g. with a 'T' or a space separator between date and time. This commit sets up that possibility, albeit with an additional dummy format for the time being. --- beets/dbcore/query.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/beets/dbcore/query.py b/beets/dbcore/query.py index 7b07284083..3014e73a5c 100644 --- a/beets/dbcore/query.py +++ b/beets/dbcore/query.py @@ -534,7 +534,7 @@ class Period(object): """ precisions = ('year', 'month', 'day', 'hour', 'minute') - date_formats = ('%Y', '%Y-%m', '%Y-%m-%d', '%Y-%m-%dT%H', '%Y-%m-%dT%H:%M') + date_formats = (('%Y',), ('%Y-%m',), ('%Y-%m-%d',), ('%Y-%m-%dT%H', 'dummy-format'), ('%Y-%m-%dT%H:%M',)) def __init__(self, date, precision): """Create a period with the given date (a `datetime` object) and @@ -555,13 +555,19 @@ def parse(cls, string): if not string: return None date = None + found = False for ordinal, date_format in enumerate(cls.date_formats): - try: - date = datetime.strptime(string, date_format) + if found is True: + ordinal -= 1 break - except ValueError: - # Parsing failed. - pass + for format_option in date_format: + try: + date = datetime.strptime(string, format_option) + found = True + break + except ValueError: + # Parsing failed. + pass if date is None: raise InvalidQueryArgumentTypeError(string, 'a valid datetime string') From c3771f722ce08972cd73f7b34f96e075de98ad77 Mon Sep 17 00:00:00 2001 From: discopatrick Date: Thu, 27 Apr 2017 00:04:46 +0100 Subject: [PATCH 07/19] Allow hour precision queries to use space separator --- beets/dbcore/query.py | 2 +- test/test_datequery.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/beets/dbcore/query.py b/beets/dbcore/query.py index 3014e73a5c..4c109c5b8e 100644 --- a/beets/dbcore/query.py +++ b/beets/dbcore/query.py @@ -534,7 +534,7 @@ class Period(object): """ precisions = ('year', 'month', 'day', 'hour', 'minute') - date_formats = (('%Y',), ('%Y-%m',), ('%Y-%m-%d',), ('%Y-%m-%dT%H', 'dummy-format'), ('%Y-%m-%dT%H:%M',)) + date_formats = (('%Y',), ('%Y-%m',), ('%Y-%m-%d',), ('%Y-%m-%dT%H', '%Y-%m-%d %H'), ('%Y-%m-%dT%H:%M',)) def __init__(self, date, precision): """Create a period with the given date (a `datetime` object) and diff --git a/test/test_datequery.py b/test/test_datequery.py index b1398201c2..743688b4d7 100644 --- a/test/test_datequery.py +++ b/test/test_datequery.py @@ -59,6 +59,7 @@ def test_month_precision_intervals(self): self.assertExcludes('1999-12..2000-02', '2000-03-01T00:00:00') def test_hour_precision_intervals(self): + # test with 'T' separator self.assertExcludes('2000-01-01T12..2000-01-01T13', '2000-01-01T11:59:59') self.assertContains('2000-01-01T12..2000-01-01T13', @@ -74,6 +75,12 @@ def test_hour_precision_intervals(self): self.assertExcludes('2000-01-01T12..2000-01-01T13', '2000-01-01T14:30:00') + # test with ' ' (space) separator + self.assertExcludes('2000-01-01 12..2000-01-01 13', + '2000-01-01T11:59:59') + self.assertContains('2000-01-01 12..2000-01-01 13', + '2000-01-01T12:00:00') + def test_minute_precision_intervals(self): self.assertExcludes('2000-01-01T12:30..2000-01-01T12:31', '2000-01-01T12:29:59') From 04e2975ee954e0117a7b79628809cc3ea0676ea0 Mon Sep 17 00:00:00 2001 From: discopatrick Date: Wed, 26 Apr 2017 23:21:47 +0100 Subject: [PATCH 08/19] Separate date formats onto individual lines --- beets/dbcore/query.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/beets/dbcore/query.py b/beets/dbcore/query.py index 4c109c5b8e..949908bc25 100644 --- a/beets/dbcore/query.py +++ b/beets/dbcore/query.py @@ -534,7 +534,13 @@ class Period(object): """ precisions = ('year', 'month', 'day', 'hour', 'minute') - date_formats = (('%Y',), ('%Y-%m',), ('%Y-%m-%d',), ('%Y-%m-%dT%H', '%Y-%m-%d %H'), ('%Y-%m-%dT%H:%M',)) + date_formats = ( + ('%Y',), # year + ('%Y-%m',), # month + ('%Y-%m-%d',), # day + ('%Y-%m-%dT%H', '%Y-%m-%d %H'), # hour + ('%Y-%m-%dT%H:%M',) # minute + ) def __init__(self, date, precision): """Create a period with the given date (a `datetime` object) and From c10eb8f69dbde91d947b25d16c8596efee06d13a Mon Sep 17 00:00:00 2001 From: discopatrick Date: Wed, 26 Apr 2017 23:23:21 +0100 Subject: [PATCH 09/19] Keep docstring line <= 79 characters --- beets/dbcore/query.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beets/dbcore/query.py b/beets/dbcore/query.py index 949908bc25..e7001dcb20 100644 --- a/beets/dbcore/query.py +++ b/beets/dbcore/query.py @@ -544,8 +544,8 @@ class Period(object): def __init__(self, date, precision): """Create a period with the given date (a `datetime` object) and - precision (a string, one of "year", "month", "day", "hour", "minute", or - "second"). + precision (a string, one of "year", "month", "day", "hour", "minute", + or "second"). """ if precision not in Period.precisions: raise ValueError(u'Invalid precision {0}'.format(precision)) From 02bd19fb328a433165b25d29268f14b7d7f5996d Mon Sep 17 00:00:00 2001 From: discopatrick Date: Wed, 26 Apr 2017 23:33:53 +0100 Subject: [PATCH 10/19] Allow minute precision queries to use space separator --- beets/dbcore/query.py | 2 +- test/test_datequery.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/beets/dbcore/query.py b/beets/dbcore/query.py index e7001dcb20..f9090e76bd 100644 --- a/beets/dbcore/query.py +++ b/beets/dbcore/query.py @@ -539,7 +539,7 @@ class Period(object): ('%Y-%m',), # month ('%Y-%m-%d',), # day ('%Y-%m-%dT%H', '%Y-%m-%d %H'), # hour - ('%Y-%m-%dT%H:%M',) # minute + ('%Y-%m-%dT%H:%M', '%Y-%m-%d %H:%M') # minute ) def __init__(self, date, precision): diff --git a/test/test_datequery.py b/test/test_datequery.py index 743688b4d7..14156febcc 100644 --- a/test/test_datequery.py +++ b/test/test_datequery.py @@ -93,6 +93,12 @@ def test_minute_precision_intervals(self): self.assertExcludes('2000-01-01T12:30..2000-01-01T12:31', '2000-01-01T12:32:00') + # test with ' ' (space) separator + self.assertExcludes('2000-01-01 12:30..2000-01-01 12:31', + '2000-01-01T12:29:59') + self.assertContains('2000-01-01 12:30..2000-01-01 12:31', + '2000-01-01T12:30:00') + def test_unbounded_endpoints(self): self.assertContains('..', date=datetime.max) self.assertContains('..', date=datetime.min) From 24890c77f1fe3228950fb92a943f6266c07ddaaa Mon Sep 17 00:00:00 2001 From: discopatrick Date: Thu, 27 Apr 2017 00:24:17 +0100 Subject: [PATCH 11/19] =?UTF-8?q?Add=20a=20date=20query=20precision=20of?= =?UTF-8?q?=20=E2=80=98second=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- beets/dbcore/query.py | 7 +++++-- test/test_datequery.py | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/beets/dbcore/query.py b/beets/dbcore/query.py index f9090e76bd..3e6ff270d2 100644 --- a/beets/dbcore/query.py +++ b/beets/dbcore/query.py @@ -533,13 +533,14 @@ class Period(object): instants of time during January 2014. """ - precisions = ('year', 'month', 'day', 'hour', 'minute') + precisions = ('year', 'month', 'day', 'hour', 'minute', 'second') date_formats = ( ('%Y',), # year ('%Y-%m',), # month ('%Y-%m-%d',), # day ('%Y-%m-%dT%H', '%Y-%m-%d %H'), # hour - ('%Y-%m-%dT%H:%M', '%Y-%m-%d %H:%M') # minute + ('%Y-%m-%dT%H:%M', '%Y-%m-%d %H:%M'), # minute + ('%Y-%m-%dT%H:%M:%S', '%Y-%m-%d %H:%M:%S') # second ) def __init__(self, date, precision): @@ -599,6 +600,8 @@ def open_right_endpoint(self): return date + timedelta(hours=1) elif 'minute' == precision: return date + timedelta(minutes=1) + elif 'second' == precision: + return date + timedelta(seconds=1) else: raise ValueError(u'unhandled precision {0}'.format(precision)) diff --git a/test/test_datequery.py b/test/test_datequery.py index 14156febcc..99381be193 100644 --- a/test/test_datequery.py +++ b/test/test_datequery.py @@ -99,6 +99,22 @@ def test_minute_precision_intervals(self): self.assertContains('2000-01-01 12:30..2000-01-01 12:31', '2000-01-01T12:30:00') + def test_second_precision_intervals(self): + self.assertExcludes('2000-01-01T12:30:50..2000-01-01T12:30:55', + '2000-01-01T12:30:49') + self.assertContains('2000-01-01T12:30:50..2000-01-01T12:30:55', + '2000-01-01T12:30:50') + self.assertContains('2000-01-01T12:30:50..2000-01-01T12:30:55', + '2000-01-01T12:30:55') + self.assertExcludes('2000-01-01T12:30:50..2000-01-01T12:30:55', + '2000-01-01T12:30:56') + + # test with ' ' (space) separator + self.assertExcludes('2000-01-01 12:30:50..2000-01-01 12:30:55', + '2000-01-01T12:30:49') + self.assertContains('2000-01-01 12:30:50..2000-01-01 12:30:55', + '2000-01-01T12:30:50') + def test_unbounded_endpoints(self): self.assertContains('..', date=datetime.max) self.assertContains('..', date=datetime.min) From 1ab913b200c8510e9f58a2b310819810b16df2c5 Mon Sep 17 00:00:00 2001 From: discopatrick Date: Thu, 27 Apr 2017 11:28:10 +0100 Subject: [PATCH 12/19] Test each valid datetime separator --- test/test_datequery.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/test_datequery.py b/test/test_datequery.py index 99381be193..c15b08cbdd 100644 --- a/test/test_datequery.py +++ b/test/test_datequery.py @@ -197,6 +197,21 @@ def test_invalid_date_query(self): with self.assertRaises(InvalidQueryArgumentTypeError): DateQuery('added', q) + def test_datetime_T_separator(self): + date_query = DateQuery('added', '2000-01-01T12') + self.assertEqual(date_query.interval.start, datetime(2000, 1, 1, 12)) + self.assertEqual(date_query.interval.end, datetime(2000, 1, 1, 13)) + + def test_datetime_t_separator(self): + date_query = DateQuery('added', '2000-01-01t12') + self.assertEqual(date_query.interval.start, datetime(2000, 1, 1, 12)) + self.assertEqual(date_query.interval.end, datetime(2000, 1, 1, 13)) + + def test_datetime_space_separator(self): + date_query = DateQuery('added', '2000-01-01 12') + self.assertEqual(date_query.interval.start, datetime(2000, 1, 1, 12)) + self.assertEqual(date_query.interval.end, datetime(2000, 1, 1, 13)) + def suite(): return unittest.TestLoader().loadTestsFromName(__name__) From 5a3b74f681b0278e11c1d3174f5dac18dcc7806e Mon Sep 17 00:00:00 2001 From: discopatrick Date: Thu, 27 Apr 2017 11:29:45 +0100 Subject: [PATCH 13/19] Test an invalid datetime separator raises error --- test/test_datequery.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/test_datequery.py b/test/test_datequery.py index c15b08cbdd..c702c70e57 100644 --- a/test/test_datequery.py +++ b/test/test_datequery.py @@ -212,6 +212,10 @@ def test_datetime_space_separator(self): self.assertEqual(date_query.interval.start, datetime(2000, 1, 1, 12)) self.assertEqual(date_query.interval.end, datetime(2000, 1, 1, 13)) + def test_datetime_invalid_separator(self): + with self.assertRaises(InvalidQueryArgumentTypeError): + DateQuery('added', '2000-01-01x12') + def suite(): return unittest.TestLoader().loadTestsFromName(__name__) From 6e6dd76513d11c90732d8156b72a9894e793f543 Mon Sep 17 00:00:00 2001 From: discopatrick Date: Thu, 27 Apr 2017 11:36:03 +0100 Subject: [PATCH 14/19] Remove space separator tests from test_x_precision_intervals tests This is not the correct place for space separator tests. Each test should test one thing only. Space separator tests are now in a separate test case. --- test/test_datequery.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/test/test_datequery.py b/test/test_datequery.py index c702c70e57..d2223a8d74 100644 --- a/test/test_datequery.py +++ b/test/test_datequery.py @@ -75,12 +75,6 @@ def test_hour_precision_intervals(self): self.assertExcludes('2000-01-01T12..2000-01-01T13', '2000-01-01T14:30:00') - # test with ' ' (space) separator - self.assertExcludes('2000-01-01 12..2000-01-01 13', - '2000-01-01T11:59:59') - self.assertContains('2000-01-01 12..2000-01-01 13', - '2000-01-01T12:00:00') - def test_minute_precision_intervals(self): self.assertExcludes('2000-01-01T12:30..2000-01-01T12:31', '2000-01-01T12:29:59') @@ -93,12 +87,6 @@ def test_minute_precision_intervals(self): self.assertExcludes('2000-01-01T12:30..2000-01-01T12:31', '2000-01-01T12:32:00') - # test with ' ' (space) separator - self.assertExcludes('2000-01-01 12:30..2000-01-01 12:31', - '2000-01-01T12:29:59') - self.assertContains('2000-01-01 12:30..2000-01-01 12:31', - '2000-01-01T12:30:00') - def test_second_precision_intervals(self): self.assertExcludes('2000-01-01T12:30:50..2000-01-01T12:30:55', '2000-01-01T12:30:49') @@ -109,12 +97,6 @@ def test_second_precision_intervals(self): self.assertExcludes('2000-01-01T12:30:50..2000-01-01T12:30:55', '2000-01-01T12:30:56') - # test with ' ' (space) separator - self.assertExcludes('2000-01-01 12:30:50..2000-01-01 12:30:55', - '2000-01-01T12:30:49') - self.assertContains('2000-01-01 12:30:50..2000-01-01 12:30:55', - '2000-01-01T12:30:50') - def test_unbounded_endpoints(self): self.assertContains('..', date=datetime.max) self.assertContains('..', date=datetime.min) From 50a2e37a4d8033e6340942f1d59322660eebcd59 Mon Sep 17 00:00:00 2001 From: discopatrick Date: Thu, 27 Apr 2017 15:58:08 +0100 Subject: [PATCH 15/19] Keep function names lowercase to pass flake8 tests --- test/test_datequery.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_datequery.py b/test/test_datequery.py index d2223a8d74..b8bb109257 100644 --- a/test/test_datequery.py +++ b/test/test_datequery.py @@ -179,12 +179,12 @@ def test_invalid_date_query(self): with self.assertRaises(InvalidQueryArgumentTypeError): DateQuery('added', q) - def test_datetime_T_separator(self): + def test_datetime_uppercase_t_separator(self): date_query = DateQuery('added', '2000-01-01T12') self.assertEqual(date_query.interval.start, datetime(2000, 1, 1, 12)) self.assertEqual(date_query.interval.end, datetime(2000, 1, 1, 13)) - def test_datetime_t_separator(self): + def test_datetime_lowercase_t_separator(self): date_query = DateQuery('added', '2000-01-01t12') self.assertEqual(date_query.interval.start, datetime(2000, 1, 1, 12)) self.assertEqual(date_query.interval.end, datetime(2000, 1, 1, 13)) From 1c0b79590e53335ee7c82241b6e92d8ab9472f40 Mon Sep 17 00:00:00 2001 From: discopatrick Date: Mon, 1 May 2017 03:05:48 +0100 Subject: [PATCH 16/19] Refactor date-finding loop into an inner function This is an attempt to make this code more robust and more readable. --- beets/dbcore/query.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/beets/dbcore/query.py b/beets/dbcore/query.py index 3e6ff270d2..89ee7aef7e 100644 --- a/beets/dbcore/query.py +++ b/beets/dbcore/query.py @@ -559,22 +559,21 @@ def parse(cls, string): string is empty, or raise an InvalidQueryArgumentTypeError if the string could not be parsed to a date. """ + + def find_date_and_format(string): + for ord, format in enumerate(cls.date_formats): + for format_option in format: + try: + date = datetime.strptime(string, format_option) + return date, ord + except ValueError: + # Parsing failed. + pass + return (None, None) + if not string: return None - date = None - found = False - for ordinal, date_format in enumerate(cls.date_formats): - if found is True: - ordinal -= 1 - break - for format_option in date_format: - try: - date = datetime.strptime(string, format_option) - found = True - break - except ValueError: - # Parsing failed. - pass + date, ordinal = find_date_and_format(string) if date is None: raise InvalidQueryArgumentTypeError(string, 'a valid datetime string') From e1101d4e95664ef593135b16c846886cb55532c8 Mon Sep 17 00:00:00 2001 From: discopatrick Date: Thu, 1 Jun 2017 12:33:23 +0100 Subject: [PATCH 17/19] Update assertion with correct error name --- test/test_datequery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_datequery.py b/test/test_datequery.py index 6cd97fd306..71daa42e44 100644 --- a/test/test_datequery.py +++ b/test/test_datequery.py @@ -195,7 +195,7 @@ def test_datetime_space_separator(self): self.assertEqual(date_query.interval.end, datetime(2000, 1, 1, 13)) def test_datetime_invalid_separator(self): - with self.assertRaises(InvalidQueryArgumentTypeError): + with self.assertRaises(InvalidQueryArgumentValueError): DateQuery('added', '2000-01-01x12') From 95eeec937c7466987cd53e39207d4fff41fc0332 Mon Sep 17 00:00:00 2001 From: discopatrick Date: Thu, 1 Jun 2017 13:11:40 +0100 Subject: [PATCH 18/19] Add docs for datetime queries --- docs/reference/query.rst | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/reference/query.rst b/docs/reference/query.rst index b4789aa106..70988f0267 100644 --- a/docs/reference/query.rst +++ b/docs/reference/query.rst @@ -188,6 +188,33 @@ Find all items with a file modification time between 2008-12-01 and $ beet ls 'mtime:2008-12-01..2008-12-02' +You can also add an optional time value to date queries, specifying hours, +minutes, and seconds. + +Times are separated from dates by a space, an uppercase 'T' or a lowercase +'t', for example: ``2008-12-01T23:59:59``. If you specify a time, then the +date must contain a year, month, and day. The minutes and seconds are +optional. + +Here is an example that finds all items added on 2008-12-01 at or after 22:00 +but before 23:00:: + + $ beet ls 'added:2008-12-01T22' + +Find all items added on or after 2008-12-01 22:45:: + + $ beet ls 'added:2008-12-01T22:45..' + +Find all items added on 2008-12-01, at or after 22:45:20 but before 22:45:41:: + + $ beet ls 'added:2008-12-01T22:45:20..2008-12-01T22:45:40' + +Examples of each time format:: + + $ beet ls 'added:2008-12-01T22:45:20' + $ beet ls 'added:2008-12-01t22:45:20' + $ beet ls 'added:2008-12-01 22:45:20' + .. _not_query: Query Term Negation From 291b287f562b8d268a5495ffb9bc3dcae7c0429d Mon Sep 17 00:00:00 2001 From: discopatrick Date: Mon, 5 Jun 2017 16:31:37 +0100 Subject: [PATCH 19/19] Add a test for a non-range date query --- test/test_datequery.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/test_datequery.py b/test/test_datequery.py index 71daa42e44..ba88570840 100644 --- a/test/test_datequery.py +++ b/test/test_datequery.py @@ -75,6 +75,12 @@ def test_hour_precision_intervals(self): self.assertExcludes('2000-01-01T12..2000-01-01T13', '2000-01-01T14:30:00') + # test non-range query + self.assertContains('2008-12-01T22', + '2008-12-01T22:30:00') + self.assertExcludes('2008-12-01T22', + '2008-12-01T23:30:00') + def test_minute_precision_intervals(self): self.assertExcludes('2000-01-01T12:30..2000-01-01T12:31', '2000-01-01T12:29:59')