Carbon::setTimezone

Converts the instance to another timezone, changing the displayed clock time but not the underlying instant.

Signature
public function setTimezone(DateTimeZone|string|int $timeZone): static
  • Added in Laravel Carbon 1.0.0

Parameters

ParameterTypeDescription
$timeZoneDateTimeZone|string|intA DateTimeZone object, a timezone identifier or offset string such as 'Asia/Tokyo' or '+02:00', or an integer read as a whole-hour offset.

Return value

staticThe instance rendered in the new timezone — the same object for mutable Carbon, a new object for CarbonImmutable.

Errors

Carbon\Exceptions\InvalidTimeZoneException when the value is not a recognised timezone, or when a numeric offset exceeds CarbonTimeZone::MAXIMUM_TIMEZONE_OFFSET (99).

Changelog

  • PHP Carbon 3.0The parameter gained the native type DateTimeZone|string|int, so passing null now raises a TypeError instead of being coerced. The value is resolved through CarbonTimeZone::instance(), which turns an integer into a whole-hour offset.

setTimezone() re-renders one instant in a different zone. The timestamp is untouched — only the wall-clock fields you read back move. timezone() and tz() are aliases, and utc() is shorthand for setTimezone('UTC').

When to reach for it

The normal Laravel pattern is to store everything in UTC and convert only at the edges. Call setTimezone($user->timezone) just before formatting a date for display, or before grouping timestamps into per-day buckets that must match what the user sees on their calendar.

What trips people up

Mutation. Illuminate\Support\Carbon extends the mutable Carbon\Carbon, so setTimezone() rewrites the object in place and returns $this. Calling it on a model attribute inside a loop or a Blade partial changes that attribute for every later read in the request. Use ->copy()->setTimezone(...), or switch the application to CarbonImmutable via Date::use().

Confusing it with shiftTimezone. setTimezone() keeps the instant and moves the clock reading; shiftTimezone() keeps the clock reading and moves the instant. If a converted date is off by exactly the offset, you reached for the wrong one.

An integer argument is an hour offset, not a day number: setTimezone(2) means +02:00.

Example

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

When to use it

  • Render a UTC-stored appointment time in the signed-in user's own timezone.
  • Convert an incoming API timestamp to UTC before persisting it.
  • Group event timestamps into per-day buckets that match the viewer's calendar.
  • Print a deploy log in the on-call engineer's local time without re-querying.

Frequently asked questions

How do I convert a UTC date to a user's timezone in Laravel?

Call setTimezone() with the user's timezone identifier — $date->copy()->setTimezone($user->timezone) — then format it. The underlying instant is unchanged; only the clock fields you read back move. Always copy first unless the instance is a CarbonImmutable, because Laravel's default Carbon is mutable.

What is the difference between setTimezone and shiftTimezone?

setTimezone keeps the same instant and changes the displayed clock time, while shiftTimezone keeps the displayed clock time and changes the instant. Use setTimezone to show a stored moment in another region, and shiftTimezone only to re-tag wall-clock digits that arrived labelled with the wrong zone.

Why did setTimezone change my model attribute everywhere?

Because Illuminate\Support\Carbon is mutable, so setTimezone rewrites the object in place and returns the same instance rather than a new one. Insert ->copy() before the call, or make the whole application immutable by calling Date::use(CarbonImmutable::class) in a service provider's boot method.

More examples

setTimezone versus shiftTimezone

setTimezone keeps timestamp 1773513000 and moves the reading to 2026-03-15 03:30:00. shiftTimezone keeps the reading 2026-03-14 18:30:00 and moves the timestamp back nine hours to 1773480600 — it reinterprets the same digits as Tokyo local time. Reach for shiftTimezone only when a source system sent you wall-clock digits that were tagged with the wrong zone.

Example · php
<?php

use Illuminate\Support\Carbon;

date_default_timezone_set('UTC');

$a = Carbon::parse('2026-03-14 18:30:00', 'UTC');
$b = Carbon::parse('2026-03-14 18:30:00', 'UTC');

$a->setTimezone('Asia/Tokyo');
$b->shiftTimezone('Asia/Tokyo');

echo 'setTimezone   ', $a->toDateTimeString(), '  ts=', $a->getTimestamp(), PHP_EOL;
echo 'shiftTimezone ', $b->toDateTimeString(), '  ts=', $b->getTimestamp(), PHP_EOL;

Mutable in place, immutable by copy

The first var_dump prints bool(true): the mutable instance returned itself and the original variable now reads 2026-03-15 03:30:00. The second prints bool(false) and the immutable original still reads 2026-03-14 18:30:00. This is the difference behind most "the time changed after I displayed it" reports.

Example · php
<?php

use Carbon\CarbonImmutable;
use Illuminate\Support\Carbon;

date_default_timezone_set('UTC');

$mutable  = Carbon::parse('2026-03-14 18:30:00', 'UTC');
$returned = $mutable->setTimezone('Asia/Tokyo');

var_dump($returned === $mutable);
echo $mutable->toDateTimeString(), PHP_EOL;

$immutable = CarbonImmutable::parse('2026-03-14 18:30:00', 'UTC');
$converted = $immutable->setTimezone('Asia/Tokyo');

var_dump($converted === $immutable);
echo 'original: ', $immutable->toDateTimeString(), PHP_EOL;
echo 'converted: ', $converted->toDateTimeString(), PHP_EOL;

Displaying a stored UTC time to a user

starts_at arrives as a mutable Carbon in the application timezone. The ->copy() is the load-bearing part: without it, $booking->starts_at is itself converted, and any later code in the request — a notification, an audit log, a second Blade partial — reads the shifted value. This snippet needs a booted application with config, a database and a request.

Example · php
<?php

namespace App\Http\Controllers;

use App\Models\Booking;
use Illuminate\Http\Request;

class BookingController extends Controller
{
    public function show(Request $request, Booking $booking)
    {
        $zone = $request->user()?->timezone ?? config('app.timezone');

        $localStartsAt = $booking->starts_at->copy()->setTimezone($zone);

        echo $localStartsAt->format('D j M Y, g:ia T'), PHP_EOL;

        return view('bookings.show', compact('booking', 'localStartsAt'));
    }
}

Discussion

  • Be the first to comment on this lesson.