Skip to content

Commit

Permalink
Consider the local tz when creating a datetime object from timestamp
Browse files Browse the repository at this point in the history
Otherwise datetime.fromtimestamp will return a naive datetime in the
local timezone and applying timezone modifications later with TIMEZONE
and TO_TIMEZONE won't do the right thing.

Parsing a unix timestamp value should always result in the exact same
instant in time regardless of current time zone, so it shouldn't matter
what the current TIMEZONE setting is, whether 'UTC', 'local', any other
timezone, or even unset.
  • Loading branch information
onlynone committed Jul 27, 2021
1 parent 60d8003 commit 3faf65f
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 12 deletions.
15 changes: 12 additions & 3 deletions dateparser/date.py
Expand Up @@ -4,6 +4,7 @@
from datetime import datetime, timedelta

import regex as re
from tzlocal import get_localzone
from dateutil.relativedelta import relativedelta

from dateparser.date_parser import date_parser
Expand All @@ -13,7 +14,8 @@
from dateparser.parser import _parse_absolute, _parse_nospaces
from dateparser.timezone_parser import pop_tz_offset_from_string
from dateparser.utils import apply_timezone_from_settings, \
set_correct_day_from_settings
set_correct_day_from_settings, \
get_timezone_from_tz_string

APOSTROPHE_LOOK_ALIKE_CHARS = [
'\N{RIGHT SINGLE QUOTATION MARK}', # '\u2019'
Expand Down Expand Up @@ -114,11 +116,18 @@ def sanitize_date(date_string):
def get_date_from_timestamp(date_string, settings):
match = RE_SEARCH_TIMESTAMP.search(date_string)
if match:
if settings is not None and settings.TIMEZONE is not None and 'local' not in settings.TIMEZONE.lower():
local_timezone = get_timezone_from_tz_string(settings.TIMEZONE)
else:
local_timezone = get_localzone()

seconds = int(match.group(1))
millis = int(match.group(2) or 0)
micros = int(match.group(3) or 0)
date_obj = datetime.fromtimestamp(seconds)
date_obj = date_obj.replace(microsecond=millis * 1000 + micros)
date_obj = (datetime
.fromtimestamp(seconds, local_timezone)
.replace(microsecond=millis * 1000 + micros, tzinfo=None)
)
date_obj = apply_timezone_from_settings(date_obj, settings)
return date_obj

Expand Down
19 changes: 10 additions & 9 deletions dateparser/utils/__init__.py
Expand Up @@ -65,22 +65,23 @@ def _get_missing_parts(fmt):
return missing


def localize_timezone(date_time, tz_string):
if date_time.tzinfo:
return date_time

tz = None

def get_timezone_from_tz_string(tz_string):
try:
tz = timezone(tz_string)
return timezone(tz_string)
except UnknownTimeZoneError as e:
for name, info in _tz_offsets:
if info['regex'].search(' %s' % tz_string):
tz = StaticTzInfo(name, info['offset'])
break
return StaticTzInfo(name, info['offset'])
else:
raise e


def localize_timezone(date_time, tz_string):
if date_time.tzinfo:
return date_time

tz = get_timezone_from_tz_string(tz_string)

if hasattr(tz, 'localize'):
date_time = tz.localize(date_time)
else:
Expand Down

0 comments on commit 3faf65f

Please sign in to comment.