From: Rob Browning Date: Sat, 28 Mar 2015 20:09:47 +0000 (-0500) Subject: Get TZ offset from C localtime, given tm_gmtoff X-Git-Tag: 0.27-rc4^0 X-Git-Url: https://arthur.barton.de/gitweb/?a=commitdiff_plain;h=6da6b774b03aa0e2629931788b901ca9f5e5eb0a;p=bup.git Get TZ offset from C localtime, given tm_gmtoff If we detect that struct tm contains tm_gmtoff, use the system localtime() to compute timezone offsets. This may help fix problems on platforms where Python strftime "%z" doesn't report accurate timzeone information. Thanks to Patrick Rouleau for reporting just such a problem on Cygwin. Signed-off-by: Rob Browning Tested-by: Rob Browning --- diff --git a/config/configure b/config/configure index d6156d2..ccc02b2 100755 --- a/config/configure +++ b/config/configure @@ -74,4 +74,6 @@ AC_CHECK_FIELD stat st_atimensec sys/types.h sys/stat.h unistd.h AC_CHECK_FIELD stat st_mtimensec sys/types.h sys/stat.h unistd.h AC_CHECK_FIELD stat st_ctimensec sys/types.h sys/stat.h unistd.h +AC_CHECK_FIELD tm tm_gmtoff time.h + AC_OUTPUT config.vars diff --git a/lib/bup/_helpers.c b/lib/bup/_helpers.c index 59f0fa3..caa5c02 100644 --- a/lib/bup/_helpers.c +++ b/lib/bup/_helpers.c @@ -33,6 +33,10 @@ #include #endif +#ifdef HAVE_TM_TM_GMTOFF +#include +#endif + #include "bupsplit.h" #if defined(FS_IOC_GETFLAGS) && defined(FS_IOC_SETFLAGS) @@ -1297,6 +1301,30 @@ static PyObject *bup_fstat(PyObject *self, PyObject *args) } +#ifdef HAVE_TM_TM_GMTOFF +static PyObject *bup_localtime(PyObject *self, PyObject *args) +{ + long long lltime; + time_t ttime; + if (!PyArg_ParseTuple(args, "L", &lltime)) + return NULL; + if (!INTEGRAL_ASSIGNMENT_FITS(&ttime, lltime)) + return PyErr_Format(PyExc_OverflowError, "time value too large"); + + struct tm tm; + if(localtime_r(&ttime, &tm) == NULL) + return PyErr_SetFromErrno(PyExc_OSError); + + // Match the Python struct_time values. + return Py_BuildValue("[i,i,i,i,i,i,i,i,i,i,s]", + 1900 + tm.tm_year, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_wday, tm.tm_yday + 1, + tm.tm_isdst, tm.tm_gmtoff, tm.tm_zone); +} +#endif /* def HAVE_TM_TM_GMTOFF */ + + static PyMethodDef helper_methods[] = { { "write_sparsely", bup_write_sparsely, METH_VARARGS, "Write buf excepting zeros at the end. Return trailing zero count." }, @@ -1355,6 +1383,10 @@ static PyMethodDef helper_methods[] = { "Extended version of lstat." }, { "fstat", bup_fstat, METH_VARARGS, "Extended version of fstat." }, +#ifdef HAVE_TM_TM_GMTOFF + { "localtime", bup_localtime, METH_VARARGS, + "Return struct_time elements plus the timezone offset and name." }, +#endif { NULL, NULL, 0, NULL }, // sentinel }; diff --git a/lib/bup/git.py b/lib/bup/git.py index afd1b36..2fc155f 100644 --- a/lib/bup/git.py +++ b/lib/bup/git.py @@ -761,7 +761,7 @@ class PackWriter: def _git_date(date): - return '%d %s' % (date, time.strftime('%z', time.localtime(date))) + return '%d %s' % (date, utc_offset_str(date)) def _gitenv(repo_dir = None): diff --git a/lib/bup/helpers.py b/lib/bup/helpers.py index c15b357..bcaec1b 100644 --- a/lib/bup/helpers.py +++ b/lib/bup/helpers.py @@ -1,14 +1,13 @@ """Helper functions and classes for bup.""" +from collections import namedtuple from ctypes import sizeof, c_void_p from os import environ from contextlib import contextmanager import sys, os, pwd, subprocess, errno, socket, select, mmap, stat, re, struct -import hashlib, heapq, operator, time, grp, tempfile +import hashlib, heapq, math, operator, time, grp, tempfile from bup import _helpers -import bup._helpers as _helpers -import math # This function should really be in helpers, not in bup.options. But we # want options.py to be standalone so people can include it in other projects. @@ -987,4 +986,37 @@ def grafted_path_components(graft_points, path): return result return path_components(clean_path) + Sha1 = hashlib.sha1 + + +_localtime = getattr(_helpers, 'localtime', None) + +if _localtime: + bup_time = namedtuple('bup_time', ['tm_year', 'tm_mon', 'tm_mday', + 'tm_hour', 'tm_min', 'tm_sec', + 'tm_wday', 'tm_yday', + 'tm_isdst', 'tm_gmtoff', 'tm_zone']) + +# Define a localtime() that returns bup_time when possible. Note: +# this means that any helpers.localtime() results may need to be +# passed through to_py_time() before being passed to python's time +# module, which doesn't appear willing to ignore the extra items. +if _localtime: + def localtime(time): + return bup_time(*_helpers.localtime(time)) + def utc_offset_str(t): + 'Return the local offset from UTC as "+hhmm" or "-hhmm" for time t.' + off = localtime(t).tm_gmtoff + hrs = off / 60 / 60 + return "%+03d%02d" % (hrs, abs(off - (hrs * 60 * 60))) + def to_py_time(x): + if isinstance(x, time.struct_time): + return x + return time.struct_time(x[:9]) +else: + localtime = time.localtime + def utc_offset_str(t): + return time.strftime('%z', localtime(t)) + def to_py_time(x): + return x