You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
startOfDay() here operates in the timezone of the indexed value (UTC), while $where['value'] sits at midnight of the app timezone. Those are two different instants, and eq() compares absolute instants — so with any non-zero UTC offset the two sides can never line up. Since the indexed side always lands on 00:00 UTC and the comparison side never does, no date matches at all.
filterWhereTime() has the same shape: it takes the H:i:s string built in app timezone and applies it to the UTC value via setTimeFromTimeString(), comparing local wall-clock digits against a UTC instant.
How to reproduce
App timezone Europe/Zurich, a dated collection, one entry at 2026-03-12 00:00 local time (file 2026-03-12-0000.foo.md, augmented to 2026-03-11 23:00:00 UTC).
With a non-UTC app timezone that query returns an empty collection every minute, so EntryScheduleReached never fires. On a site with static caching that means a scheduled entry stays invisible indefinitely — the listing keeps serving the cached version until someone runs static:clear by hand. The scheduler itself is running fine; it just never finds anything.
Logs
Environment
EnvironmentLaravel Version: 12.67.0PHP Version: 8.4.17Composer Version: 2.10.2Environment: localDebug Mode: ENABLEDMaintenance Mode: OFFTimezone: Europe/ZurichLocale: deCacheConfig: NOT CACHEDEvents: NOT CACHEDRoutes: NOT CACHEDViews: NOT CACHEDDriversBroadcasting: logCache: fileDatabase: sqliteLogs: stack / singleMail: smtpQueue: syncSession: fileStatamicAddons: 1Sites: 2Stache Watcher: Enabled (auto)Static Caching: halfVersion: 6.28.0 PRO
Installation
Existing site, upgraded over time
Additional details
Only affects the Stache/flat-file builder. The Eloquent path delegates to the database and is unaffected.
whereYear(), whereMonth() and whereDay() compare via format() and keep working, which makes this easy to miss.
Passing a plain string works if it reaches eq() as a string (Carbon then parses it in the receiver's timezone), which is why the mismatch is invisible when testing the comparison in isolation.
Bug description
On the Stache/flat-file query builder,
whereDate()andwhereTime()never match anything whenapp.timezoneis set to something other thanUTC.The comparison value is normalised into the app timezone in
src/Query/Builder.php:The indexed values, however, are augmented dates and therefore in UTC.
Stache\Query\Builder::filterWhereDate()then does:startOfDay()here operates in the timezone of the indexed value (UTC), while$where['value']sits at midnight of the app timezone. Those are two different instants, andeq()compares absolute instants — so with any non-zero UTC offset the two sides can never line up. Since the indexed side always lands on 00:00 UTC and the comparison side never does, no date matches at all.filterWhereTime()has the same shape: it takes theH:i:sstring built in app timezone and applies it to the UTC value viasetTimeFromTimeString(), comparing local wall-clock digits against a UTC instant.How to reproduce
App timezone
Europe/Zurich, a dated collection, one entry at2026-03-12 00:00local time (file2026-03-12-0000.foo.md, augmented to2026-03-11 23:00:00 UTC).What the two sides actually hold:
Setting
app.timezoneback toUTCmakes both sides agree and the query works again.Knock-on effect: scheduled entries are never published
HandleEntrySchedulerunsMinuteEntries, which is built onwhereDate()+whereTime():With a non-UTC app timezone that query returns an empty collection every minute, so
EntryScheduleReachednever fires. On a site with static caching that means a scheduled entry stays invisible indefinitely — the listing keeps serving the cached version until someone runsstatic:clearby hand. The scheduler itself is running fine; it just never finds anything.Logs
Environment
Installation
Existing site, upgraded over time
Additional details
whereYear(),whereMonth()andwhereDay()compare viaformat()and keep working, which makes this easy to miss.whereDate()/whereTime()ignore timezone ofCarbon/DateTimeInterfacevalues, shifting comparison instant #14939. That report was about passing aCarbonin a foreign timezone, and the normalisation ontoconfig('app.timezone')that came out of it is exactly the line that now collides with the UTC-based index values. The environment there wasTimezone: UTC, so this case wouldn't have shown up.eq()as a string (Carbon then parses it in the receiver's timezone), which is why the mismatch is invisible when testing the comparison in isolation.