Carbon::subDays

Subtracts a number of days from a Carbon instance, mutating it in place.

Signature
public function subDays(int|float $value = 1): static
  • Added in Laravel Carbon 1.0

Parameters

ParameterTypeDescription
$$valueoptionalint|float= 1Number of days to subtract. Negative values move forward; a fractional value subtracts the corresponding part of a day.

Return value

staticThe same instance, moved backward by the given number of days ($this for the mutable Carbon class, a new instance for CarbonImmutable).

Errors

Throws Carbon\Exceptions\UnitException when the value cannot be interpreted as a number of days.

Changelog

  • PHP 3.0Documented as subDays(int|float $value = 1); resolved through Carbon's magic __call and delegates to subUnit('day', ...).

subDays() is the mirror of addDays(): it walks the calendar backwards using a date interval, so wall-clock time survives a daylight-saving boundary. Its most common job is a cutoff — the oldest record worth keeping, the start of a rolling window, the point beyond which a token is stale.

Pair it with a boundary

now()->subDays(30) keeps the current time of day, which makes a query's result depend on the minute it runs and makes tests flaky. For anything reported to a user, add startOfDay() or start from today() so the cutoff lands on a stable midnight.

Same mutability trap

Carbon instances are mutable. $window = $start; $window->subDays(7); moves $start too, because both names point at one object. Use copy(), or chain straight from a factory call such as now(), which always returns a fresh instance.

Negative values

subDays(-2) moves forward two days rather than throwing, which turns an unvalidated user-supplied offset into a date in the wrong direction. Clamp the input rather than relying on the sign.

For month-level cutoffs prefer subMonthNoOverflow(): plain subMonth() on 31 March lands on 3 March.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Building a cutoff for pruning records older than a retention window.
  • Defining the lower bound of a last-N-days dashboard query.
  • Checking whether a password or token was issued more than N days ago.

Frequently asked questions

Does subDays modify the instance it is called on?

Yes — Carbon\Carbon and Illuminate\Support\Carbon are mutable, so the original date moves. Call ->copy() before subtracting, or use CarbonImmutable, when you need to keep the starting value.

Should I use now()->subDays(30) or today()->subDays(30)?

Use today() when the boundary is a calendar day, since now() keeps the current time and shifts the cutoff every minute the code runs. now() is right only when you genuinely mean thirty times twenty-four hours from this instant.

What does subDays do with a negative number?

It moves the date forward: subDays(-2) is the same as addDays(2), with no exception raised. Validate or clamp the value when it comes from user input.

More examples

Pruning old records

Starting from today() rather than now() means the command deletes the same set whether it runs at 02:00 or 23:00 — the boundary is a midnight, not the moment the scheduler fired.

Example · php
<?php

namespace App\Console\Commands;

use App\Models\AuditLog;
use Illuminate\Console\Command;

class PruneAuditLogs extends Command
{
    protected $signature = 'audit:prune {--days=90}';

    public function handle(): int
    {
        $cutoff = today()->subDays((int) $this->option('days'));

        $deleted = AuditLog::where('created_at', '<', $cutoff)->delete();

        $this->info("Deleted {$deleted} entries older than {$cutoff->toDateString()}.");

        return self::SUCCESS;
    }
}

A last-seven-days series

Counting the offset down to zero produces the buckets in chronological order, which is what a chart needs. Each iteration calls today() again, so no instance is mutated between rounds.

Example · php
<?php

use Carbon\Carbon;

Carbon::setTestNow('2026-03-14 09:41:07');

$days = [];

foreach (range(6, 0) as $offset) {
    $day = today()->subDays($offset);
    $days[$day->toDateString()] = $day->format('D');
}

print_r($days);

// Array
// (
//     [2026-03-08] => Sun
//     [2026-03-09] => Mon
//     [2026-03-10] => Tue
//     [2026-03-11] => Wed
//     [2026-03-12] => Thu
//     [2026-03-13] => Fri
//     [2026-03-14] => Sat
// )

Carbon::setTestNow();

Staleness check on a token

Comparing against a computed cutoff reads more directly than a diff, and it avoids the signed-float surprise of diffInDays. Each now() call builds a fresh instance, so the two cutoffs do not interfere.

Example · php
<?php

use Carbon\Carbon;

Carbon::setTestNow('2026-03-14 09:41:07');

$issuedAt = Carbon::parse('2026-02-01 08:00:00');

var_dump($issuedAt->lessThan(now()->subDays(30)));  // bool(true)
var_dump($issuedAt->lessThan(now()->subDays(60)));  // bool(false)

Carbon::setTestNow();

Discussion

  • Be the first to comment on this lesson.