-
-
Notifications
You must be signed in to change notification settings - Fork 617
Date-only fields show wrong date in CP collection table for timezones ahead of UTC #14413
Description
Bug description
When using a date field without time (time_enabled: false, format Y-m-d) and the application timezone is set to a
timezone ahead of UTC (e.g. Europe/Rome, GMT+1), the Control Panel collection table displays dates one day
behind the actual stored value.
Stored value (content file) -> 2026-03-14
CP entry publish form -> 14/03/2026
CP collection table -> 13/03/2026 (wrong)
Frontend (Antlers/Blade) -> 14/03/2026
Root cause
The issue is in src/Fieldtypes/Date.php, in the parseSaved() method (line ~333):
private function parseSaved($value)
{
$hasTime = false;
if (is_int($value)) {
$hasTime = true;
} elseif (DateFormat::containsTime($this->saveFormat())) {
$hasTime = true;
}
$carbon = $this->parseSavedToCarbon($value);
if (! $hasTime) {
$carbon = $carbon->startOfDay();
}
return $carbon->utc(); // ? this is the problem
}
For a date-only value like 2026-03-14:
- parseSavedToCarbon() creates 2026-03-14 00:00:00 Europe/Rome
- startOfDay() keeps it at 2026-03-14 00:00:00 Europe/Rome
- ->utc() converts it to 2026-03-13 23:00:00 UTC
Then in preProcessIndex(), this gets serialized via ->toIso8601ZuluString('millisecond') as
2026-03-13T23:00:00.000Z.
The Vue component DateIndexFieldtype.vue passes this Zulu string to new Date(props.value.date), and
DateFormatter.js renders it using Intl.DateTimeFormat with the browser's locale ? which correctly interprets the
UTC timestamp but shows March 13 because the date was already shifted back by the UTC conversion.
Why the frontend works correctly
On the frontend (Blade/Antlers), dates go through a different code path. In our case, we use a display_date()
helper that calls ->setTimezone(Statamic::displayTimezone()) on the augmented Carbon value, which correctly
converts it back to Europe/Rome. The augment() method also works differently from preProcessIndex() ? it doesn't
lose the date-only semantic.
However, the CP collection table uses preProcessIndex() ? Zulu string ? JavaScript Date ? Intl.DateTimeFormat,
which has no awareness that this was originally a date-only value and should not be affected by timezone
conversion.
Expected behavior
Date-only fields (without time component) should display the same date in the CP collection table as the value
stored in the content file, regardless of the application timezone. A date like 2026-03-14 should always render
as March 14 in the table.
Suggested fix
When hasTime is false in parseSaved(), the UTC conversion should be skipped or the date should be normalized to
noon (12:00) before converting, to prevent the day from shifting across the midnight boundary:
if (! $hasTime) {
$carbon = $carbon->startOfDay()->setTime(12, 0);
}
return $carbon->utc();
Alternatively, preProcessIndex() could pass the raw date string (without UTC conversion) when time_enabled is
false, since the JavaScript side doesn't need timezone-aware rendering for date-only values.
How to reproduce
- Set the application timezone to any timezone ahead of UTC (e.g. Europe/Rome, Asia/Tokyo):
APP_TIMEZONE=Europe/Rome
// config/statamic/system.php
'display_timezone' => 'Europe/Rome', - Create a collection with a date field (time_enabled: false):
handle: date
field:
type: date
time_enabled: false
format: Y-m-d - Create an entry and set the date to any value (e.g. 2026-03-14)
- Open the collection listing in the Control Panel ? the date column shows 13/03/2026 instead of 14/03/2026
- Open the entry's publish form ? the date picker correctly shows 14/03/2026
- Render the date on the frontend (Antlers or Blade) ? it correctly shows 14/03/2026
Note: The issue only affects timezones ahead of UTC (positive offset). Timezones behind UTC (e.g.
America/New_York) would not exhibit this problem, since subtracting hours from midnight would still land on the
same calendar date or shift forward instead of backward.
Logs
Environment
Environment
Laravel Version: 13.2.0
PHP Version: 8.4.18
Composer Version: 2.9.5
Environment: local
Debug Mode: ENABLED
Maintenance Mode: OFF
Timezone: Europe/Rome
Locale: it
Cache
Config: NOT CACHED
Events: NOT CACHED
Routes: NOT CACHED
Views: CACHED
Drivers
Broadcasting: log
Cache: file
Database: mariadb
Logs: daily
Mail: smtp
Queue: database
Session: file
Storage
public/storage: LINKED
Sentry
Enabled: MISSING DSN
Environment: local
Laravel SDK Version: 4.24.0
PHP SDK Version: 4.23.1
Release: NOT SET
Sample Rate Errors: 100%
Sample Rate Performance Monitoring: NOT SET
Sample Rate Profiling: NOT SET
Send Default PII: DISABLED
Statamic
Addons: 4
Sites: 5 (Italiano, Inglese, Francese, Tedesco, Spagnolo)
Stache Watcher: Enabled (auto)
Static Caching: Disabled
Version: 6.8.0 PRO
Statamic Addons
rias/statamic-redirect: 4.1.7
statamic-rad-pack/runway: 9.3.2
statamic/eloquent-driver: 5.5.0
statamic/seo-pro: 7.3.0
Statamic Eloquent Driver
Addon Settings: file
Asset Containers: file
Assets: file
Blueprints: file
Collection Trees: file
Collections: file
Entries: file
Fieldsets: file
Form Submissions: eloquent
Forms: file
Global Sets: file
Global Variables: file
Navigation Trees: file
Navigations: file
Revisions: file
Sites: file
Taxonomies: file
Terms: file
Tokens: fileInstallation
Starter Kit using via CLI
Additional details
I'm new to using github issues, so feel free to tell me if and where i'm wrong.