Do not replace strftime placehoders escaped with double %

This commit is contained in:
Cédric Krier
2023-05-09 22:53:51 +02:00
parent f141830d1b
commit 5c1f28efa0
2 changed files with 45 additions and 6 deletions
+27 -5
View File
@@ -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):
+18 -1
View File
@@ -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):