Laravel

Get Last Week’s Data in Laravel: Carbon Week Query Tutorial

In Laravel, "last week" doesn't mean "seven days ago." It means the previous calendar week—Monday through Sunday. If you're building weekly reports, dashboards, or any feature that depends on calendar boundaries, this distinction trips up a lot of developers.

Carbon, Laravel's date extension, makes week-based queries clean once you understand the logic. This guide walks through exactly how to pull records from the previous calendar week using Carbon, with practical examples you can drop into your own projects.

The short version

  • Carbon::now()->subWeek() returns 7 days ago, not last week
  • Chain startOfWeek() and endOfWeek() to get Monday-Sunday boundaries
  • WhereBetween filters records within a date range
  • Week start day respects Carbon locale settings
  • The same pattern works for months, quarters, and years

The week vs. Days confusion

Let's say today is June 7 (a Tuesday). If you need "last week" data for a report:

  • Using `subWeek()` alone = June 1, exactly 7 days back
  • Using `subWeek()->startOfWeek()` = May 30, the Monday of the previous calendar week
  • Using `subWeek()->endOfWeek()` = June 5, the Sunday of that same week

For business reporting, calendar weeks are almost always what stakeholders expect. A report showing "last 7 days" and "last week" look different, and mixing them up leads to confusing conversations with clients or managers.

Building the query

Here's the complete example from the reference material, annotated:

“`php

Use Carbon\Carbon;

// Today: June 7, 2022

$startWeek = Carbon::now()->subWeek()->startOfWeek(); // May 30, 2022 (Monday)

$endWeek = Carbon::now()->subWeek()->endOfWeek(); // June 5, 2022 (Sunday)

$orders = Order::query()

->whereBetween('created_at', [$startWeek, $endWeek])

->get();

Dd($orders);

“`

The `whereBetween` method includes records where the `created_at` timestamp falls within your start and end dates. This gives you every order from the previous Monday through Sunday.

Why Carbon beats raw PHP for this

Raw PHP date functions work, but they're verbose. To get the same result with vanilla PHP:

“`php

$today = new DateTime();

$sevenDaysAgo = $today->modify('-1 week');

$startWeek = clone $sevenDaysAgo;

While ($startWeek->format('N') != 1) {

$startWeek->modify('-1 day');

}

$endWeek = clone $startWeek;

$endWeek->modify('+6 days');

“`

That's roughly 15 lines of code doing what Carbon does in two. Beyond brevity, Carbon handles edge cases like leap years, timezone transitions, and locale-specific week starts automatically.

Handling different week start configurations

Some regions start weeks on Sunday. Carbon respects your application locale, but you can also set week boundaries explicitly:

“`php

// Sunday as first day of week

$startWeek = Carbon::now()->subWeek()->startOfWeek(Carbon::SUNDAY);

$endWeek = Carbon::now()->subWeek()->endOfWeek(Carbon::SATURDAY);

“`

Passing the day constant removes any ambiguity when your codebase serves users in different locales.

Our analysis

Date boundary bugs cause more production incidents than most developers expect. "Last week" means different things to different people, and unless your team has explicitly documented the convention, you'll eventually ship a report that off-by-one errors its way into a Monday morning meeting.

The fix isn't just understanding Carbon—it's writing tests that lock in your date behavior. Mock the current date in your test suite and assert that your query returns exactly the records you expect. Without that safety net, refactoring or upgrading Laravel can silently change date behavior in ways that only surface when someone notices discrepancies in historical reports.

Another consideration: timezone handling. If your server runs UTC but your users are in different timezones, `Carbon::now()` returns UTC time. If orders are stored in local time, you may need to adjust for that offset, especially around daylight saving transitions where a day might be 23 or 25 hours long.

Practical example

Say you're building a weekly order dashboard for a finance team. Every Monday, they want to review what was ordered the previous week to reconcile payments and flag any issues.

You write the Carbon query, deploy it, and it works fine for months. Then one Monday, the report shows orders from June 1-7 instead of May 30-June 5. The team notices the discrepancy because their spreadsheet has the "correct" numbers, and suddenly you're debugging a date boundary bug instead of working on new features.

This happens when developers use `subWeek()` without realizing it returns 7 days ago, not the previous calendar week. The fix takes two method calls: `startOfWeek()` and `endOfWeek()`.

Extending the pattern

The same approach works for other date periods:

  • Last month: `subMonth()->startOfMonth()` and `subMonth()->endOfMonth()`
  • Last quarter: `subQuarter()->startOfQuarter()` and `subQuarter()->endOfQuarter()`
  • Last year: `subYear()->startOfYear()` and `subYear()->endOfYear()`

Carbon's fluent interface makes chaining these methods intuitive once you understand the base pattern.

Bottom line

Carbon makes calendar week queries straightforward. Remember the key rule: `subWeek()` alone gives you days, not weeks. Add `startOfWeek()` and `endOfWeek()` to get actual Monday-through-Sunday boundaries. Test your date queries with fixed timestamps to prevent boundary bugs from reaching production.

Key takeaways

  • Always use startOfWeek() and endOfWeek() with subWeek() for calendar week queries
  • Test date logic with Carbon::setTestNow() to catch boundary bugs before deployment
  • Explicitly pass week start constants when your application serves multiple locales
  • The same pattern extends to months, quarters, and years using corresponding methods

Frequently asked questions

How do I get the current week instead of last week?

Use `Carbon::now()->startOfWeek()` for the Monday boundary and `Carbon::now()->endOfWeek()` for Sunday. Remove the `subWeek()` call.

Can I query records from a specific week number?

Yes, use `whereYear('created_at', $year)->whereWeek('created_at', $weekNumber)` to filter by year and ISO week number.

What if my server timezone differs from my users' timezones?

Set your application timezone in config/app.php. For user-specific timezones, use Carbon::setLocale() or pass timezone explicitly to Carbon methods.

How do I test week-based queries?

Use Carbon::setTestNow() to freeze time to a known date, then assert your query returns expected records for that mocked time.

Can I use this pattern with raw SQL instead of Eloquent?

Yes, but you'll need database-specific functions like DATE_SUB() in MySQL or INTERVAL in PostgreSQL. Carbon handles portability automatically, making it the cleaner choice for Laravel projects.

Related