# HG changeset patch # User Goffi # Date 1527847446 -7200 # Node ID 5b26033c49a86eb83a2f5d3fb012a155f7525aa3 # Parent 0b6adc2672d9ca2a64d1642e45c0f72baf554904 tools (common): moved date_fmt function from template filters to new date_utils module, so it can be used everywhere. diff -r 0b6adc2672d9 -r 5b26033c49a8 sat/core/constants.py --- a/sat/core/constants.py Fri Jun 01 12:02:09 2018 +0200 +++ b/sat/core/constants.py Fri Jun 01 12:04:06 2018 +0200 @@ -368,6 +368,10 @@ KEY_PROGRESS_ID = u'progress_id' + #internationalisation + DEFAULT_LOCALE = u'en_GB' + + ## Misc ## SAVEFILE_DATABASE = APP_NAME_FILE + ".db" IQ_SET = '/iq[@type="set"]' diff -r 0b6adc2672d9 -r 5b26033c49a8 sat/tools/common/date_utils.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sat/tools/common/date_utils.py Fri Jun 01 12:04:06 2018 +0200 @@ -0,0 +1,84 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# SAT: a jabber client +# Copyright (C) 2009-2018 Jérôme Poisson (goffi@goffi.org) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +"""tools to help manipulating time and dates""" + +from sat.core.constants import Const as C +import datetime +from babel import dates +import time + + +def date_fmt(timestamp, fmt='short', date_only=False, auto_limit=7, auto_old_fmt='short', auto_new_fmt='relative', locale_str=C.DEFAULT_LOCALE): + """format date according to locale + + @param timestamp(basestring, int): unix time + @param fmt(str): one of: + - short: e.g. u'31/12/17' + - medium: e.g. u'Apr 1, 2007' + - long: e.g. u'April 1, 2007' + - full: e.g. u'Sunday, April 1, 2007' + - relative: format in relative time + e.g.: 3 hours + note that this format is not precise + - iso: ISO 8601 format + e.g.: u'2007-04-01T19:53:23Z' + - auto: use auto_old_fmt if date is older than auto_limit + else use auto_new_fmt + - auto_day: shorcut to set auto format with change on day + old format will be short, and new format will be time only + or a free value which is passed to babel.dates.format_datetime + @param date_only(bool): if True, only display date (not datetime) + @param auto_limit (int): limit in days before using auto_old_fmt + use 0 to have a limit at last midnight (day change) + @param auto_old_fmt(unicode): format to use when date is older than limit + @param auto_new_fmt(unicode): format to use when date is equal to or more recent + than limit + + """ + if fmt == 'auto_day': + fmt, auto_limit, auto_old_fmt, auto_new_fmt = 'auto', 0, 'short', 'HH:mm' + if fmt == 'auto': + if auto_limit == 0: + today = time.mktime(datetime.date.today().timetuple()) + if int(timestamp) < today: + fmt = auto_old_fmt + else: + fmt = auto_new_fmt + else: + days_delta = (time.time() - int(timestamp)) / 3600 + if days_delta > (auto_limit or 7): + fmt = auto_old_fmt + else: + fmt = auto_new_fmt + + if fmt == 'relative': + delta = int(timestamp) - time.time() + return dates.format_timedelta(delta, granularity="minute", add_direction=True, locale=locale_str) + elif fmt in ('short', 'long'): + formatter = dates.format_date if date_only else dates.format_datetime + return formatter(int(timestamp), format=fmt, locale=locale_str) + elif fmt == 'iso': + if date_only: + fmt = 'yyyy-MM-dd' + else: + fmt = "yyyy-MM-ddTHH:mm:ss'Z'" + return dates.format_datetime(int(timestamp), format=fmt) + else: + return dates.format_datetime(int(timestamp), format=fmt, locale=locale_str) diff -r 0b6adc2672d9 -r 5b26033c49a8 sat/tools/common/template.py --- a/sat/tools/common/template.py Fri Jun 01 12:02:09 2018 +0200 +++ b/sat/tools/common/template.py Fri Jun 01 12:04:06 2018 +0200 @@ -22,17 +22,16 @@ from sat.core.constants import Const as C from sat.core.i18n import _ from sat.core import exceptions +from sat.tools.common import date_utils from sat.core.log import getLogger log = getLogger(__name__) import os.path from xml.sax.saxutils import quoteattr -import datetime import time import re from babel import support from babel import Locale from babel.core import UnknownLocaleError -from babel import dates import pygments from pygments import lexers from pygments import formatters @@ -53,7 +52,6 @@ from lxml import etree HTML_EXT = ('html', 'xhtml') -DEFAULT_LOCALE = u'en_GB' RE_ATTR_ESCAPE = re.compile(r'[^a-z_-]') # TODO: handle external path (an additional search path for templates should be settable by user # TODO: handle absolute URL (should be used for trusted use cases) only (e.g. jp) for security reason @@ -205,7 +203,7 @@ lstrip_blocks=True, extensions=['jinja2.ext.i18n'], ) - self._locale_str = DEFAULT_LOCALE + self._locale_str = C.DEFAULT_LOCALE self._locale = Locale.parse(self._locale_str) self.installTranslations() # we want to have access to SàT constants in templates @@ -258,22 +256,22 @@ locale = Locale.parse(locale_str) except ValueError as e: log.warning(_(u"invalid locale value: {msg}").format(msg=e)) - locale_str = self._locale_str = DEFAULT_LOCALE + locale_str = self._locale_str = C.DEFAULT_LOCALE locale = Locale.parse(locale_str) locale_str = unicode(locale) - if locale_str != DEFAULT_LOCALE: + if locale_str != C.DEFAULT_LOCALE: try: translations = self.translations[locale] except KeyError: log.warning(_(u"Can't find locale {locale}".format(locale=locale))) - locale_str = DEFAULT_LOCALE + locale_str = C.DEFAULT_LOCALE locale = Locale.parse(self._locale_str) else: self.env.install_gettext_translations(translations, True) log.debug(_(u'Switched to {lang}').format(lang=locale.english_name)) - if locale_str == DEFAULT_LOCALE: + if locale_str == C.DEFAULT_LOCALE: self.env.install_null_translations(True) self._locale = locale @@ -366,75 +364,15 @@ return value if not current else u"{}_{}".format(value, current) def _date_fmt(self, timestamp, fmt='short', date_only=False, auto_limit=None, auto_old_fmt=None): + if is_undefined(fmt): + fmt = u'short' + try: - return self.date_fmt(timestamp, fmt, date_only, auto_limit, auto_old_fmt) + return date_utils.date_fmt(timestamp, fmt, date_only, auto_limit, auto_old_fmt) except Exception as e: log.warning(_(u"Can't parse date: {msg}").format(msg=e)) return timestamp - def date_fmt(self, timestamp, fmt='short', date_only=False, auto_limit=7, auto_old_fmt='short', auto_new_fmt='relative'): - """format date according to locale - - @param timestamp(basestring, int): unix time - @param fmt(str): one of: - - short: e.g. u'31/12/17' - - medium: e.g. u'Apr 1, 2007' - - long: e.g. u'April 1, 2007' - - full: e.g. u'Sunday, April 1, 2007' - - relative: format in relative time - e.g.: 3 hours - note that this format is not precise - - iso: ISO 8601 format - e.g.: u'2007-04-01T19:53:23Z' - - auto: use auto_old_fmt if date is older than auto_limit - else use auto_new_fmt - - auto_day: shorcut to set auto format with change on day - old format will be short, and new format will be time only - or a free value which is passed to babel.dates.format_datetime - @param date_only(bool): if True, only display date (not datetime) - @param auto_limit (int): limit in days before using auto_old_fmt - use 0 to have a limit at last midnight (day change) - @param auto_old_fmt(unicode): format to use when date is older than limit - @param auto_new_fmt(unicode): format to use when date is equal to or more recent - than limit - - """ - if is_undefined(fmt): - fmt = u'short' - - if (auto_limit is not None or auto_old_fmt is not None) and fmt != 'auto': - raise ValueError(u'auto argument can only be used with auto fmt') - if fmt == 'auto_day': - fmt, auto_limit, auto_old_fmt, auto_new_fmt = 'auto', 0, 'short', 'HH:mm' - if fmt == 'auto': - if auto_limit == 0: - today = time.mktime(datetime.date.today().timetuple()) - if int(timestamp) < today: - fmt = auto_old_fmt - else: - fmt = auto_new_fmt - else: - days_delta = (time.time() - int(timestamp)) / 3600 - if days_delta > (auto_limit or 7): - fmt = auto_old_fmt - else: - fmt = auto_new_fmt - - if fmt == 'relative': - delta = int(timestamp) - time.time() - return dates.format_timedelta(delta, granularity="minute", add_direction=True, locale=self._locale_str) - elif fmt in ('short', 'long'): - formatter = dates.format_date if date_only else dates.format_datetime - return formatter(int(timestamp), format=fmt, locale=self._locale_str) - elif fmt == 'iso': - if date_only: - fmt = 'yyyy-MM-dd' - else: - fmt = "yyyy-MM-ddTHH:mm:ss'Z'" - return dates.format_datetime(int(timestamp), format=fmt) - else: - return dates.format_datetime(int(timestamp), format=fmt, locale=self._locale_str) - def attr_escape(self, text): """escape a text to a value usable as an attribute @@ -595,7 +533,7 @@ name=name, cls=(' ' + cls) if cls else '')) - def render(self, template, theme=None, locale=DEFAULT_LOCALE, root_path=u'', media_path=u'', css_files=None, css_inline=False, **kwargs): + def render(self, template, theme=None, locale=C.DEFAULT_LOCALE, root_path=u'', media_path=u'', css_files=None, css_inline=False, **kwargs): """render a template . @param template(unicode): template to render (e.g. blog/articles.html)