Allow user to specify date_format

This commit is contained in:
Oliver Walters 2026-06-19 23:43:27 +00:00
parent 3bfea9f624
commit 75a7db6015
3 changed files with 81 additions and 24 deletions

View File

@ -409,9 +409,22 @@ For rendering date and datetime information, the following helper functions are
Both functions resolve their output using the following priority order:
1. **`fmt=` argument** — a [strftime format string](https://docs.python.org/3/library/datetime.html#format-codes). When provided, this takes full priority and the `locale` argument is ignored.
2. **`locale=` argument** (or the default system locale setting) — when no `fmt` is given, Babel formats the value in a locale-aware *medium* style (e.g. `Jan 12, 2025` for `en-us`).
3. **ISO 8601 fallback** — when neither `fmt` nor a locale is available, the value is returned in ISO format (e.g. `2025-01-12`).
1. **`fmt=` argument** — a [strftime format string](https://docs.python.org/3/library/datetime.html#format-codes). When provided, this takes full priority; `locale` and `date_format` are ignored.
2. **`locale=` argument** — when no `fmt` is given, Babel formats the value using the style set by `date_format` (default `medium`).
3. **Server `LANGUAGE_CODE`** — used as the locale when no `locale=` argument is supplied.
#### Date Format Styles
The `date_format` argument controls how Babel renders the date when locale-aware formatting is used. The four named styles are:
| Style | `format_date` example (en-us, 2025-01-12) | `format_datetime` example (en-us, 2025-01-12 14:30) |
| --- | --- | --- |
| `full` | `Sunday, January 12, 2025` | `Sunday, January 12, 2025 at 2:30:00 PM UTC` |
| `long` | `January 12, 2025` | `January 12, 2025 at 2:30:00 PM UTC` |
| `medium` *(default)* | `Jan 12, 2025` | `Jan 12, 2025, 2:30:00 PM` |
| `short` | `1/12/25` | `1/12/25, 2:30 PM` |
The exact output varies by locale — the table above uses `en-us`.
### format_date
@ -426,23 +439,25 @@ Both functions resolve their output using the following priority order:
{% raw %}
{% load report %}
<!-- ISO fallback (no fmt, no locale) -->
<!-- Default: medium style, locale from LANGUAGE_CODE -->
{% format_date my_date %}
<!-- output: 2025-01-12 -->
<!-- output (en-us): Jan 12, 2025 -->
<!-- Explicit strftime format string — locale is ignored -->
<!-- Explicit strftime format string — locale and date_format are ignored -->
{% format_date my_date fmt="%d/%m/%Y" %}
<!-- output: 12/01/2025 -->
<!-- Locale-aware medium format -->
<!-- Locale-aware, default medium style -->
{% format_date my_date locale='en-us' %}
<!-- output: Jan 12, 2025 -->
{% format_date my_date locale='de-de' %}
<!-- output: 12.01.2025 -->
<!-- Short style -->
{% format_date my_date locale='en-us' date_format='short' %}
<!-- output: 1/12/25 -->
<!-- Convert from a different timezone before formatting -->
{% format_date my_date timezone="Australia/Sydney" locale='en-au' %}
<!-- Full style -->
{% format_date my_date locale='en-us' date_format='full' %}
<!-- output: Sunday, January 12, 2025 -->
{% endraw %}
```
@ -460,20 +475,21 @@ Both functions resolve their output using the following priority order:
{% raw %}
{% load report %}
<!-- ISO fallback (no fmt, no locale) -->
<!-- Default: medium style, locale from LANGUAGE_CODE -->
{% format_datetime my_datetime %}
<!-- output: 2025-01-12T14:30:00+00:00 -->
<!-- output (en-us): Jan 12, 2025, 2:30:00 PM -->
<!-- Explicit strftime format — locale is ignored -->
<!-- Explicit strftime format — locale and date_format are ignored -->
{% format_datetime my_datetime fmt="%d-%m-%Y %H:%M" %}
<!-- output: 12-01-2025 14:30 -->
<!-- Locale-aware medium format -->
<!-- Locale-aware, default medium style -->
{% format_datetime my_datetime locale='en-us' %}
<!-- output: Jan 12, 2025, 2:30:00 PM -->
{% format_datetime my_datetime locale='de-de' %}
<!-- output: 12.01.2025, 14:30:00 -->
<!-- Short style -->
{% format_datetime my_datetime locale='de-de' date_format='short' %}
<!-- output: 12.01.25, 14:30 -->
<!-- Convert to a specific timezone before formatting -->
{% format_datetime my_datetime timezone="Australia/Sydney" locale='en-au' %}

View File

@ -982,6 +982,7 @@ def format_number(
max_decimal_places: Optional[int] = None,
fmt: Optional[str] = None,
locale: Optional[str] = None,
**kwargs,
) -> str:
"""Render a number with optional formatting options.
@ -1065,14 +1066,17 @@ def format_datetime(
timezone: Optional[str] = None,
fmt: Optional[str] = None,
locale: Optional[str] = None,
date_format: str = 'medium',
**kwargs,
):
"""Format a datetime object for display.
Arguments:
dt: The datetime object to format
timezone: The timezone to use for the date (defaults to the server timezone)
fmt: The strftime format string to use. When provided, takes priority over locale.
fmt: The strftime format string to use. When provided, takes priority over locale and date_format.
locale: Optional locale override (e.g. 'en-us', 'de-de'). Used for locale-aware formatting when no fmt is given.
date_format: Babel date format style. One of 'full', 'long', 'medium' (default), 'short'.
"""
check_nulls('format_datetime', dt)
@ -1081,10 +1085,7 @@ def format_datetime(
if fmt:
return dt.strftime(fmt)
return babel_format_datetime(dt, format='medium', locale=get_locale(locale))
# Final backup - return an ISO formatted string
return dt.isoformat()
return babel_format_datetime(dt, format=date_format, locale=get_locale(locale))
@register.simple_tag
@ -1093,14 +1094,17 @@ def format_date(
timezone: Optional[str] = None,
fmt: Optional[str] = None,
locale: Optional[str] = None,
date_format: str = 'medium',
**kwargs,
):
"""Format a date object for display.
Arguments:
dt: The date to format
timezone: The timezone to use for the date (defaults to the server timezone)
fmt: The strftime format string to use. When provided, takes priority over locale.
fmt: The strftime format string to use. When provided, takes priority over locale and date_format.
locale: Optional locale override (e.g. 'en-us', 'de-de'). Used for locale-aware formatting when no fmt is given.
date_format: Babel date format style. One of 'full', 'long', 'medium' (default), 'short'.
"""
check_nulls('format_date', dt)
@ -1112,7 +1116,7 @@ def format_date(
if fmt:
return dt.strftime(fmt)
return babel_format_date(dt, format='medium', locale=get_locale(locale))
return babel_format_date(dt, format=date_format, locale=get_locale(locale))
@register.simple_tag()

View File

@ -730,6 +730,25 @@ class ReportTagTest(PartImageTestMixin, InvenTreeTestCase):
with override_settings(LANGUAGE_CODE='en-us'):
self.assertEqual(report_tags.format_date(dt), 'Mar 13, 2024')
# date_format controls the Babel style
self.assertEqual(
report_tags.format_date(dt, locale='en-us', date_format='short'), '3/13/24'
)
self.assertEqual(
report_tags.format_date(dt, locale='en-us', date_format='long'),
'March 13, 2024',
)
self.assertEqual(
report_tags.format_date(dt, locale='en-us', date_format='full'),
'Wednesday, March 13, 2024',
)
# fmt= wins over date_format=
self.assertEqual(
report_tags.format_date(dt, fmt='%Y', locale='en-us', date_format='full'),
'2024',
)
# Invalid locale raises ValidationError
with self.assertRaises(ValidationError):
report_tags.format_date(dt, locale='xx-zz')
@ -758,6 +777,24 @@ class ReportTagTest(PartImageTestMixin, InvenTreeTestCase):
with override_settings(LANGUAGE_CODE='de-de'):
self.assertEqual(report_tags.format_datetime(dt), '19.06.2026, 15:30:00')
# date_format controls the Babel style
self.assertEqual(
report_tags.format_datetime(dt, locale='de-de', date_format='short'),
'19.06.26, 15:30',
)
self.assertEqual(
report_tags.format_datetime(dt, locale='de-de', date_format='long'),
'19. Juni 2026 um 15:30:00 UTC', # codespell:ignore "Juni"
)
# fmt= wins over date_format=
self.assertEqual(
report_tags.format_datetime(
dt, fmt='%H:%M', locale='en-us', date_format='full'
),
'15:30',
)
# Invalid locale raises ValidationError
with self.assertRaises(ValidationError):
report_tags.format_datetime(dt, locale='xx-zz')