1

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.

Alain
  • 1,251
  • 1
  • 14
  • 32

1 Answers1

2

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 dateutil.tz 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())
Paul
  • 10,381
  • 13
  • 48
  • 86
  • tried your revised code and still does not work if my local tz is not US/Eastern. You said: "You would need to instead use datetime_obj = pytz.timezone(tz_code).localize(datetime_obj)" but that is what I do on line #4... Alos, timezone, is from pytz, so I guess the issue is there. – Alain Oct 16 '16 at 14:22
  • 1
    @Alain No, on line 4 you are using `datetime_obj.replace(tz=pytz.timezone(tz_code))`. `pytz` does not support the standard time zone attachment interface. – Paul Oct 16 '16 at 14:24
  • @Alain I have updated my answer, I believe the real issue is that `mktime` ignores time zone. – Paul Oct 16 '16 at 14:29
  • Going to be afk for a few hours. I replaces mktime with int(datetime_obj_localized.strftime('%s')) and still does not work. – Alain Oct 16 '16 at 14:34
  • oups, havent tried the updated answer (SO page did not refresh). Going when I am back home. thx – Alain Oct 16 '16 at 14:35
  • It worked! Thanks, learned a bunch in things from your answer and the linked question. – Alain Oct 17 '16 at 01:10