Fixed — a half-picked date range answered 500 on every visit
When a DateRangeFilter's state held a single date — the user picked the start of a range but not the end — apply() exploded it into one element and passed the raw string to modifyQueryUsing as $startDate. Any closure typed ?CarbonInterface threw:
$startDate must be of type ?CarbonInterface, string given
Filament persists filter state per user, so that user then got a 500 on the table on every visit until their session expired. State that did not match the format threw InvalidFormatException the same way.
Now:
| Filter state | 5.0.7 | 5.0.8 |
|---|---|---|
27/10/2026 - 28/10/2026
| both days | unchanged |
27/10/2026 (lone date)
| raw string to the callback → TypeError / 500 | that whole day, start-of-day → end-of-day in the display timezone, converted to the storage timezone |
not-a-date, 31/02/2026, wrong format
| InvalidFormatException → 500
| ignored: callback gets null, null, filter does nothing, no indicator chip
|
modifyQueryUsing now always receives Carbon instances or nulls. The input also shows a persisted lone date as that day instead of an empty box over an active filter. Covered by tests/SingleDateStateTest.php.
Also fixed
- Time picker end bound — without seconds in the format, the end "23:59" was read as 23:59:00 and dropped the last 59 seconds of every range, including the end of each built-in preset. It is now the end of that minute; formats with seconds keep the exact second.
- Time picker minute select — the default end is 23:59 but the default increment is 30, so the select showed
00while:59was applied. The selected minutes are now always offered. - RTL / Arabic panels — the trigger input and the selection preview showed
23:59 27/10/2026 - 00:00 27/10/2026for27/10/2026 00:00 - 27/10/2026 23:59. Dates now read correctly; the preview arrow is mirrored. - Setters that accepted what the getters could not handle:
firstDayOfWeek(fn () => 6)on the filter (TypeError),autoApply(fn () => …)(TypeError), stringstartDate()/endDate()/defaultCustom()(format() on string), andCarbonImmutablepresets (sent as UTC timestamps, so the previous day west of UTC).
Merged pull requests
- #257 Predefined ranges keep their time with
timePicker()— thanks @xplodman. Follow-up: date-only presets (which is how every Carbon preset reaches the browser, including the built-in Today) still cover the whole day instead of ending at 00:00. - #254 The stylesheet loads on request instead of on every panel page — thanks @wit3. About 48 KB less on pages without a picker, login included.
- #250
useDualState()works across Filament schemas — thanks @fritzfr. Separate start/end paths are entangled and dehydrated properly, with relative paths for nested schemas, repeaters and action forms (->useDualState('start', 'end', relative: true)). Follow-up: dual-state values are written ingetSystemTimezone(), so thesystemTimezone()setter applies to them too.
(#251, Laravel 13 support for the Filament v3 line, was merged into x3; it is not part of this release.)
Upgrading
No action needed. Run php artisan filament:assets on deploy as usual, because the JS and CSS in dist/ changed. A workaround that normalised a string $startDate in your own modifyQueryUsing keeps working, but is no longer needed.
Tested on Filament 5.11 and 4.16: 114 PHP tests and 149 JS tests.