(Generated with the help of an LLM.)
Problem/Motivation
Optional End Date replaces the core date range formatters daterange_default, daterange_custom and daterange_plain (see OptionalEndDateHooks::fieldFormatterInfoAlter()). All three replacements share OptionalEndDateDateTimeRangeTrait::viewElements().
Since #3458951, this method always calls renderStartEndWithIsoAttribute() and buildDateWithIsoAttribute(). In core, only the Default formatter uses these ISO helpers. Core's DateRangeCustomFormatter and DateRangePlainFormatter use renderStartEnd() and buildDate(). The doc comment of DateRangeCustomFormatter says it "renders the data range as plain text".
As a result, once this module is enabled, the Custom and Plain formatters output a <time datetime="…">…</time> element instead of plain text. This is a regression compared to core and breaks every place that uses the formatted value as text:
- REST/Views serializer exports (for example rest_views
field_export) now contain HTML:
"19.10.1946"becomes"<time datetime=\"1946-10-19T12:00:00Z\">19.10.1946</time>\n"(including the trailing newline fromtime.html.twig). - Views rewrite tokens, for example
<section id="year-{{ field_date }}">, now produce invalid attributes. - Views grouping titles and CSV exports get extra markup and whitespace.
Steps to reproduce
- Install Drupal 10.3+/11 with datetime_range, views, rest, serialization and optional_end_date 2.0.x.
- Add a
daterangefield (date only) to a content type and create a node with start date 1946-10-19 and the same end date. - Create a view with a REST export display and add the field using the Custom formatter, date format
d.m.Y. - Open the export.
Expected (core behaviour, module disabled): "19.10.1946"
Actual: "<time datetime=\"1946-10-19T12:00:00Z\">19.10.1946</time>\n"
The same happens with the Plain formatter and with ranges (19.10.1946 - 20.10.1946).
Proposed resolution
Let each formatter decide whether to use the ISO helpers, as core does:
- Add
protected function useIsoAttribute(): booltoOptionalEndDateDateTimeRangeTrait, returningTRUEby default (Default formatter, unchanged). - In
viewElements(), callrenderStartEnd()/buildDate()when it returnsFALSE, andrenderStartEndWithIsoAttribute()/buildDateWithIsoAttribute()otherwise. - Merge
$item->_attributesonly for the ISO variant, as core does, and only if an element was built. - Override
useIsoAttribute()to returnFALSEinOptionalEndDateDateRangeCustomFormatterandOptionalEndDateDateRangePlainFormatter.
Open-ended ranges keep working: when there is no end date, the start date alone is rendered, as plain text for Custom/Plain and as <time> for Default.
protected function useIsoAttribute(): bool { return TRUE; } // In viewElements(): $elements[$delta] = $use_iso_attribute ? $this->renderStartEndWithIsoAttribute($start_date, $separator, $end_date) : $this->renderStartEnd($start_date, $separator, $end_date);
A patch is attached.
Remaining tasks
- Review the patch.
- Add a kernel test asserting that Custom/Plain output contains no
<time>element, for both full and open-ended ranges.
User interface changes
None. The Custom and Plain formatters output plain text again, as in core.
API changes
New protected method OptionalEndDateDateTimeRangeTrait::useIsoAttribute().
Data model changes
None.
Issue fork optional_end_date-3634802
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
internetter commentedComment #4
internetter commentedI added a working patch as MergeRequest.