From 207cd8f9cf16e13040e2418454804c8b0639c5cd Mon Sep 17 00:00:00 2001 From: Baviaan Date: Tue, 19 Oct 2021 18:06:51 +0100 Subject: [PATCH 1/3] Fix #403 --- dateparser/date_parser.py | 2 +- dateparser/parser.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/dateparser/date_parser.py b/dateparser/date_parser.py index 7c36a2fbc..3a58399e6 100644 --- a/dateparser/date_parser.py +++ b/dateparser/date_parser.py @@ -17,7 +17,7 @@ def parse(self, date_string, parse_method, settings=None): date_string = strip_braces(date_string) date_string, ptz = pop_tz_offset_from_string(date_string) - date_obj, period = parse_method(date_string, settings=settings) + date_obj, period = parse_method(date_string, settings=settings, ptz=ptz) _settings_tz = settings.TIMEZONE.lower() diff --git a/dateparser/parser.py b/dateparser/parser.py index e2df66b03..deb1991ed 100644 --- a/dateparser/parser.py +++ b/dateparser/parser.py @@ -63,11 +63,11 @@ def resolve_date_order(order, lst=None): return chart_list[order] if lst else date_order_chart[order] -def _parse_absolute(datestring, settings): - return _parser.parse(datestring, settings) +def _parse_absolute(datestring, settings, ptz=None): + return _parser.parse(datestring, settings, ptz) -def _parse_nospaces(datestring, settings): +def _parse_nospaces(datestring, settings, ptz=None): return _no_spaces_parser.parse(datestring, settings) @@ -517,13 +517,17 @@ def _correct_for_day(self, dateobj): return dateobj @classmethod - def parse(cls, datestring, settings): + def parse(cls, datestring, settings, ptz=None): tokens = tokenizer(datestring) po = cls(tokens.tokenize(), settings) dateobj = po._results() + if ptz: # Shift naive time by utc offset before comparing to utc during corrections + dateobj = dateobj - ptz.utcoffset(None) # This argument does nothing? # correction for past, future if applicable dateobj = po._correct_for_time_frame(dateobj) + if ptz: # Shift time back by utc offset + dateobj = dateobj + ptz.utcoffset(None) # This argument does nothing? # correction for preference of day: beginning, current, end dateobj = po._correct_for_day(dateobj) From 8caa4aab0c75c450fe5f8be94b85a809e035de8f Mon Sep 17 00:00:00 2001 From: Baviaan Date: Tue, 19 Oct 2021 23:04:43 +0100 Subject: [PATCH 2/3] pass tz to _correct_for_time_frame --- dateparser/date_parser.py | 2 +- dateparser/parser.py | 25 +++++++++++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/dateparser/date_parser.py b/dateparser/date_parser.py index 3a58399e6..1dda44113 100644 --- a/dateparser/date_parser.py +++ b/dateparser/date_parser.py @@ -17,7 +17,7 @@ def parse(self, date_string, parse_method, settings=None): date_string = strip_braces(date_string) date_string, ptz = pop_tz_offset_from_string(date_string) - date_obj, period = parse_method(date_string, settings=settings, ptz=ptz) + date_obj, period = parse_method(date_string, settings=settings, tz=ptz) _settings_tz = settings.TIMEZONE.lower() diff --git a/dateparser/parser.py b/dateparser/parser.py index deb1991ed..d64c62ad8 100644 --- a/dateparser/parser.py +++ b/dateparser/parser.py @@ -63,11 +63,11 @@ def resolve_date_order(order, lst=None): return chart_list[order] if lst else date_order_chart[order] -def _parse_absolute(datestring, settings, ptz=None): - return _parser.parse(datestring, settings, ptz) +def _parse_absolute(datestring, settings, tz=None): + return _parser.parse(datestring, settings, tz) -def _parse_nospaces(datestring, settings, ptz=None): +def _parse_nospaces(datestring, settings, tz=None): return _no_spaces_parser.parse(datestring, settings) @@ -417,7 +417,7 @@ def _results(self): return self._get_datetime_obj(**params) - def _correct_for_time_frame(self, dateobj): + def _correct_for_time_frame(self, dateobj, tz): days = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'] token_weekday, _ = getattr(self, '_token_weekday', (None, None)) @@ -490,11 +490,16 @@ def _correct_for_time_frame(self, dateobj): self._token_month, self._token_day, hasattr(self, '_token_weekday')]): + # Convert dateobj to utc time to compare with self.now + if tz: + dateobj_time = (dateobj - tz.utcoffset(dateobj)).time() + else: + dateobj_time = dateobj.time() if 'past' in self.settings.PREFER_DATES_FROM: - if self.now.time() < dateobj.time(): + if self.now.time() < dateobj_time: dateobj = dateobj + timedelta(days=-1) if 'future' in self.settings.PREFER_DATES_FROM: - if self.now.time() > dateobj.time(): + if self.now.time() > dateobj_time: dateobj = dateobj + timedelta(days=1) # Reset dateobj to the original value, thus removing any offset awareness that may @@ -517,17 +522,13 @@ def _correct_for_day(self, dateobj): return dateobj @classmethod - def parse(cls, datestring, settings, ptz=None): + def parse(cls, datestring, settings, tz=None): tokens = tokenizer(datestring) po = cls(tokens.tokenize(), settings) dateobj = po._results() - if ptz: # Shift naive time by utc offset before comparing to utc during corrections - dateobj = dateobj - ptz.utcoffset(None) # This argument does nothing? # correction for past, future if applicable - dateobj = po._correct_for_time_frame(dateobj) - if ptz: # Shift time back by utc offset - dateobj = dateobj + ptz.utcoffset(None) # This argument does nothing? + dateobj = po._correct_for_time_frame(dateobj, tz) # correction for preference of day: beginning, current, end dateobj = po._correct_for_day(dateobj) From 50c3872fb670bb46acb1ec0d63559502a9668abb Mon Sep 17 00:00:00 2001 From: Baviaan Date: Tue, 19 Oct 2021 23:05:29 +0100 Subject: [PATCH 3/3] add tests for prefer dates from future/past --- tests/test_date_parser.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/test_date_parser.py b/tests/test_date_parser.py index ead18292b..384278296 100644 --- a/tests/test_date_parser.py +++ b/tests/test_date_parser.py @@ -806,6 +806,32 @@ def test_parsing_strings_containing_only_separator_tokens(self, date_string, exp self.then_period_is('day') self.then_date_obj_exactly_is(expected) + @parameterized.expand([ + param('4pm EDT', datetime(2021, 10, 19, 20, 0)), + ]) + def test_date_skip_ahead(self, date_string, expected): + self.given_parser(settings={'PREFER_DATES_FROM': 'future', + 'TO_TIMEZONE': 'etc/utc', + 'RETURN_AS_TIMEZONE_AWARE': False, + 'RELATIVE_BASE': datetime(2021, 10, 19, 18, 0), + }) + self.when_date_is_parsed(date_string) + self.then_date_was_parsed_by_date_parser() + self.then_date_obj_exactly_is(expected) + + @parameterized.expand([ + param('11pm AEDT', datetime(2021, 10, 19, 12, 0)), + ]) + def test_date_step_back(self, date_string, expected): + self.given_parser(settings={'PREFER_DATES_FROM': 'past', + 'TO_TIMEZONE': 'etc/utc', + 'RETURN_AS_TIMEZONE_AWARE': False, + 'RELATIVE_BASE': datetime(2021, 10, 19, 18, 0), + }) + self.when_date_is_parsed(date_string) + self.then_date_was_parsed_by_date_parser() + self.then_date_obj_exactly_is(expected) + def given_local_tz_offset(self, offset): self.add_patch( patch.object(dateparser.timezone_parser,