Alain Alain - 3 months ago 11
Python Question

Why is this code dependant on my local machine timezone?

Why does this code:

def parse_date(datetime_string, tz_code):
tz = timezone(tz_code)
datetime_obj = parser.parse(datetime_string)
datetime_obj_localized = datetime_obj.replace(tzinfo=tz)
return time.mktime(datetime_obj_localized.timetuple())

def test_parse_date(self):
self.assertEquals(1482951600, parse_date('2016-12-28 14:00', 'US/Eastern')

returns a different value depending on the timezone of the machine it is running on?

In my understanding, the parser returns a datetime without a timezone, it then assign a tz, without changing anything else and finally it is converted into a timestamp. My local tz should not be used anywhere.


dateutil.parser will attach a time zone if and only if it finds one, which could depend on your local machine time zone settings, because if it detects an abbreviated time zone like "EST" that is in the list of abbreviations for your local time zone, it will assume that that's the one you mean.

That said, this is not what is happening in this case. Even if parser were attaching a time zone, datetime.replace does a wholesale replacement of the time zone, not a conversion. I do not think there is enough information in your question to fully diagnose this issue, but if timezone() is pytz.timezone(), at least one of your issues is that pytz time zones can't be attached to datetime objects with replace. You would need to instead use datetime_obj = pytz.timezone(tz_code).localize(datetime_obj).

Note that the localize method assumes that you have a timezone-naive object, so it will throw an error if datetutil's parser returns a timezone-aware datetime, so you should pass ignoretz=True to the parser to prevent this.

That said, even once you do this, the real issue is in your use of time.mktime. See this answer, which is exactly the same problem. To summarize that answer, the best thing to do is to use calendar.timegm instead of mktime, and convert to UTC before calling it. Incorporating my suggestions, here is an updated version of your code:

import calendar
from import tzutc

def parse_date(datetime_string, tz_code):
    tz = timezone(tz_code)
    datetime_obj = parser.parse(datetime_string, ignoretz=True)
    datetime_obj_localized = tz.localize(datetime_obj)

    datetime_obj_utc = datetime_obj_localized.astimezone(tzutc())
    return calendar.timegm(datetime_obj_utc.timetuple())