diff --git a/.travis.yml b/.travis.yml index 17f7c36ed07c6df8f86b10c2a249af8e82c953d4..b72b430e9d9b739bb0f5b0a8cd900523da490d12 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,9 +30,6 @@ matrix: env: CHERRYPY=3.2 before_install: - # Enforce timezone - - echo 'Canada/Eastern' | sudo tee /etc/timezone - - sudo dpkg-reconfigure --frontend noninteractive tzdata # Install requirements - sudo apt-get install python-pysqlite2 rdiff-backup - sudo pip install tox-travis diff --git a/Jenkinsfile b/Jenkinsfile index d54838ac7f417aa058b7403c1c20d2e59dc66d73..7a790d1cf64028e67dce7a43ea3d73e36275ac5d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -23,8 +23,6 @@ for (z in axisCherrypy) { deleteDir() // Checkout checkout scm - echo 'Enforce timezone for tests to work.' - sh 'ln -snf /usr/share/zoneinfo/America/Montreal /etc/localtime && echo "America/Montreal" > /etc/timezone' echo 'Upgrade python and install dependencies to avoid compiling from sources.' sh 'apt-get update && apt-get -qq install python-pysqlite2 libldap2-dev libsasl2-dev rdiff-backup node-less' sh 'pip install pip setuptools tox --upgrade' diff --git a/rdiffweb/librdiff.py b/rdiffweb/librdiff.py index 77d94fe1ff64c47ea429d6e13ae9756620bc5f96..27d994b6cbdc686595eca979f511fd11a915977a 100755 --- a/rdiffweb/librdiff.py +++ b/rdiffweb/librdiff.py @@ -125,21 +125,18 @@ class RdiffTime(object): assert value is None or isinstance(value, int) or isinstance(value, str) if value is None: # Get GMT time. - self.timeInSeconds = int(time.time()) - self.tzOffset = tz_offset or 0 + self._time_seconds = int(time.time()) + self._tz_offset = 0 elif isinstance(value, int): - self.timeInSeconds = value - self.tzOffset = tz_offset or 0 + self._time_seconds = value + self._tz_offset = tz_offset or 0 + elif isinstance(RdiffTime, int): + self._time_seconds = value._time_seconds + self._tz_offset = value._tz_offset else: - self._initFromString(value) + self._from_str(value) - def initFromMidnightUTC(self, daysFromToday): - self.timeInSeconds = time.time() - self.timeInSeconds -= self.timeInSeconds % (24 * 60 * 60) - self.timeInSeconds += daysFromToday * 24 * 60 * 60 - self.tzOffset = 0 - - def _initFromString(self, timeString): + def _from_str(self, timeString): try: date, daytime = timeString[:19].split("T") year, month, day = list(map(int, date.split("-"))) @@ -152,54 +149,44 @@ class RdiffTime(object): assert 0 <= second <= 61 # leap seconds timetuple = (year, month, day, hour, minute, second, -1, -1, 0) - self.timeInSeconds = calendar.timegm(timetuple) - self.tzOffset = self._tzdtoseconds(timeString[19:]) - self.getTimeZoneString() # to get assertions there + self._time_seconds = calendar.timegm(timetuple) + self._tz_offset = self._tzdtoseconds(timeString[19:]) + self._tz_str() # to get assertions there except (TypeError, ValueError, AssertionError): raise ValueError(timeString) - def getLocalDaysSinceEpoch(self): - return self.getLocalSeconds() // (24 * 60 * 60) - - def getLocalSeconds(self): - return self.timeInSeconds + def get_local_day_since_epoch(self): + return self._time_seconds // (24 * 60 * 60) - def getSeconds(self): - return self.timeInSeconds - self.tzOffset - - def getDisplayString(self): - value = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(self.getLocalSeconds())) - if isinstance(value, bytes): - value = value.decode(encoding='latin1') - return value + def epoch(self): + return self._time_seconds - self._tz_offset - def getTimeZoneString(self): - if self.tzOffset: - tzinfo = self._getTimeZoneDisplayInfo() - return "%s%s:%s" % (tzinfo["plusMinus"], tzinfo["hours"], tzinfo["minutes"]) + def _tz_str(self): + if self._tz_offset: + hours, minutes = divmod(old_div(abs(self._tz_offset), 60), 60) + assert 0 <= hours <= 23 + assert 0 <= minutes <= 59 + if self._tz_offset > 0: + plusMinus = "+" + else: + plusMinus = "-" + return "%s%s:%s" % (plusMinus, "%02d" % hours, "%02d" % minutes) else: return "Z" - def setTime(self, hour, minute, second): - year = time.gmtime(self.timeInSeconds)[0] - month = time.gmtime(self.timeInSeconds)[1] - day = time.gmtime(self.timeInSeconds)[2] - self.timeInSeconds = calendar.timegm( + def set_time(self, hour, minute, second): + year = time.gmtime(self._time_seconds)[0] + month = time.gmtime(self._time_seconds)[1] + day = time.gmtime(self._time_seconds)[2] + self._time_seconds = calendar.timegm( (year, month, day, hour, minute, second, -1, -1, 0)) - def _getTimeZoneDisplayInfo(self): - hours, minutes = divmod(old_div(abs(self.tzOffset), 60), 60) - assert 0 <= hours <= 23 - assert 0 <= minutes <= 59 - - if self.tzOffset > 0: - plusMinus = "+" - else: - plusMinus = "-" - return {"plusMinus": plusMinus, - "hours": "%02d" % hours, - "minutes": "%02d" % minutes} + def strftime(self, dateformat): + value = time.strftime(dateformat, time.gmtime(self._time_seconds)) + if isinstance(value, bytes): + value = value.decode(encoding='latin1') + return value def _tzdtoseconds(self, tzd): """Given w3 compliant TZD, converts it to number of seconds from UTC""" @@ -218,60 +205,64 @@ class RdiffTime(object): def __add__(self, other): """Support plus (+) timedelta""" assert isinstance(other, timedelta) - return RdiffTime(self.timeInSeconds + int(other.total_seconds()), self.tzOffset) + return RdiffTime(self._time_seconds + int(other.total_seconds()), self._tz_offset) def __sub__(self, other): """Support minus (-) timedelta""" assert isinstance(other, timedelta) or isinstance(other, RdiffTime) # Sub with timedelta, return RdiffTime if isinstance(other, timedelta): - return RdiffTime(self.timeInSeconds - int(other.total_seconds()), self.tzOffset) + return RdiffTime(self._time_seconds - int(other.total_seconds()), self._tz_offset) # Sub with RdiffTime, return timedelta if isinstance(other, RdiffTime): - return timedelta(seconds=self.timeInSeconds - other.timeInSeconds) + return timedelta(seconds=self._time_seconds - other._time_seconds) def __int__(self): """Return this date as seconds since epoch.""" - return self.timeInSeconds + return self.epoch() def __lt__(self, other): assert isinstance(other, RdiffTime) - return self.getSeconds() < other.getSeconds() + return self.epoch() < other.epoch() def __le__(self, other): assert isinstance(other, RdiffTime) - return self.getSeconds() <= other.getSeconds() + return self.epoch() <= other.epoch() def __gt__(self, other): assert isinstance(other, RdiffTime) - return self.getSeconds() > other.getSeconds() + return self.epoch() > other.epoch() def __ge__(self, other): assert isinstance(other, RdiffTime) - return self.getSeconds() >= other.getSeconds() + return self.epoch() >= other.epoch() def __cmp__(self, other): assert isinstance(other, RdiffTime) - return cmp(self.getSeconds(), other.getSeconds()) + return cmp(self.epoch(), other.epoch()) def __eq__(self, other): return (isinstance(other, RdiffTime) and - self.getSeconds() == other.getSeconds()) + self.epoch() == other.epoch()) def __hash__(self): - return hash(self.getSeconds()) + return hash(self.epoch()) def __str__(self): """return utf-8 string""" - return self.getDisplayString() + value = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(self._time_seconds)) + if isinstance(value, bytes): + value = value.decode(encoding='latin1') + return value + self._tz_str() def __repr__(self): """return second since epoch""" - return str(self.getSeconds()) + return "RdiffTime('" + str(self) + "')" # Interfaced objects # + class DirEntry(object): """Includes name, isDir, fileSize, exists, and dict (changeDates) of sorted @@ -1014,7 +1005,7 @@ class RdiffRepo(object): # Convert the date into epoch. if isinstance(restore_date, RdiffTime): - restore_date = restore_date.getSeconds() + restore_date = restore_date.epoch() # Define a nice filename for the archive or file to be created. if path == b"": @@ -1176,6 +1167,7 @@ class RdiffRepo(object): return bytes([int(match.group()[1:])]) except: return match.group + # Remove quote using regex return re.sub(b";[0-9]{3}", unquoted_char, name, re.S) diff --git a/rdiffweb/page_status.py b/rdiffweb/page_status.py index 620062fcfe7074e8cf8211c502a94517f1a72f4e..ca98b8b2d4c3b67ca7d50004677e9e080e169a20 100644 --- a/rdiffweb/page_status.py +++ b/rdiffweb/page_status.py @@ -22,6 +22,7 @@ from __future__ import unicode_literals from builtins import bytes from builtins import str import cherrypy +from datetime import timedelta import logging from rdiffweb import librdiff @@ -29,7 +30,6 @@ from rdiffweb import page_main from rdiffweb import rdw_helpers from rdiffweb.rdw_helpers import unquote_url - # Define the logger logger = logging.getLogger(__name__) @@ -122,23 +122,19 @@ class StatusPage(page_main.MainPage): # Set the start and end time to be the start and end of the day, # respectively, to get all entries for that day - startTime = librdiff.RdiffTime() - startTime.timeInSeconds = date.timeInSeconds - startTime.tzOffset = date.tzOffset - startTime.setTime(0, 0, 0) + startTime = librdiff.RdiffTime(date) + startTime.set_time(0, 0, 0) - endTime = librdiff.RdiffTime() - endTime.timeInSeconds = date.timeInSeconds - endTime.tzOffset = date.tzOffset - endTime.setTime(23, 59, 59) + endTime = librdiff.RdiffTime(date) + endTime.set_time(23, 59, 59) return self._getUserMessages(userRepos, True, False, startTime, endTime) def _get_recent_user_messages(self, failuresOnly): user_repos = self.app.currentuser.repos - asOfDate = librdiff.RdiffTime() - asOfDate.initFromMidnightUTC(-5) + + asOfDate = librdiff.RdiffTime() - timedelta(days=5) return self._getUserMessages(user_repos, not failuresOnly, True, asOfDate, None) @@ -176,7 +172,7 @@ class StatusPage(page_main.MainPage): if successfulBackups: lastSuccessDate = successfulBackups[0]["date"] successfulBackups = rdw_helpers.groupby( - successfulBackups, lambda x: x["date"].getLocalDaysSinceEpoch()) + successfulBackups, lambda x: x["date"].get_local_day_since_epoch()) userMessages = [] diff --git a/rdiffweb/plugins/graphs/__init__.py b/rdiffweb/plugins/graphs/__init__.py index 9dce598c5c26d339ec149e173bfc6f673b58fe77..e2ac434a46d8f05748da6ad62eba85d69200eb8c 100644 --- a/rdiffweb/plugins/graphs/__init__.py +++ b/rdiffweb/plugins/graphs/__init__.py @@ -83,7 +83,7 @@ class GraphsPage(page_main.MainPage): yield '\n' # Content for d, s in iteritems(repo_obj.session_statistics): - yield str(d.getSeconds()) + yield str(d.epoch()) for attr in attrs: yield ',' yield str(getattr(s, attr)) diff --git a/rdiffweb/rdw_templating.py b/rdiffweb/rdw_templating.py index 224ed708d21dcded802e14d7d6f854548293678c..179a4f32ce3536eaf0e738298572e396c6cfb621 100755 --- a/rdiffweb/rdw_templating.py +++ b/rdiffweb/rdw_templating.py @@ -33,7 +33,6 @@ from rdiffweb import i18n from rdiffweb import librdiff from rdiffweb import rdw_helpers - # Define the logger logger = logging.getLogger(__name__) @@ -95,15 +94,10 @@ def do_filter(sequence, attribute_name): def do_format_datetime(value, dateformat='%Y-%m-%d %H:%M'): """Used to format an epoch into local time.""" - if not value: return "" - - if isinstance(value, librdiff.RdiffTime): - value = value.getSeconds() - - # TODO Try to figure out the time zone name (it's a ) - return time.strftime(dateformat, time.localtime(value)) + assert isinstance(value, librdiff.RdiffTime) + return value.strftime(dateformat) def do_format_filesize(value, binary=True): @@ -186,7 +180,7 @@ def url_for_restore(repo, path, date, kind=None): url.append(rdw_helpers.quote_url(path)) # Append date url.append("?date=") - url.append(str(date.getSeconds())) + url.append(str(date.epoch())) if kind: url.append("&kind=%s" % kind) return ''.join(url) @@ -213,7 +207,7 @@ def url_for_status_entry(date, repo=None): url.append("/") if date: url.append("?date=") - url.append(str(date.getSeconds())) + url.append(str(date.epoch())) return ''.join(url) diff --git a/rdiffweb/templates/browse.html b/rdiffweb/templates/browse.html index a241a115df36689d53fd4dd14d02d1b04d995a63..3f1b071e541fce32a1549b8f05ebbb13f2954088 100644 --- a/rdiffweb/templates/browse.html +++ b/rdiffweb/templates/browse.html @@ -47,7 +47,7 @@ {{ entry.file_size | filesize }} {% endif %} - + {% if entry.change_dates %}