Turbo Turtle
Turbo Turtle

Reputation: 305

Convert timestamp string to Unix time with or without timezone in python2.7

I'm attempting to convert a given string to Unix time. The string always has a time in it and optionally has a date with it as well. For example, 12/31/15 11:59PM, 12/31/15 11:59, and 11:59 are strings that I can expect to get.

Using the following, any of those strings gets converted correctly:

from dateutil import parser 
import time

timezone = "12/31/15 11:59"
target = time.mktime(parser.parse(timestamp).timetuple())

However, if given a timezone as well, e.g. 12/31/15 11:59PM PST, the timezone gets dropped by timetuple() and while the conversion works, it will still give the same result as if the timezone weren't in the string (and thus is correct only for the local time of the system).

I haven't found an elegant way to 1) properly convert the string to the appropriate timezone when a timezone is given and at the same time 2) allow for the timezone string to be present, or if missing just assume local timezone.

Upvotes: 1

Views: 1087

Answers (1)

jfs
jfs

Reputation: 414795

The input time string may be ambiguous:

  • 11:59: the utc offset may depend on the date e.g., it may be different during the summer time. To disambiguate the date, you could pass default parameter to the .parse() method
  • PST may correspond to different timezones. To disambiguate the utc offset, you could pass tzinfos parameter

mktime() may fail on some systems or during a DST transition. tzlocal enables a portable solution:

#!/usr/bin/env python
from datetime import datetime
from dateutil.parser import parse  # $ pip install python-dateutil
from pytz import utc               # $ pip install pytz
from tzlocal import get_localzone  # $ pip install tzlocal

epoch = datetime(1970, 1, 1, tzinfo=utc)  # POSIX Epoch
default = datetime(2016, 1, 1)
tzinfos = dict(PST=-8 * 3600)
tz = get_localzone()  # local timezone
for time_string in ["12/31/15 11:59PM", "12/31/15 11:59PM PST", "12/31/15 11:59",
                    "11:59"]:
    dt = parse(time_string, default=default, tzinfos=tzinfos)
    if dt.tzinfo is None or dt.utcoffset() is None:  # naive
        dt = tz.normalize(tz.localize(dt))  # assume local timezone
    posix_timestamp = (dt - epoch).total_seconds()
    dt = datetime.fromtimestamp(posix_timestamp, dt.tzinfo)
    print("{dt} <- {posix_timestamp:.0f} <- {time_string}".format(**vars()))

Output

2015-12-31 23:59:00+01:00 <- 1451602740 <- 12/31/15 11:59PM
2015-12-31 23:59:00-08:00 <- 1451635140 <- 12/31/15 11:59PM PST
2015-12-31 11:59:00+01:00 <- 1451559540 <- 12/31/15 11:59
2016-01-01 11:59:00+01:00 <- 1451645940 <- 11:59

tz.localize() uses is_dst=False, to disambiguate time during DST transitions or for non-existent local times, see "Can I just always set is_dst=True?" section.

Upvotes: 1

Related Questions