From: Leif Ryge Date: Mon, 4 Jan 2016 16:00:59 +0000 (+0000) Subject: wui: use standard time format (#1077) X-Git-Url: https://git.rkrishnan.org/specifications/%5B/%5D%20/nxhtml.html?a=commitdiff_plain;h=6226f6b497028889859dfcff56d4dbd7b65b8c2c;p=tahoe-lafs%2Ftahoe-lafs.git wui: use standard time format (#1077) --- diff --git a/src/allmydata/test/test_util.py b/src/allmydata/test/test_util.py index db64bf19..6335d6f6 100644 --- a/src/allmydata/test/test_util.py +++ b/src/allmydata/test/test_util.py @@ -1006,6 +1006,21 @@ class TimeFormat(unittest.TestCase): def test_parse_date(self): self.failUnlessEqual(time_format.parse_date("2010-02-21"), 1266710400) + def test_format_time(self): + self.failUnlessEqual(time_format.format_time(time.gmtime(0)), '1970-01-01 00:00:00') + self.failUnlessEqual(time_format.format_time(time.gmtime(60)), '1970-01-01 00:01:00') + self.failUnlessEqual(time_format.format_time(time.gmtime(60*60)), '1970-01-01 01:00:00') + seconds_per_day = 60*60*24 + leap_years_1970_to_2014_inclusive = ((2012 - 1968) // 4) + self.failUnlessEqual(time_format.format_time(time.gmtime(seconds_per_day*((2015 - 1970)*365+leap_years_1970_to_2014_inclusive))), '2015-01-01 00:00:00') + + def test_format_time_y2038(self): + seconds_per_day = 60*60*24 + leap_years_1970_to_2047_inclusive = ((2044 - 1968) // 4) + self.failUnlessEqual(time_format.format_time(time.gmtime(seconds_per_day*((2048 - 1970)*365+leap_years_1970_to_2047_inclusive))), '2048-01-01 00:00:00') + + test_format_time_y2038.todo = "one day we'll move beyond 32-bit time" + class CacheDir(unittest.TestCase): def test_basic(self): basedir = "test_util/CacheDir/test_basic" diff --git a/src/allmydata/util/time_format.py b/src/allmydata/util/time_format.py index 0f8f2f38..c481adc3 100644 --- a/src/allmydata/util/time_format.py +++ b/src/allmydata/util/time_format.py @@ -3,6 +3,9 @@ import calendar, datetime, re, time +def format_time(t): + return time.strftime("%Y-%m-%d %H:%M:%S", t) + def iso_utc_date(now=None, t=time.time): if now is None: now = t() @@ -13,11 +16,6 @@ def iso_utc(now=None, sep='_', t=time.time): now = t() return datetime.datetime.utcfromtimestamp(now).isoformat(sep) -def iso_local(now=None, sep='_', t=time.time): - if now is None: - now = t() - return datetime.datetime.fromtimestamp(now).isoformat(sep) - def iso_utc_time_to_seconds(isotime, _conversion_re=re.compile(r"(?P\d{4})-(?P\d{2})-(?P\d{2})[T_ ](?P\d{2}):(?P\d{2}):(?P\d{2})(?P\.\d+)?")): """ The inverse of iso_utc(). diff --git a/src/allmydata/web/common.py b/src/allmydata/web/common.py index 52fed6a0..b8e6d5b4 100644 --- a/src/allmydata/web/common.py +++ b/src/allmydata/web/common.py @@ -1,5 +1,7 @@ +import time import simplejson + from twisted.web import http, server from twisted.python import log from zope.interface import Interface @@ -13,11 +15,10 @@ from allmydata.interfaces import ExistingChildError, NoSuchChildError, \ MustBeReadonlyError, MustNotBeUnknownRWError, SDMF_VERSION, MDMF_VERSION from allmydata.mutable.common import UnrecoverableFileError from allmydata.util import abbreviate +from allmydata.util.time_format import format_time from allmydata.util.encodingutil import to_str, quote_output -TIME_FORMAT = "%H:%M:%S %d-%b-%Y" - def get_filenode_metadata(filenode): metadata = {'mutable': filenode.is_mutable()} if metadata['mutable']: @@ -209,6 +210,12 @@ def text_plain(text, ctx): req.setHeader("content-length", b"%d" % len(text)) return text +def spaces_to_nbsp(text): + return unicode(text).replace(u' ', u'\u00A0') + +def render_time(t): + return spaces_to_nbsp(format_time(time.localtime(t))) + class WebError(Exception): def __init__(self, text, code=http.BAD_REQUEST): self.text = text diff --git a/src/allmydata/web/directory.py b/src/allmydata/web/directory.py index d82bf643..29b57620 100644 --- a/src/allmydata/web/directory.py +++ b/src/allmydata/web/directory.py @@ -12,7 +12,7 @@ from nevow.inevow import IRequest from foolscap.api import fireEventually -from allmydata.util import base32, time_format +from allmydata.util import base32 from allmydata.util.encodingutil import to_str from allmydata.uri import from_string_dirnode from allmydata.interfaces import IDirectoryNode, IFileNode, IFilesystemNode, \ @@ -26,7 +26,7 @@ from allmydata.web.common import text_plain, WebError, \ boolean_of_arg, get_arg, get_root, parse_replace_arg, \ should_create_intermediate_directories, \ getxmlfile, RenderMixin, humanize_failure, convert_children_json, \ - get_format, get_mutable_type, get_filenode_metadata + get_format, get_mutable_type, get_filenode_metadata, render_time from allmydata.web.filenode import ReplaceMeMixin, \ FileNodeHandler, PlaceHolderNodeHandler from allmydata.web.check_results import CheckResultsRenderer, \ @@ -702,21 +702,21 @@ class DirectoryAsHTML(rend.Page): times = [] linkcrtime = metadata.get('tahoe', {}).get("linkcrtime") if linkcrtime is not None: - times.append("lcr: " + time_format.iso_local(linkcrtime)) + times.append("lcr: " + render_time(linkcrtime)) else: # For backwards-compatibility with links last modified by Tahoe < 1.4.0: if "ctime" in metadata: - ctime = time_format.iso_local(metadata["ctime"]) + ctime = render_time(metadata["ctime"]) times.append("c: " + ctime) linkmotime = metadata.get('tahoe', {}).get("linkmotime") if linkmotime is not None: if times: times.append(T.br()) - times.append("lmo: " + time_format.iso_local(linkmotime)) + times.append("lmo: " + render_time(linkmotime)) else: # For backwards-compatibility with links last modified by Tahoe < 1.4.0: if "mtime" in metadata: - mtime = time_format.iso_local(metadata["mtime"]) + mtime = render_time(metadata["mtime"]) if times: times.append(T.br()) times.append("m: " + mtime) diff --git a/src/allmydata/web/introweb.py b/src/allmydata/web/introweb.py index 6287af63..2cfe6a55 100644 --- a/src/allmydata/web/introweb.py +++ b/src/allmydata/web/introweb.py @@ -7,7 +7,7 @@ import allmydata import simplejson from allmydata import get_package_versions_string from allmydata.util import idlib -from allmydata.web.common import getxmlfile, get_arg, TIME_FORMAT +from allmydata.web.common import getxmlfile, get_arg, render_time class IntroducerRoot(rend.Page): @@ -53,7 +53,7 @@ class IntroducerRoot(rend.Page): # FIXME: This code is duplicated in root.py and introweb.py. def data_rendered_at(self, ctx, data): - return time.strftime(TIME_FORMAT, time.localtime()) + return render_time(time.time()) def data_version(self, ctx, data): return get_package_versions_string() def data_import_path(self, ctx, data): @@ -92,7 +92,7 @@ class IntroducerRoot(rend.Page): ctx.fillSlots("connection-hints", "connection hints: " + " ".join(ad.connection_hints)) ctx.fillSlots("connected", "?") - when_s = time.strftime("%H:%M:%S %d-%b-%Y", time.localtime(ad.when)) + when_s = render_time(ad.when) ctx.fillSlots("announced", when_s) ctx.fillSlots("version", ad.version) ctx.fillSlots("service_name", ad.service_name) @@ -105,7 +105,7 @@ class IntroducerRoot(rend.Page): ctx.fillSlots("nickname", s.nickname) ctx.fillSlots("tubid", s.tubid) ctx.fillSlots("connected", s.remote_address) - since_s = time.strftime("%H:%M:%S %d-%b-%Y", time.localtime(s.when)) + since_s = render_time(s.when) ctx.fillSlots("since", since_s) ctx.fillSlots("version", s.version) ctx.fillSlots("service_name", s.service_name) diff --git a/src/allmydata/web/root.py b/src/allmydata/web/root.py index 8a9969c6..c46a6dd5 100644 --- a/src/allmydata/web/root.py +++ b/src/allmydata/web/root.py @@ -14,7 +14,7 @@ from allmydata.interfaces import IFileNode from allmydata.web import filenode, directory, unlinked, status, operations from allmydata.web import storage from allmydata.web.common import abbreviate_size, getxmlfile, WebError, \ - get_arg, RenderMixin, get_format, get_mutable_type, TIME_FORMAT + get_arg, RenderMixin, get_format, get_mutable_type, render_time class URIHandler(RenderMixin, rend.Page): @@ -171,7 +171,7 @@ class Root(rend.Page): # FIXME: This code is duplicated in root.py and introweb.py. def data_rendered_at(self, ctx, data): - return time.strftime(TIME_FORMAT, time.localtime()) + return render_time(time.time()) def data_version(self, ctx, data): return get_package_versions_string() def data_import_path(self, ctx, data): @@ -309,10 +309,8 @@ class Root(rend.Page): ctx.fillSlots("connected", connected) ctx.fillSlots("connected_alt", self._connectedalts[connected]) ctx.fillSlots("connected-bool", bool(rhost)) - ctx.fillSlots("since", time.strftime(TIME_FORMAT, - time.localtime(since))) - ctx.fillSlots("announced", time.strftime(TIME_FORMAT, - time.localtime(announced))) + ctx.fillSlots("since", render_time(since)) + ctx.fillSlots("announced", render_time(announced)) ctx.fillSlots("version", version) ctx.fillSlots("service_name", service_name) ctx.fillSlots("available_space", available_space) diff --git a/src/allmydata/web/status.py b/src/allmydata/web/status.py index b363f857..7ab3efe2 100644 --- a/src/allmydata/web/status.py +++ b/src/allmydata/web/status.py @@ -5,7 +5,7 @@ from twisted.internet import defer from nevow import rend, inevow, tags as T from allmydata.util import base32, idlib from allmydata.web.common import getxmlfile, get_arg, \ - abbreviate_time, abbreviate_rate, abbreviate_size, plural, compute_rate + abbreviate_time, abbreviate_rate, abbreviate_size, plural, compute_rate, render_time from allmydata.interfaces import IUploadStatus, IDownloadStatus, \ IPublishStatus, IRetrieveStatus, IServermapUpdaterStatus @@ -162,9 +162,7 @@ class UploadStatusPage(UploadResultsRendererMixin, rend.Page): return d def render_started(self, ctx, data): - TIME_FORMAT = "%H:%M:%S %d-%b-%Y" - started_s = time.strftime(TIME_FORMAT, - time.localtime(data.get_started())) + started_s = render_time(data.get_started()) return started_s def render_si(self, ctx, data): @@ -614,9 +612,7 @@ class DownloadStatusPage(DownloadResultsRendererMixin, rend.Page): return d def render_started(self, ctx, data): - TIME_FORMAT = "%H:%M:%S %d-%b-%Y" - started_s = time.strftime(TIME_FORMAT, - time.localtime(data.get_started())) + started_s = render_time(data.get_started()) return started_s + " (%s)" % data.get_started() def render_si(self, ctx, data): @@ -647,9 +643,7 @@ class DownloadStatusTimelinePage(rend.Page): docFactory = getxmlfile("download-status-timeline.xhtml") def render_started(self, ctx, data): - TIME_FORMAT = "%H:%M:%S %d-%b-%Y" - started_s = time.strftime(TIME_FORMAT, - time.localtime(data.get_started())) + started_s = render_time(data.get_started()) return started_s + " (%s)" % data.get_started() def render_si(self, ctx, data): @@ -684,9 +678,7 @@ class RetrieveStatusPage(rend.Page, RateAndTimeMixin): self.retrieve_status = data def render_started(self, ctx, data): - TIME_FORMAT = "%H:%M:%S %d-%b-%Y" - started_s = time.strftime(TIME_FORMAT, - time.localtime(data.get_started())) + started_s = render_time(data.get_started()) return started_s def render_si(self, ctx, data): @@ -772,9 +764,7 @@ class PublishStatusPage(rend.Page, RateAndTimeMixin): self.publish_status = data def render_started(self, ctx, data): - TIME_FORMAT = "%H:%M:%S %d-%b-%Y" - started_s = time.strftime(TIME_FORMAT, - time.localtime(data.get_started())) + started_s = render_time(data.get_started()) return started_s def render_si(self, ctx, data): @@ -883,18 +873,14 @@ class MapupdateStatusPage(rend.Page, RateAndTimeMixin): self.update_status = data def render_started(self, ctx, data): - TIME_FORMAT = "%H:%M:%S %d-%b-%Y" - started_s = time.strftime(TIME_FORMAT, - time.localtime(data.get_started())) + started_s = render_time(data.get_started()) return started_s def render_finished(self, ctx, data): when = data.get_finished() if not when: return "not yet" - TIME_FORMAT = "%H:%M:%S %d-%b-%Y" - started_s = time.strftime(TIME_FORMAT, - time.localtime(data.get_finished())) + started_s = render_time(data.get_finished()) return started_s def render_si(self, ctx, data): @@ -1110,9 +1096,7 @@ class Status(rend.Page): def render_row(self, ctx, data): s = data - TIME_FORMAT = "%H:%M:%S %d-%b-%Y" - started_s = time.strftime(TIME_FORMAT, - time.localtime(s.get_started())) + started_s = render_time(s.get_started()) ctx.fillSlots("started", started_s) si_s = base32.b2a_or_none(s.get_storage_index())