GPM format_duration(): make 'largest_unit' as optional parameter

This commit is contained in:
un-pogaz 2025-07-09 19:49:44 +02:00
parent 85f448527b
commit 3bc9f1c4f4

View File

@ -3418,11 +3418,11 @@ See also the functions :ref:`make_url`, :ref:`make_url_extended` and :ref:`query
class BuiltinFormatDuration(BuiltinFormatterFunction): class BuiltinFormatDuration(BuiltinFormatterFunction):
name = 'format_duration' name = 'format_duration'
arg_count = 3 arg_count = -1
category = FORMATTING_VALUES category = FORMATTING_VALUES
__doc__ = doc = _( __doc__ = doc = _(
r''' r'''
``format_duration(value, template, largest_unit)`` -- format the value, a number ``format_duration(value, template, [largest_unit])`` -- format the value, a number
of seconds, into a string showing weeks, days, hours, minutes, and seconds. If of seconds, into a string showing weeks, days, hours, minutes, and seconds. If
the value is a float then it is rounded to the nearest integer.[/] You choose the value is a float then it is rounded to the nearest integer.[/] You choose
how to format the value using a template consisting of value selectors how to format the value using a template consisting of value selectors
@ -3436,23 +3436,20 @@ surrounded by ``[`` and ``]`` characters. The selectors are:
[/LIST] [/LIST]
You can put arbitrary text between selectors. You can put arbitrary text between selectors.
The ``largest_unit`` parameter specifies the largest of weeks, days, hours, minutes,
and seconds that will be produced by the template. It must be one of the value selectors.
The following examples use a duration of 2 days (172,800 seconds) 1 hour (3,600 seconds) The following examples use a duration of 2 days (172,800 seconds) 1 hour (3,600 seconds)
and 20 seconds, which totals to 176,420 seconds. and 20 seconds, which totals to 176,420 seconds.
[LIST] [LIST]
[*]``format_duration(176420, '[d][h][m][s]', 'd')`` will return the value ``2d 1h 0m 20s``. [*]``format_duration(176420, '[d][h][m][s]')`` will return the value ``2d 1h 0m 20s``.
[*]``format_duration(176420, '[h][m][s]', 'h')`` will return the value ``49h 0m 20s``. [*]``format_duration(176420, '[h][m][s]')`` will return the value ``49h 0m 20s``.
[*]``format_duration(176420, 'Your reading time is [d][h][m][s]', 'h')`` returns the value [*]``format_duration(176420, 'Your reading time is [d][h][m][s]')`` returns the value
``Your reading time is 49h 0m 20s``. ``Your reading time is 49h 0m 20s``.
[*]``format_duration(176420, '[w][d][h][m][s]', 'w')`` will return the value ``2d 1h 0m 20s``. [*]``format_duration(176420, '[w][d][h][m][s]')`` will return the value ``2d 1h 0m 20s``.
Note that the zero weeks value is not returned. Note that the zero weeks value is not returned.
[/LIST] [/LIST]
If you want to see zero values for items such as weeks in the above example, If you want to see zero values for items such as weeks in the above example,
use an uppercase selector. For example, the following uses ``'W'`` to show zero weeks: use an uppercase selector. For example, the following uses ``'W'`` to show zero weeks:
``format_duration(176420, '[W][d][h][m][s]', 'w')`` returns ``0w 2d 1h 0m 20s``. ``format_duration(176420, '[W][d][h][m][s]')`` returns ``0w 2d 1h 0m 20s``.
By default the text following a value is the selector followed by a space. By default the text following a value is the selector followed by a space.
You can change that to whatever text you want. The format for a selector with You can change that to whatever text you want. The format for a selector with
@ -3477,12 +3474,35 @@ For example, the selector:
[*]``[w: weeks | week ]`` produces ``'0 weeks '``, ``'1 week '``, or ``'2 weeks '``. [*]``[w: weeks | week ]`` produces ``'0 weeks '``, ``'1 week '``, or ``'2 weeks '``.
[*]``[w: weeks ]`` produces ``0 weeks '``, ``1 weeks '``, or ``2 weeks '``. [*]``[w: weeks ]`` produces ``0 weeks '``, ``1 weeks '``, or ``2 weeks '``.
[/LIST] [/LIST]
The optional ``largest_unit`` parameter specifies the largest of weeks, days, hours, minutes,
and seconds that will be produced by the template. It must be one of the value selectors.
This can be useful to truncate a value.
``format_duration(176420, '[h][m][s]', 'd')`` will return the value ``1h 0m 20s`` instead of ``49h 0m 20s``.
''') ''')
def evaluate(self, formatter, kwargs, mi, locals, value, template, largest_unit): def evaluate(self, formatter, kwargs, mi, locals, value, template, largest_unit=''):
if largest_unit not in 'wdhms': if largest_unit not in 'wdhms':
raise ValueError(_('the {0} parameter must be one of {1}').format('largest_unit', 'wdhms')) raise ValueError(_('the {0} parameter must be one of {1}').format('largest_unit', 'wdhms'))
pat = re.compile(r'\[(.)(:(.*?))?\]')
if not largest_unit:
for m in pat.finditer(template):
fmt_char = m.group(1)
match fmt_char.lower():
case 'w' if largest_unit in 'dhms':
largest_unit = 'w'
case 'd' if largest_unit in 'hms':
largest_unit = 'd'
case 'h' if largest_unit in 'ms':
largest_unit = 'h'
case 'm' if largest_unit in 's':
largest_unit = 'm'
case 's' if not largest_unit:
largest_unit = 's'
int_val = remainder = round(float(value)) if value else 0 int_val = remainder = round(float(value)) if value else 0
weeks,remainder = divmod(remainder, 60*60*24*7) if largest_unit == 'w' else (-1,remainder) weeks,remainder = divmod(remainder, 60*60*24*7) if largest_unit == 'w' else (-1,remainder)
days,remainder = divmod(remainder, 60*60*24) if largest_unit in 'wd' else (-1,remainder) days,remainder = divmod(remainder, 60*60*24) if largest_unit in 'wd' else (-1,remainder)
@ -3491,9 +3511,8 @@ For example, the selector:
seconds = remainder seconds = remainder
def repl(mo): def repl(mo):
txt = mo.group() fmt_char = mo.group(1)
fmt_char = txt[1] suffixes = re.split(r'\|', mo.group(3) or '')
suffixes = re.split(r'\|', txt[3:-1])
match len(suffixes): match len(suffixes):
case 1 if not suffixes[0]: case 1 if not suffixes[0]:
zero_suffix = one_suffix = more_suffix = fmt_char.lower() + ' ' zero_suffix = one_suffix = more_suffix = fmt_char.lower() + ' '
@ -3537,8 +3556,7 @@ For example, the selector:
case _: case _:
raise ValueError(_('The {} format specifier is not valid').format(fmt_char)) raise ValueError(_('The {} format specifier is not valid').format(fmt_char))
s = re.sub(r'\[.:?.*?\]', repl, template) return pat.sub(repl, template)
return s
_formatter_builtins = [ _formatter_builtins = [