Skip to content

561: Replace SpoonDate#442

Merged
ilaria-orlando merged 1 commit into
masterfrom
561-sppondate-vervangen-door-intldateformatter
Jun 1, 2026
Merged

561: Replace SpoonDate#442
ilaria-orlando merged 1 commit into
masterfrom
561-sppondate-vervangen-door-intldateformatter

Conversation

@ilaria-orlando
Copy link
Copy Markdown

@ilaria-orlando ilaria-orlando commented May 28, 2026

Type

  • Enhancement

Pull request description

Replaces SpoonDate with IntlDateFormatter to format dates and timestamps where language is used, replace by date where language does not matter.

Summary by Sourcery

Replace legacy SpoonDate usage with IntlDateFormatter-based date handling and add helper methods for parameterized translations.

Enhancements:

  • Migrate backend and frontend date and time formatting from SpoonDate to IntlDateFormatter in grids, blog archive titles, breadcrumbs, form exports, and RSS metadata.
  • Introduce Language helper methods to fetch labels, messages, and errors with parameter substitution.
  • Improve the time-ago rendering logic to use localized IntlDateFormatter output and translation keys for singular and plural time units.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 28, 2026

Reviewer's Guide

Replaces legacy SpoonDate usage with IntlDateFormatter and native PHP date functions, and adds language helper methods for parameterized labels/messages/errors, ensuring locale-aware date formatting across backend and frontend components.

Sequence diagram for the updated getTimeAgo date formatting flow

sequenceDiagram
    participant Caller
    participant DataGridFunctions
    participant BackendLanguage
    participant Language
    participant IntlDateFormatter

    Caller->>DataGridFunctions: getTimeAgo(timestamp)
    alt [timestamp === 0]
        DataGridFunctions-->>Caller: ""
    else [timestamp > 0]
        DataGridFunctions->>BackendLanguage: getInterfaceLanguage()
        BackendLanguage-->>DataGridFunctions: locale
        DataGridFunctions->>IntlDateFormatter: __construct(locale, NONE, NONE, null, null, "yyyy-MM-dd HH:mm:ss")
        DataGridFunctions->>IntlDateFormatter: format(timestamp)
        IntlDateFormatter-->>DataGridFunctions: formattedDateTime
        DataGridFunctions->>IntlDateFormatter: __construct(locale, NONE, NONE, null, null, "d MMMM yyyy HH:mm")
        DataGridFunctions->>IntlDateFormatter: format(timestamp)
        IntlDateFormatter-->>DataGridFunctions: formattedTitle
        DataGridFunctions-->>DataGridFunctions: compute seconds, minutes, hours, days, months, years
        alt [count <= 0]
            DataGridFunctions->>Language: lbl(TimeAgoEmpty)
            Language-->>DataGridFunctions: label
        else [count > 0]
            alt [count === 1]
                DataGridFunctions->>Language: lbl(keySingular)
                Language-->>DataGridFunctions: timeAgo
            else [count > 1]
                DataGridFunctions->>Language: lblWithParameters(keyPlural, [count])
                Language-->>DataGridFunctions: timeAgo
            end
        end
        DataGridFunctions-->>Caller: "<time ...>" + timeAgo
    end
Loading

File-Level Changes

Change Details Files
Replace SpoonDate-based formatting helpers in backend datagrid with IntlDateFormatter and custom time-ago implementation.
  • Swap SpoonDate import for IntlDateFormatter and add Backend\Core\Language\Language import.
  • Refactor getDate, getLongDate, and getTime to instantiate IntlDateFormatter with locale from BackendLanguage::getInterfaceLanguage() and explicit patterns, returning formatter output directly.
  • Rewrite getTimeAgo to manually compute time differences (seconds to years), choose singular/plural translation keys, and use Language::lbl / lblWithParameters, while using IntlDateFormatter for datetime and title attributes in the tag.
