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();