Reader Stacks

Comparing Dates in a Laravel Eloquent Where Query

whereDate, whereBetween, and raw Carbon comparisons in Eloquent — which one to use and the timezone mistake that causes off-by-one-day bugs.

Comparing Dates in a Laravel Eloquent Where Query

Eloquent has several ways to filter by date, and picking the wrong one is a common source of subtle bugs — usually an off-by-one-day error caused by timezone handling, not the query method itself.

Exact date match: whereDate()

Use whereDate() when you want every row on a specific calendar date, regardless of the time portion:

Order::whereDate('created_at', '2026-09-01')->get();

This is equivalent to DATE(created_at) = '2026-09-01' at the SQL level, and correctly ignores the time component — you don't need to build a start/end-of-day range yourself for this case.

A date range: whereBetween()

Order::whereBetween('created_at', [
    now()->subDays(7)->startOfDay(),
    now()->endOfDay(),
])->get();

For ranges, pass full Carbon instances rather than date-only strings — whereBetween does a straightforward comparison, so a bare '2026-09-01' string is treated as midnight, silently excluding everything from later that same day.

Relative comparisons

Order::where('created_at', '>=', now()->subDays(30))->get();

The timezone bug

If created_at is stored in UTC (Laravel's default) but your application's display timezone is different, whereDate('created_at', today()) compares against UTC "today," which can be a different calendar day than the user's local "today" for several hours around midnight. Two fixes: convert the boundary to UTC explicitly before querying, or store and compare consistently in UTC and only convert to local time for display, never for the query itself.

$startOfDayUtc = now('Europe/Kyiv')->startOfDay()->setTimezone('UTC');
$endOfDayUtc = now('Europe/Kyiv')->endOfDay()->setTimezone('UTC');

Order::whereBetween('created_at', [$startOfDayUtc, $endOfDayUtc])->get();
Topics: Database Queries & Eloquent