Do not replace strftime placehoders escaped with double %
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
import datetime
|
||||
from ast import literal_eval
|
||||
from decimal import Decimal
|
||||
from itertools import takewhile
|
||||
from locale import CHAR_MAX
|
||||
|
||||
from sql import Table
|
||||
@@ -41,6 +42,27 @@ class DeleteDefaultError(UserError):
|
||||
NO_BREAKING_SPACE = '\u00A0'
|
||||
|
||||
|
||||
def _replace(src, old, new, escape='%'):
|
||||
"""Return a copy of src with all occurrences of substring old replaced
|
||||
by new if not escaped using double escape"""
|
||||
def is_escape(x):
|
||||
return x == escape
|
||||
assert old.startswith(escape)
|
||||
start = 0
|
||||
while start < len(src):
|
||||
i = src.find(old, start)
|
||||
if i < 0:
|
||||
break
|
||||
# if there is an odd number of percentage before the
|
||||
# placeholder then it is not escaped
|
||||
if len(list(takewhile(is_escape, reversed(src[:i])))) % 2:
|
||||
start = i + len(old)
|
||||
else:
|
||||
src = src[:i] + new + src[i + len(old):]
|
||||
start = i + len(new)
|
||||
return src
|
||||
|
||||
|
||||
class Lang(DeactivableMixin, ModelSQL, ModelView):
|
||||
"Language"
|
||||
__name__ = "ir.lang"
|
||||
@@ -547,15 +569,15 @@ class Lang(DeactivableMixin, ModelSQL, ModelView):
|
||||
if isinstance(value, datetime.datetime):
|
||||
format += ' %H:%M:%S'
|
||||
if '%x' in format:
|
||||
format = format.replace('%x', self.date)
|
||||
format = format.replace('%X', '%H:%M:%S')
|
||||
format = _replace(format, '%x', self.date)
|
||||
format = _replace(format, '%X', '%H:%M:%S')
|
||||
if isinstance(value, datetime.date):
|
||||
for f, i, klass in (('%A', 6, Day), ('%B', 1, Month)):
|
||||
for field, f in [('name', f), ('abbreviation', f.lower())]:
|
||||
if f in format:
|
||||
locale = klass.locale(self, field=field)
|
||||
format = format.replace(
|
||||
f, locale[value.timetuple()[i]])
|
||||
format = _replace(
|
||||
format, f, locale[value.timetuple()[i]])
|
||||
if '%p' in format:
|
||||
if isinstance(value, datetime.time):
|
||||
time = value
|
||||
@@ -569,7 +591,7 @@ class Lang(DeactivableMixin, ModelSQL, ModelView):
|
||||
p = self.am or 'AM'
|
||||
else:
|
||||
p = self.pm or 'PM'
|
||||
format = format.replace('%p', p)
|
||||
format = _replace(format, '%p', p)
|
||||
return value.strftime(format).replace(' ', NO_BREAKING_SPACE)
|
||||
|
||||
def format_number(self, value, digits=None, grouping=True, monetary=None):
|
||||
|
||||
@@ -13,6 +13,7 @@ except ImportError:
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from trytond.config import config
|
||||
from trytond.ir.lang import _replace
|
||||
from trytond.pool import Pool
|
||||
from trytond.pyson import Eval, If, PYSONEncoder
|
||||
from trytond.tools import timezone
|
||||
@@ -174,6 +175,17 @@ class IrTestCase(ModuleTestCase):
|
||||
self.assertEqual(
|
||||
lang.format(percent, value, grouping, monetary, *add), result)
|
||||
|
||||
def test_lang_replace(self):
|
||||
"Test string _replace"
|
||||
for src, result in [
|
||||
('%x', 'foo'),
|
||||
('%%x', '%%x'),
|
||||
('%x %x', 'foo foo'),
|
||||
('%x %y %x %%x', 'foo %y foo %%x'),
|
||||
]:
|
||||
with self.subTest(src=src):
|
||||
self.assertEqual(_replace(src, '%x', 'foo'), result)
|
||||
|
||||
@with_transaction()
|
||||
def test_lang_strftime(self):
|
||||
"Test Lang.strftime"
|
||||
@@ -192,9 +204,14 @@ class IrTestCase(ModuleTestCase):
|
||||
'%H:%M:%S', "14:30:12"),
|
||||
(datetime.datetime(2018, 11, 1, 14, 30, 12), None,
|
||||
"11/01/2018 14:30:12"),
|
||||
(datetime.date(2016, 8, 3), '%d %%m %Y', "03 %m 2016"),
|
||||
(datetime.date(2018, 11, 1), '%d %%x', "01 %x"),
|
||||
(datetime.date(2018, 11, 1), '%d %%a', "01 %a"),
|
||||
(datetime.datetime(2018, 11, 1, 14, 30, 12), '%d %%p', "01 %p"),
|
||||
]
|
||||
for date, format_, result in test_data:
|
||||
self.assertEqual(lang.strftime(date, format_), result)
|
||||
with self.subTest(date=date, format=format_):
|
||||
self.assertEqual(lang.strftime(date, format_), result)
|
||||
|
||||
@with_transaction()
|
||||
def test_lang_format_number(self):
|
||||
|
||||
Reference in New Issue
Block a user