src/Backend/Core/Engine/DataGridFunctions.php
Add parameterized translation helpers to backend Language class.
  • Introduce lblWithParameters(), msgWithParameters(), and errWithParameters() methods that wrap getLabel, getMessage, and getError respectively.
  • Each helper optionally formats translation strings with vsprintf when parameters are provided, otherwise returning the raw label/message/error.
src/Backend/Core/Language/Language.php
Update frontend blog archive month formatting to use IntlDateFormatter instead of SpoonDate.
  • Import IntlDateFormatter in the archive action.
  • Replace \SpoonDate::getDate('F', ...) calls for page title and breadcrumb month labels with IntlDateFormatter configured with LANGUAGE and 'MMMM' pattern.
src/Frontend/Modules/Blog/Actions/Archive.php
Use IntlDateFormatter for exported submission timestamps in FormBuilder backend export.
  • Import IntlDateFormatter in ExportData action.
  • Replace \SpoonDate::getDate('Y-m-d H:i:s', ...) when populating the Sent On column with IntlDateFormatter configured for the working language and 'yyyy-MM-dd HH:mm:ss' pattern.
src/Backend/Modules/FormBuilder/Actions/ExportData.php
Replace SpoonDate usages where localization is not required with native date().
  • Update FAQ feedback update statement to use PHP date('Y-m-d H:i:s') for edited_on field.
  • Change frontend RSS feed copyright year to use date('Y') instead of \SpoonDate::getDate('Y').
src/Backend/Modules/Faq/Engine/Model.php
src/Frontend/Core/Engine/Rss.php

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The repeated instantiation of IntlDateFormatter with identical patterns (e.g. in getDate, getLongDate, getTime, blog archive title/breadcrumb, and form export) could be centralized in a small helper/factory to avoid duplication and make future format or locale changes easier.
  • In DataGridFunctions::getTimeAgo, consider using an ISO-8601-compliant format (e.g. DateTimeImmutable->format(DATE_ATOM) or c) for the <time datetime="..."> attribute instead of a localized IntlDateFormatter pattern, so the markup is consistently machine-readable regardless of locale.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The repeated instantiation of `IntlDateFormatter` with identical patterns (e.g. in `getDate`, `getLongDate`, `getTime`, blog archive title/breadcrumb, and form export) could be centralized in a small helper/factory to avoid duplication and make future format or locale changes easier.
- In `DataGridFunctions::getTimeAgo`, consider using an ISO-8601-compliant format (e.g. `DateTimeImmutable->format(DATE_ATOM)` or `c`) for the `<time datetime="...">` attribute instead of a localized `IntlDateFormatter` pattern, so the markup is consistently machine-readable regardless of locale.

## Individual Comments

### Comment 1
<location path="src/Frontend/Modules/Blog/Actions/Archive.php" line_range="174-180" />
<code_context>
         }

