Python: Figure out local timezone

前端 未结 18 1618
野性不改
野性不改 2020-12-07 15:50

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

>>&         


        
相关标签:
18条回答
  • 2020-12-07 16:02

    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.
    0 讨论(0)
  • 2020-12-07 16:03

    tzlocal from dateutil.

    Code example follows. Last string suitable for use in filenames.

    >>> from datetime import datetime
    >>> from dateutil.tz import tzlocal
    >>> str(datetime.now(tzlocal()))
    '2015-04-01 11:19:47.980883-07:00'
    >>> str(datetime.now(tzlocal())).replace(' ','-').replace(':','').replace('.','-')
    '2015-04-01-111947-981879-0700'
    >>> 
    
    0 讨论(0)
  • 2020-12-07 16:04

    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)
    
    0 讨论(0)
  • 2020-12-07 16:06

    Try dateutil, which has a tzlocal type that does what you need.

    0 讨论(0)
  • 2020-12-07 16:08

    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)
    
    0 讨论(0)
  • 2020-12-07 16:10

    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())
    
    0 讨论(0)
提交回复
热议问题