diff --git a/dateparser/utils/__init__.py b/dateparser/utils/__init__.py index 70b2fae48..203ea34f1 100644 --- a/dateparser/utils/__init__.py +++ b/dateparser/utils/__init__.py @@ -121,22 +121,29 @@ def apply_timezone(date_time, tz_string): def apply_timezone_from_settings(date_obj, settings): - tz = get_localzone() if settings is None: return date_obj - if 'local' in settings.TIMEZONE.lower(): - if hasattr(tz, 'localize'): - date_obj = tz.localize(date_obj) + is_originally_aware = is_aware(date_obj) + + if not is_originally_aware: + if 'local' in settings.TIMEZONE.lower(): + tz = get_localzone() + if hasattr(tz, 'localize'): + date_obj = tz.localize(date_obj) + else: + date_obj = date_obj.replace(tzinfo=tz) else: - date_obj = date_obj.replace(tzinfo=tz) - else: - date_obj = localize_timezone(date_obj, settings.TIMEZONE) + date_obj = localize_timezone(date_obj, settings.TIMEZONE) if settings.TO_TIMEZONE: date_obj = apply_timezone(date_obj, settings.TO_TIMEZONE) - if settings.RETURN_AS_TIMEZONE_AWARE is not True: + if ( + not settings.RETURN_AS_TIMEZONE_AWARE + or (settings.RETURN_AS_TIMEZONE_AWARE + and 'default' == settings.RETURN_AS_TIMEZONE_AWARE and not is_originally_aware) + ): date_obj = date_obj.replace(tzinfo=None) return date_obj @@ -235,3 +242,15 @@ def setup_logging(): }, } logging.config.dictConfig(config) + + +def is_aware(date_obj): + """ + From Python docs (https://docs.python.org/3/library/datetime.html#determining-if-an-object-is-aware-or-naive): + + A datetime object d is aware if both of the following hold: + 1. d.tzinfo is not None + 2. d.tzinfo.utcoffset(d) does not return None + + """ + return date_obj.tzinfo is not None and date_obj.tzinfo.utcoffset(date_obj) is not None diff --git a/tests/test_date.py b/tests/test_date.py index 191c733d5..ed50fff05 100644 --- a/tests/test_date.py +++ b/tests/test_date.py @@ -3,7 +3,7 @@ import unittest from collections import OrderedDict from copy import copy -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from unittest.mock import Mock, patch from parameterized import parameterized, param @@ -12,6 +12,7 @@ from dateparser.date import DateData from dateparser import date from dateparser.conf import settings +from dateparser.timezone_parser import StaticTzInfo from tests import BaseTestCase @@ -258,6 +259,23 @@ def test_should_parse_date(self, date_string, date_formats, expected_result): self.then_parsed_period_is('day') self.then_parsed_date_is(expected_result) + @parameterized.expand([ + param(date_string='Fri Jan 26 16:32:21 +0000 2018', date_formats=['%a %b %d %H:%M:%S %z %Y'], + expected_result=datetime(2018, 1, 26, 16, 32, 21)), + param(date_string='15/04/1904 14:05 +0004', date_formats=['%d/%m/%Y %H:%M %z'], + expected_result=datetime(1904, 4, 15, 14, 5, tzinfo=timezone(timedelta(0, 240)))), + param( + date_string='20-05-1994 23:00 GMT', date_formats=['%d-%m-%Y %H:%M %Z'], expected_result=datetime( + 1994, 5, 20, 23, 0, tzinfo=StaticTzInfo('GMT', timedelta(seconds=0)) + ) + ), + ]) + def test_should_parse_localized_dates(self, date_string, date_formats, expected_result): + self.when_date_is_parsed_with_formats(date_string, date_formats) + self.then_date_was_parsed() + self.then_parsed_period_is('day') + self.then_parsed_date_is(expected_result) + @parameterized.expand([ param(date_string='09.16', date_formats=['%m.%d'], expected_month=9, expected_day=16), ])