diff --git a/pkgs/python-packages.nix b/pkgs/python-packages.nix --- a/pkgs/python-packages.nix +++ b/pkgs/python-packages.nix @@ -1602,13 +1602,13 @@ }; }; pytz = super.buildPythonPackage { - name = "pytz-2017.3"; + name = "pytz-2018.3"; buildInputs = with self; []; doCheck = false; propagatedBuildInputs = with self; []; src = fetchurl { - url = "https://pypi.python.org/packages/60/88/d3152c234da4b2a1f7a989f89609ea488225eaea015bc16fbde2b3fdfefa/pytz-2017.3.zip"; - md5 = "7006b56c0d68a162d9fe57d4249c3171"; + url = "https://pypi.python.org/packages/1b/50/4cdc62fc0753595fc16c8f722a89740f487c6e5670c644eb8983946777be/pytz-2018.3.tar.gz"; + md5 = "abb07c09c79f78d7c04f222a550c99ef"; }; meta = { license = [ pkgs.lib.licenses.mit ]; @@ -1683,7 +1683,7 @@ name = "rhodecode-enterprise-ce-4.12.0"; buildInputs = with self; [pytest py pytest-cov pytest-sugar pytest-runner pytest-catchlog pytest-profiling gprof2dot pytest-timeout mock WebTest cov-core coverage configobj]; doCheck = true; - propagatedBuildInputs = with self; [setuptools-scm amqp authomatic Babel Beaker celery Chameleon channelstream click colander configobj cssselect decorator deform docutils dogpile.cache dogpile.core ecdsa FormEncode future futures gnureadline infrae.cache iso8601 itsdangerous Jinja2 billiard kombu lxml Mako Markdown MarkupSafe msgpack-python MySQL-python objgraph packaging Paste PasteDeploy PasteScript pathlib2 peppercorn psutil psycopg2 py-bcrypt pycrypto pycurl pyflakes pygments-markdown-lexer Pygments pyparsing pyramid-beaker pyramid-debugtoolbar pyramid-jinja2 pyramid-mako pyramid pysqlite python-dateutil python-ldap python-memcached python-pam pytz pyzmq py-gfm recaptcha-client redis repoze.lru requests Routes setproctitle simplejson six SQLAlchemy sshpubkeys subprocess32 supervisor Tempita translationstring trollius urllib3 URLObject venusian WebError WebHelpers2 WebHelpers WebOb Whoosh wsgiref zope.cachedescriptors zope.deprecation zope.event zope.interface nbconvert bleach nbformat jupyter-client alembic invoke bumpversion transifex-client gevent greenlet gunicorn waitress uWSGI ipdb ipython CProfileV bottle rhodecode-tools appenlight-client pytest py pytest-cov pytest-sugar pytest-runner pytest-catchlog pytest-profiling gprof2dot pytest-timeout mock WebTest cov-core coverage]; + propagatedBuildInputs = with self; [setuptools-scm amqp authomatic Babel Beaker celery Chameleon channelstream click colander configobj cssselect decorator deform docutils dogpile.cache dogpile.core ecdsa FormEncode future futures gnureadline infrae.cache iso8601 itsdangerous Jinja2 billiard kombu lxml Mako Markdown MarkupSafe msgpack-python MySQL-python objgraph packaging Paste PasteDeploy PasteScript pathlib2 peppercorn psutil psycopg2 py-bcrypt pycrypto pycurl pyflakes pygments-markdown-lexer Pygments pyparsing pyramid-beaker pyramid-debugtoolbar pyramid-jinja2 pyramid-mako pyramid pysqlite python-dateutil python-ldap python-memcached python-pam pytz tzlocal pyzmq py-gfm recaptcha-client redis repoze.lru requests Routes setproctitle simplejson six SQLAlchemy sshpubkeys subprocess32 supervisor Tempita translationstring trollius urllib3 URLObject venusian WebError WebHelpers2 WebHelpers WebOb Whoosh wsgiref zope.cachedescriptors zope.deprecation zope.event zope.interface nbconvert bleach nbformat jupyter-client alembic invoke bumpversion transifex-client gevent greenlet gunicorn waitress uWSGI ipdb ipython CProfileV bottle rhodecode-tools appenlight-client pytest py pytest-cov pytest-sugar pytest-runner pytest-catchlog pytest-profiling gprof2dot pytest-timeout mock WebTest cov-core coverage]; src = ./.; meta = { license = [ { fullName = "Affero GNU General Public License v3 or later (AGPLv3+)"; } { fullName = "AGPLv3, and Commercial License"; } ]; @@ -1910,6 +1910,19 @@ license = [ pkgs.lib.licenses.asl20 ]; }; }; + tzlocal = super.buildPythonPackage { + name = "tzlocal-1.5.1"; + buildInputs = with self; []; + doCheck = false; + propagatedBuildInputs = with self; [pytz]; + src = fetchurl { + url = "https://pypi.python.org/packages/cb/89/e3687d3ed99bc882793f82634e9824e62499fdfdc4b1ae39e211c5b05017/tzlocal-1.5.1.tar.gz"; + md5 = "4553be891efa0812c4adfb0c6e818eec"; + }; + meta = { + license = [ pkgs.lib.licenses.mit ]; + }; + }; uWSGI = super.buildPythonPackage { name = "uWSGI-2.0.15"; buildInputs = with self; []; diff --git a/requirements.txt b/requirements.txt --- a/requirements.txt +++ b/requirements.txt @@ -61,7 +61,8 @@ python-dateutil python-ldap==2.4.45 python-memcached==1.58 python-pam==1.8.2 -pytz==2017.3 +pytz==2018.3 +tzlocal==1.5.1 pyzmq==14.6.0 py-gfm==0.1.3 recaptcha-client==1.0.6 diff --git a/rhodecode/lib/helpers.py b/rhodecode/lib/helpers.py --- a/rhodecode/lib/helpers.py +++ b/rhodecode/lib/helpers.py @@ -25,6 +25,7 @@ Consists of functions to typically be us available to Controllers. This module is available to both as 'h'. """ +import os import random import hashlib import StringIO @@ -742,18 +743,23 @@ short_id = lambda x: x[:12] hide_credentials = lambda x: ''.join(credentials_filter(x)) +import pytz +import tzlocal +local_timezone = tzlocal.get_localzone() + + def age_component(datetime_iso, value=None, time_is_local=False): title = value or format_date(datetime_iso) tzinfo = '+00:00' # detect if we have a timezone info, otherwise, add it - if isinstance(datetime_iso, datetime) and not datetime_iso.tzinfo: - if time_is_local: - tzinfo = time.strftime("+%H:%M", - time.gmtime( - (datetime.now() - datetime.utcnow()).seconds + 1 - ) - ) + if time_is_local and isinstance(datetime_iso, datetime) and not datetime_iso.tzinfo: + force_timezone = os.environ.get('RC_TIMEZONE', '') + if force_timezone: + force_timezone = pytz.timezone(force_timezone) + timezone = force_timezone or local_timezone + offset = timezone.localize(datetime_iso).strftime('%z') + tzinfo = '{}:{}'.format(offset[:-2], offset[-2:]) return literal( '