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'); // whereTimeformat() 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 insteadSame 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 insteadLogs
Environment
EnvironmentLaravel Version: 13.18.1PHP Version: 8.5.7Composer Version: 2.10.1Environment: localDebug Mode: ENABLEDMaintenance Mode: OFFTimezone: UTCLocale: enCacheConfig: NOT CACHEDEvents: NOT CACHEDRoutes: NOT CACHEDViews: CACHEDDriversBroadcasting: logCache: fileDatabase: sqliteLogs: stack / singleMail: logQueue: syncSession: fileStoragepublic/storage: NOT LINKEDStatamicAddons: 0License Key: Not setSites: 1Stache Watcher: Enabled (auto)Static Caching: DisabledVersion: 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.