diff --git a/bin/addons/base/res/res_lang.py b/bin/addons/base/res/res_lang.py index d19e9389884..f59127318ec 100644 --- a/bin/addons/base/res/res_lang.py +++ b/bin/addons/base/res/res_lang.py @@ -19,18 +19,22 @@ # ############################################################################## +import locale +import logging + from osv import fields, osv from locale import localeconv import tools from tools.safe_eval import safe_eval as eval from tools.translate import _ -import locale -import logging class lang(osv.osv): _name = "res.lang" _description = "Languages" + _disallowed_datetime_patterns = tools.DATETIME_FORMATS_MAP.keys() + _disallowed_datetime_patterns.remove('%y') # this one is in fact allowed, just not good practice + def install_lang(self, cr, uid, **args): lang = tools.config.get('lang') if not lang: @@ -73,13 +77,14 @@ class lang(osv.osv): return '\xc2\xa0' return s - def fix_time_format(format): - """Python's strftime does not support all the compound formats - from C's strftime, as returned by locale.nl_langinfo(). - Under Ubuntu at least, we encounter %T or %r for some locales.""" - format = format.replace('%T', '%H:%M:%S')\ - .replace('%r', '%I:%M:%S %p')\ - .replace('%R', '%H:%M') + def fix_datetime_format(format): + """Python's strftime supports only the format directives + that are available on the platform's libc, so in order to + be 100% cross-platform we map to the directives required by + the C standard (1989 version), always available on platforms + with a C standard implementation.""" + for pattern, replacement in tools.DATETIME_FORMATS_MAP.iteritems(): + format = format.replace(pattern, replacement) return str(format) lang_info = { @@ -87,8 +92,8 @@ class lang(osv.osv): 'iso_code': iso_lang, 'name': lang_name, 'translatable': 1, - 'date_format' : str(locale.nl_langinfo(locale.D_FMT).replace('%y', '%Y')), - 'time_format' : fix_time_format(locale.nl_langinfo(locale.T_FMT)), + 'date_format' : fix_datetime_format(locale.nl_langinfo(locale.D_FMT)), + 'time_format' : fix_datetime_format(locale.nl_langinfo(locale.T_FMT)), 'decimal_point' : fix_xa0(str(locale.localeconv()['decimal_point'])), 'thousands_sep' : fix_xa0(str(locale.localeconv()['thousands_sep'])), } @@ -99,6 +104,14 @@ class lang(osv.osv): tools.resetlocale() return lang_id + def _check_format(self, cr, uid, ids, context=None): + for lang in self.browse(cr, uid, ids, context=context): + for pattern in self._disallowed_datetime_patterns: + if (lang.time_format and pattern in lang.time_format)\ + or (lang.date_format and pattern in lang.date_format): + return False + return True + def _get_default_date_format(self,cursor,user,context={}): return '%m/%d/%Y' @@ -133,6 +146,10 @@ class lang(osv.osv): ('code_uniq', 'unique (code)', 'The code of the language must be unique !'), ] + _constraints = [ + (_check_format, 'Invalid date/time format directive specified. Please refer to the list of allowed directives, displayed when you edit a language.', ['time_format', 'date_format']) + ] + @tools.cache(skiparg=3) def _lang_data_get(self, cr, uid, lang_id, monetary=False): conv = localeconv() diff --git a/bin/tools/misc.py b/bin/tools/misc.py index 015299bcf05..d18090932dc 100644 --- a/bin/tools/misc.py +++ b/bin/tools/misc.py @@ -1352,6 +1352,51 @@ DEFAULT_SERVER_DATETIME_FORMAT = "%s %s" % ( DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_TIME_FORMAT) +# Python's strftime supports only the format directives +# that are available on the platform's libc, so in order to +# be cross-platform we map to the directives required by +# the C standard (1989 version), always available on platforms +# with a C standard implementation. +DATETIME_FORMATS_MAP = { + '%C': '', # century + '%D': '%m/%d/%Y', # modified %y->%Y + '%e': '%d', + '%E': '', # special modifier + '%F': '%Y-%m-%d', + '%g': '%Y', # modified %y->%Y + '%G': '%Y', + '%h': '%b', + '%k': '%H', + '%l': '%I', + '%n': '\n', + '%O': '', # special modifier + '%P': '%p', + '%R': '%H:%M', + '%r': '%I:%M:%S %p', + '%s': '', #num of seconds since epoch + '%T': '%H:%M:%S', + '%t': ' ', # tab + '%u': ' %w', + '%V': '%W', + '%y': '%Y', # Even if %y works, it's ambiguous, so we should use %Y + '%+': '%Y-%m-%d %H:%M:%S', + + # %Z is a special case that causes 2 problems at least: + # - the timezone names we use (in res_user.context_tz) come + # from pytz, but not all these names are recognized by + # strptime(), so we cannot convert in both directions + # when such a timezone is selected and %Z is in the format + # - %Z is replaced by an empty string in strftime() when + # there is not tzinfo in a datetime value (e.g when the user + # did not pick a context_tz). The resulting string does not + # parse back if the format requires %Z. + # As a consequence, we strip it completely from format strings. + # The user can always have a look at the context_tz in + # preferences to check the timezone. + '%z': '', + '%Z': '', +} + def server_to_local_timestamp(src_tstamp_str, src_format, dst_format, dst_tz_name, tz_offset=True, ignore_unparsable_time=True): """