Python: Figure out local timezone

2019-01-16 12:12发布

I want to compare UTC timestamps from a log file with local timestamps. When creating the local datetime object, I use something like:

>>> local_time=datetime.datetime(2010, 4, 27, 12, 0, 0, 0, 
                                 tzinfo=pytz.timezone('Israel'))

I want to find an automatic tool that would replace thetzinfo=pytz.timezone('Israel') with the current local time zone.

Any ideas?

14条回答
做个烂人
2楼-- · 2019-01-16 12:53

Based on Thoku's answer above, here's an answer that resolves the time zone to the nearest half hour (which is relevant for some timezones eg South Australia's) :

from datetime import datetime
round((round((datetime.now()-datetime.utcnow()).total_seconds())/1800)/2)
查看更多
甜甜的少女心
3楼-- · 2019-01-16 12:53

First, note that the question presents an incorrect initialization of an aware datetime object:

>>> local_time=datetime.datetime(2010, 4, 27, 12, 0, 0, 0,
...                                  tzinfo=pytz.timezone('Israel'))

creates an invalid instance. One can see the problem by computing the UTC offset of the resulting object:

>>> print(local_time.utcoffset())
2:21:00

(Note the result which is an odd fraction of an hour.)

To initialize an aware datetime properly using pytz one should use the localize() method as follows:

>>> local_time=pytz.timezone('Israel').localize(datetime.datetime(2010, 4, 27, 12))
>>> print(local_time.utcoffset())
3:00:00

Now, if you require a local pytz timezone as the new tzinfo, you should use the tzlocal package as others have explained, but if all you need is an instance with a correct local time zone offset and abbreviation then tarting with Python 3.3, you can call the astimezone() method with no arguments to convert an aware datetime instance to your local timezone:

>>> local_time.astimezone().strftime('%Y-%m-%d %H:%M %Z %z')
'2010-04-27 05:00 EDT -0400'
查看更多
家丑人穷心不美
4楼-- · 2019-01-16 12:58

Avoiding non-standard module (seems to be a missing method of datetime module):

from datetime import datetime
utcOffset_min = int(round((datetime.now() - datetime.utcnow()).total_seconds())) / 60   # round for taking time twice
utcOffset_h = utcOffset_min / 60
assert(utcOffset_min == utcOffset_h * 60)   # we do not handle 1/2 h timezone offsets

print 'Local time offset is %i h to UTC.' % (utcOffset_h)
查看更多
相关推荐>>
5楼-- · 2019-01-16 13:01

to compare UTC timestamps from a log file with local timestamps.

It is hard to find out Olson TZ name for a local timezone in a portable manner. Fortunately, you don't need it to perform the comparison.

tzlocal module returns a pytz timezone corresponding to the local timezone:

from datetime import datetime

import pytz # $ pip install pytz
from tzlocal import get_localzone # $ pip install tzlocal

tz = get_localzone()
local_dt = tz.localize(datetime(2010, 4, 27, 12, 0, 0, 0), is_dst=None)
utc_dt = local_dt.astimezone(pytz.utc) #NOTE: utc.normalize() is unnecessary here

Unlike other solutions presented so far the above code avoids the following issues:

  • local time can be ambiguous i.e., a precise comparison might be impossible for some local times
  • utc offset can be different for the same local timezone name for dates in the past. Some libraries that support timezone-aware datetime objects (e.g., dateutil) fail to take that into account

Note: to get timezone-aware datetime object from a naive datetime object, you should use*:

local_dt = tz.localize(datetime(2010, 4, 27, 12, 0, 0, 0), is_dst=None)

instead of:

#XXX fails for some timezones
local_dt = datetime(2010, 4, 27, 12, 0, 0, 0, tzinfo=tz)

*is_dst=None forces an exception if given local time is ambiguous or non-existent.

If you are certain that all local timestamps use the same (current) utc offset for the local timezone then you could perform the comparison using only stdlib:

# convert a naive datetime object that represents time in local timezone to epoch time
timestamp1 = (datetime(2010, 4, 27, 12, 0, 0, 0) - datetime.fromtimestamp(0)).total_seconds()

# convert a naive datetime object that represents time in UTC to epoch time
timestamp2 = (datetime(2010, 4, 27, 9, 0) - datetime.utcfromtimestamp(0)).total_seconds()

timestamp1 and timestamp2 can be compared directly.

Note:

  • timestamp1 formula works only if the UTC offset at epoch (datetime.fromtimestamp(0)) is the same as now
  • fromtimestamp() creates a naive datetime object in the current local timezone
  • utcfromtimestamp() creates a naive datetime object in UTC.
查看更多
成全新的幸福
6楼-- · 2019-01-16 13:01

Based on J. F. Sebastian's answer, you can do this with the standard library:

import time, datetime
local_timezone = datetime.timezone(datetime.timedelta(seconds=-time.timezone))

Tested in 3.4, should work on 3.4+

查看更多
SAY GOODBYE
7楼-- · 2019-01-16 13:05

For simple things, the following tzinfo implementation can be used, which queries the OS for time zone offsets:

import datetime
import time

class LocalTZ(datetime.tzinfo):
    _unixEpochOrdinal = datetime.datetime.utcfromtimestamp(0).toordinal()

    def dst(self, dt):
        return datetime.timedelta(0)

    def utcoffset(self, dt):
        t = (dt.toordinal() - self._unixEpochOrdinal)*86400 + dt.hour*3600 + dt.minute*60 + dt.second + time.timezone
        utc = datetime.datetime(*time.gmtime(t)[:6])
        local = datetime.datetime(*time.localtime(t)[:6])
        return local - utc


print datetime.datetime.now(LocalTZ())
print datetime.datetime(2010, 4, 27, 12, 0, 0, tzinfo=LocalTZ())

# If you're in the EU, the following datetimes are right on the DST change.
print datetime.datetime(2013, 3, 31, 0, 59, 59, tzinfo=LocalTZ())
print datetime.datetime(2013, 3, 31, 1, 0, 0, tzinfo=LocalTZ())
print datetime.datetime(2013, 3, 31, 1, 59, 59, tzinfo=LocalTZ())

# The following datetime is invalid, as the clock moves directly from
# 01:59:59 standard time to 03:00:00 daylight savings time.
print datetime.datetime(2013, 3, 31, 2, 0, 0, tzinfo=LocalTZ())

print datetime.datetime(2013, 10, 27, 0, 59, 59, tzinfo=LocalTZ())
print datetime.datetime(2013, 10, 27, 1, 0, 0, tzinfo=LocalTZ())
print datetime.datetime(2013, 10, 27, 1, 59, 59, tzinfo=LocalTZ())

# The following datetime is ambigous, as 02:00 can be either DST or standard
# time. (It is interpreted as standard time.)
print datetime.datetime(2013, 10, 27, 2, 0, 0, tzinfo=LocalTZ())
查看更多
登录 后发表回答