-        $format = 'j F Y';
+        $date = new IntlDateFormatter(
+            BackendLanguage::getInterfaceLanguage(),
+            IntlDateFormatter::NONE,
</code_context>
<issue_to_address>
**suggestion:** The IntlDateFormatter instantiation is duplicated and could be shared between methods.

Both `setPageTitle()` and `addPageToBreadcrumb()` construct the same `IntlDateFormatter` for the month label. Please extract this into a private helper (e.g. `formatMonthName(DateTimeInterface $date): string`) and call it from both methods to remove duplication and keep future changes (pattern/locale) in one place.

Suggested implementation:

```
        if ($this->hasMonth) {
            $this->header->setPageTitle(
                $this->formatMonthName($this->startDate)
            );
        }

```

`.

Here are the edits:

<file_operations>
<file_operation operation="edit" file_path="src/Frontend/Modules/Blog/Actions/Archive.php">
<<<<<<< SEARCH
        if ($this->hasMonth) {
            $date = new IntlDateFormatter(
                LANGUAGE,
                IntlDateFormatter::NONE,
                IntlDateFormatter::NONE,
                null,
                null,
                'MMMM'
            )->format($this->startDate->getTimestamp());
            $this->header->setPageTitle(
                $date
            );
        }
=======
        if ($this->hasMonth) {
            $this->header->setPageTitle(
                $this->formatMonthName($this->startDate)
            );
        }
>>>>>>> REPLACE
</file_operation>
</file_operations>

<additional_changes>
To fully implement the refactor you described, you should also:

1. Add a private helper method on the `Archive` class to encapsulate the `IntlDateFormatter` logic, for example:

   ```php
   private function formatMonthName(\DateTimeInterface $date): string
   {
       return (new IntlDateFormatter(
           LANGUAGE,
           IntlDateFormatter::NONE,
           IntlDateFormatter::NONE,
           null,
           null,
           'MMMM'
       ))->format($date->getTimestamp());
   }
   ```

   - Place this method alongside the other private helper methods in the `Archive` class.
   - If the file does not already import `DateTimeInterface`, either fully qualify it as above or add `use DateTimeInterface;` at the top and then use `DateTimeInterface` in the signature.

2. Find the duplicated `IntlDateFormatter` instantiation in `addPageToBreadcrumb()` (it should look very similar or identical to the block that was just replaced) and refactor it to use the helper as well, e.g.:

   ```php
   $monthLabel = $this->formatMonthName($this->startDate);
   // use $monthLabel where the formatted month was previously used
   ```

3. Ensure both `setPageTitle()` usage (the one shown in your snippet) and the breadcrumb code now rely solely on `formatMonthName()`, so future changes to locale/pattern only need to be applied in one place.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +174 to +180
$date = new IntlDateFormatter(
LANGUAGE,
IntlDateFormatter::NONE,
IntlDateFormatter::NONE,
null,
null,
'MMMM'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: The IntlDateFormatter instantiation is duplicated and could be shared between methods.

Both setPageTitle() and addPageToBreadcrumb() construct the same IntlDateFormatter for the month label. Please extract this into a private helper (e.g. formatMonthName(DateTimeInterface $date): string) and call it from both methods to remove duplication and keep future changes (pattern/locale) in one place.

Suggested implementation:

        if ($this->hasMonth) {
            $this->header->setPageTitle(
                $this->formatMonthName($this->startDate)
            );
        }

`.

Here are the edits:

<file_operations>
<file_operation operation="edit" file_path="src/Frontend/Modules/Blog/Actions/Archive.php">
<<<<<<< SEARCH
if ($this->hasMonth) {
$date = new IntlDateFormatter(
LANGUAGE,
IntlDateFormatter::NONE,
IntlDateFormatter::NONE,
null,
null,
'MMMM'
)->format($this->startDate->getTimestamp());
$this->header->setPageTitle(
$date
);
}

    if ($this->hasMonth) {
        $this->header->setPageTitle(
            $this->formatMonthName($this->startDate)
        );
    }

REPLACE
</file_operation>
</file_operations>

<additional_changes>
To fully implement the refactor you described, you should also:

  1. Add a private helper method on the Archive class to encapsulate the IntlDateFormatter logic, for example:

    private function formatMonthName(\DateTimeInterface $date): string
    {
        return (new IntlDateFormatter(
            LANGUAGE,
            IntlDateFormatter::NONE,
            IntlDateFormatter::NONE,
            null,
            null,
            'MMMM'
        ))->format($date->getTimestamp());
    }
    • Place this method alongside the other private helper methods in the Archive class.
    • If the file does not already import DateTimeInterface, either fully qualify it as above or add use DateTimeInterface; at the top and then use DateTimeInterface in the signature.
  2. Find the duplicated IntlDateFormatter instantiation in addPageToBreadcrumb() (it should look very similar or identical to the block that was just replaced) and refactor it to use the helper as well, e.g.:

    $monthLabel = $this->formatMonthName($this->startDate);
    // use $monthLabel where the formatted month was previously used
  3. Ensure both setPageTitle() usage (the one shown in your snippet) and the breadcrumb code now rely solely on formatMonthName(), so future changes to locale/pattern only need to be applied in one place.

@ilaria-orlando ilaria-orlando merged commit c69b935 into master Jun 1, 2026
10 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants