2

I wrote such a code to get timezone based on DST for an specific epoch time:

def getTimeZoneFromEpoch(epoch)
    if time.daylight and time.gmtime(epoch).tm_isdst==1:
        return -time.altzone/3600.0
    else:
        return -time.timezone/3600.0

But i'm not sure its correct, in fact at the moment i mistakes by 1 hour. Maybe i should swap altzone and timezone in this code, but its not what i understood from python's help (time module):

timezone -- difference in seconds between UTC and local standard time
altzone -- difference in  seconds between UTC and local DST time
tm_isdst
    1 if summer time is in effect, 0 if not, and -1 if unknown

Have i misundestood something?

saeedgnu
  • 4,110
  • 2
  • 31
  • 48

3 Answers3

4

I've tested this code to obtain the VM's locale UTC offset. Which, by the way, is only really valid at the moment it is measured. I'm not sure whether your code is equivalent or not.

def local_ephemeral_UTC_offset(epoch_time=None):
  u"Returns a datetime.timedelta object representing the local time offset from UTC at the moment"
  if epoch_time == None:
    epoch_time = time()
  return datetime.fromtimestamp(epoch_time) - datetime.utcfromtimestamp(epoch_time)
wberry
  • 18,519
  • 8
  • 53
  • 85
  • Thanks, This also worked correctly, with some pythonic modifications. I replace the last line with `return (datetime.datetime.fromtimestamp(epoch) - datetime.datetime.utcfromtimestamp(epoch)).seconds/3600.0` – saeedgnu Aug 11 '11 at 05:11
  • 1
    @ilius: it should be `.total_seconds()` instead of `.seconds` – jfs Jun 28 '13 at 14:53
  • 1
    Also please note, "UTC offset" and "time zone" are completely different concepts. The above code may function the same way at any given time for two or more different locale time zones. – wberry Jun 28 '13 at 20:49
  • `fromtimestamp(epoch_time)` might fail for past dates if the timezone had different utc offset at the time on systems without a historical timezone db (used by Python) e.g., Windows with Europe/Moscow timezone for dates before 2010. `pytz`-based solution could used instead. See [Python: Figure out local timezone](http://stackoverflow.com/a/17363006/4279). – jfs Feb 22 '14 at 08:37
  • Well, because the native Unix locales are not as "pure" as the TZ database, returning the "wrong" offset for past epoch times may actually be desired, if the offset is used to correct local times to UTC. I think it will depend on the application. – wberry Mar 02 '14 at 22:51
  • wrong offset means wrong offset. I've mentioned it only because of Windows. Most [*nix systems use Olson timezone database](http://www.iana.org/time-zones/repository/tz-link.html) i.e., `fromtimestamp()` may do the right thing already. Also, use `@J.F.` if you want me to be notified about your comment. – jfs Apr 16 '14 at 23:31
4

In short, use time.localtime() instead of time.gmtime().


The problem is that you use gmtime() , as the result of the following program shows.

from time import *

def getTimeZoneFromEpoch(epoch):
    if daylight and gmtime(epoch).tm_isdst==1:
        return -altzone/3600.0
    else:
        return -timezone/3600.0

print "                               tm_isdst of     tm_isdst of   time zone's\n" + \
      '                    epoch     gmtime(epoch)  localtime(epoch)  offset'
for d in ('13/03/2011', # DST start date in USA
          '14/03/2011',
          '',
          '06/11/2011', # DST end date in USA
          '07/11/2011',
          '',
          '27/03/2011', # DST start date in Europe
          '28/03/2011',
          '',
          '30/10/2011', # DST end date in Europe
          '31/10/2011'):
    if d:
        ds = strptime(d,'%d/%m/%Y')
        epoch = mktime(ds)
        lt = localtime(epoch)
        gt = gmtime(epoch)
        print '%s  %s  %12s %11s  %7s  %17s' % (d,ds.tm_isdst,epoch,gt.tm_isdst,lt.tm_isdst,getTimeZoneFromEpoch(epoch))
    else:
        print

With my clock set to the "UTC-07:00 Rocky Mountains" time zone, where the DST starts on March 13th 2011 and ends on November 06th 2011 , the result is:

                               tm_isdst of     tm_isdst of   time zone's
                    epoch     gmtime(epoch)  localtime(epoch)  offset
13/03/2011  -1  1299999600.0           0        0               -7.0
14/03/2011  -1  1300082400.0           0        1               -7.0

06/11/2011  -1  1320559200.0           0        1               -7.0
07/11/2011  -1  1320649200.0           0        0               -7.0

27/03/2011  -1  1301205600.0           0        1               -7.0
28/03/2011  -1  1301292000.0           0        1               -7.0

30/10/2011  -1  1319954400.0           0        1               -7.0
31/10/2011  -1  1320040800.0           0        1               -7.0

With my clock set to the "UTC+01:00 West Continental Europe" time zone, where the DST starts on March 27th 2011 and ends on October 30th 2011 , the result is:

                               tm_isdst of     tm_isdst of   time zone's
                    epoch     gmtime(epoch)  localtime(epoch)  offset
13/03/2011  -1  1299970800.0           0        0                1.0
14/03/2011  -1  1300057200.0           0        0                1.0

06/11/2011  -1  1320534000.0           0        0                1.0
07/11/2011  -1  1320620400.0           0        0                1.0

27/03/2011  -1  1301180400.0           0        0                1.0
28/03/2011  -1  1301263200.0           0        1                1.0

30/10/2011  -1  1319925600.0           0        1                1.0
31/10/2011  -1  1320015600.0           0        0                1.0
jfs
  • 399,953
  • 195
  • 994
  • 1,670
eyquem
  • 26,771
  • 7
  • 38
  • 46
  • As far as i know, epoch time is based on GMT. no? – saeedgnu Aug 11 '11 at 04:42
  • And what should I write instead of that function? – saeedgnu Aug 11 '11 at 04:45
  • Oh, yes replacing gmtime with localtime makes it correct (as I calculated for my location). Thanks – saeedgnu Aug 11 '11 at 05:09
  • 1
    note: calculating UTC offset [using `timezone` and `altzone` may fail in some cases where `fromtimestamp()` continues to work](http://www.selenic.com/pipermail/mercurial-devel/2011-November/035657.html). – jfs Apr 16 '14 at 23:31
1

Thanks for the help, besides the two methods that you suggested, I also found a more flexible (and maybe more compatible) version that can also take timezone object (or just use local zone) and return UTC offset

There was just this AmbiguousTimeError part that confused me, but I did something about it to make it (kind of) working in all cases.

from datetime import datetime
import pytz
from tzlocal import get_localzone

def getUtcOffsetByEpoch(epoch, tz=None):
    if not tz:
        tz = get_localzone()
    delta = 0
    while True:
        try:
            return tz.utcoffset(datetime.fromtimestamp(epoch + delta)).total_seconds()
        except pytz.exceptions.AmbiguousTimeError:## FIXME
            #d = datetime.fromtimestamp(epoch+3600)
            #print('AmbiguousTimeError', d.year, d.month, d.day, d.hour, d.minute, d.second)
            delta += 3600
            print('delta = %s'%delta)
        except (
            ValueError,
            OverflowError,
        ):
            return tz._utcoffset.total_seconds()
saeedgnu
  • 4,110
  • 2
  • 31
  • 48