Bug description
Builder::whereDate() and Builder::whereTime() (src/Query/Builder.php) accept a DateTimeInterface value. Internally they format the value down to a date-only or time-only string, then reparse it:
$value = Carbon::parse($value->format('Y-m-d')); // whereDate
$value = $value->format('H:i:s'); // whereTime
format() reads the components in the value's own timezone, but the reparse (Carbon::parse, or the raw string in whereTime) uses the app's default timezone (config('app.timezone')). If the passed value is in a different timezone than the app, the calendar digits get reinterpreted as if they were already in the app timezone — silently shifting the represented instant instead of converting it.
How to reproduce
App timezone UTC. Entry has test_date = 2021-11-14 09:00:00 (stored/augmented in UTC).
$value = Carbon::create(2021, 11, 15, 2, 0, 0, 'Europe/Moscow'); // = 2021-11-14 23:00:00 UTC
Entry::query()->whereDate('test_date', $value)->get();
// Expected: matches entries on 2021-11-14 (the UTC calendar date of the instant)
// Actual: matches entries on 2021-11-15 instead
Same issue for whereTime():
$value = Carbon::create(2021, 11, 13, 12, 0, 0, 'Europe/Moscow'); // = 09:00:00 UTC
Entry::query()->whereTime('test_date', $value)->get();
// Expected: matches entries at 09:00:00
// Actual: compares against 12:00:00 instead
Logs
Environment
Environment
Laravel Version: 13.18.1
PHP Version: 8.5.7
Composer Version: 2.10.1
Environment: local
Debug Mode: ENABLED
Maintenance Mode: OFF
Timezone: UTC
Locale: en
Cache
Config: NOT CACHED
Events: NOT CACHED
Routes: NOT CACHED
Views: CACHED
Drivers
Broadcasting: log
Cache: file
Database: sqlite
Logs: stack / single
Mail: log
Queue: sync
Session: file
Storage
public/storage: NOT LINKED
Statamic
Addons: 0
License Key: Not set
Sites: 1
Stache Watcher: Enabled (auto)
Static Caching: Disabled
Version: 6.24.1 PRO
Installation
Fresh statamic/statamic site via CLI
Additional details
- Only affects passing an explicit
DateTimeInterface/Carbon in a non-app timezone; plain date/time strings are unaffected since they're already parsed in the app timezone.
- Affects the Stache/flat-file query builder (
src/Query/Builder.php, consumed by IteratorBuilder) — the Eloquent-backed path (EloquentQueryBuilder) delegates straight to Laravel/DB and isn't affected.
- Fix + tests in progress on branch
fix/where-date-time-timezone.
Bug description
Builder::whereDate()andBuilder::whereTime()(src/Query/Builder.php) accept aDateTimeInterfacevalue. Internally they format the value down to a date-only or time-only string, then reparse it:format()reads the components in the value's own timezone, but the reparse (Carbon::parse, or the raw string inwhereTime) uses the app's default timezone (config('app.timezone')). If the passed value is in a different timezone than the app, the calendar digits get reinterpreted as if they were already in the app timezone — silently shifting the represented instant instead of converting it.How to reproduce
App timezone
UTC. Entry hastest_date=2021-11-14 09:00:00(stored/augmented inUTC).Same issue for
whereTime():Logs
Environment
Installation
Fresh statamic/statamic site via CLI
Additional details
DateTimeInterface/Carbonin a non-app timezone; plain date/time strings are unaffected since they're already parsed in the app timezone.src/Query/Builder.php, consumed byIteratorBuilder) — the Eloquent-backed path (EloquentQueryBuilder) delegates straight to Laravel/DB and isn't affected.fix/where-date-time-timezone.