Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

robjbrain's avatar

Eloquent Carbon and displaying dates in a users local timezone

I want to be able to display dates in the users local timezone, so if someone submits a comment in the UK at 2pm someone viewing that comment in Italy will see the time showing as 3pm. If the Italian user then submitted a reply 5 minutes later it would show as 3.05pm for him and 2.05pm for the user in the UK... fairly elementary.

I have the users timezone saved in the database accessible via Auth::user()->timezone

In the past I always saved dates as integer timestamps in the database and used date_default_timezone_set() when getting the user object and then date(d-m-Y H:i:s, $model->timestamp) would display in the users local timezone.

But now that i've tried to follow Laravel "standard" practice, use carbon and save as timestamps in the DB, I can't figure out how to get the date displaying relative to the users local timezone.

So lets say I have a model with the timestamp saved in the database like this: "2015-10-17 13:56:26"

I am using date mutators in Eloquent so this column will be converted to a carbon instance.

Now if I use:

date_default_timezone_set(Auth::user()->timezone);

$model->timestamp->format('d-m-Y H:i:s')

It will always display the exact same date, exactly what it says in the database, no matter what was set in the timezone.

But it does have an effect when saving dates e.g.

Model::create(array(['timestamp'=>Carbon::now()]);

This will save the date in the database itself as 3pm for the Italian and 2pm for the guy in the UK, even when they're both saving at the exact same time. This is exactly the opposite of what I wanted!!!! If I did this I would be saving dates in the database in all kinds of different timezones, horrendous, I definitely want to avoid that.

I've given a bit of detail in the hope someone will see my current train of thought and tell me why i'm being an idiot, but the question is simple:

Using Eloquent Date Mutators and Carbon, how can I save all dates in the database in Europe/London time, but whenever i'm displaying dates use a custom timezone set in the Users model?

0 likes
13 replies
robjbrain's avatar

I don't understand what you mean? It says on the second line the users timezone is stored in the database.

Any other storing of dates would be things like comment->time_added so i'm not sure what you're suggesting to store in a field in the database?

If you do mean store the users timezone in the users table, as I am doing, then the answer "store it in the database and use that" is literally just a rephrasing of my question which is "I have the users timezone stored, how do I use it", but not actually an instructive answer.

I'm not sure if i've misunderstood you or if you're reply has no relevance because as you said you didn't actually read my post. Either way i'm confused by your reply.

bobbybouwmann's avatar

@jlrdw Seriously? He just says that he had the local timezone in the database.. Please don't reply if you don't read! How is the helping the OP?

Now for your problem you can set the timezone after you set the format

$date = Carbon::createFromFormat('Y-m-d H:i:s', '2015-10-23 17:64:00', 'Europe/Amsterdam');
$date->setTimezone('UTC');

You can for example create a helper function to make this easy in your views ;)

9 likes
Francismori7's avatar

When echo'ing the time, do this:

{{ $model->created_at->setTimezone(Auth::user()->timezone)->toDateTimeString() }}
5 likes
jekinney's avatar

@jlrdw date time stores as the imputed date time not utc time. If not careful you could end up with more issues trying to convert to a users local time. Timestamp sets the time stamp on a universal way that is easier to manipulate.

Francismori7's avatar

@jekinney Laravel stores the timestamps as DATETIME properly using the default timezone (set in config/app.php) in all cases. If you default timezone is UTC (as per the default Laravel install), you will always have the UTC data in your database.

There is nothing wrong with DATETIME here.

Snapey's avatar

This is correct for actions performed on the system where the system itself determines what time the event occurs. If the user enters a time then this should ideally be converted to local system time, and then converted back in order to display to the user. It really depends if the field forms part of the business rules.

For instance, if I schedule a report to run at 9am, (by entering that in a date/time field) and I happen to be in a different timezone then it will need to be converted to the servers time so that the server knows when to send the report.

I prefer all the times on the server to be in the server's time (however they were created) and then converted when rendered out

jekinney's avatar

@Francismori7

Hate to call you out but it defiantly sets timestamp and not datetime. The fact that Laravel manipulates it, yes, but you still have to keep in mind what MySQL does when not manipulated (i.e. Query builder insert or using raw and/or straight MySQL).

Francismori7's avatar

@jekinney No offense taken - I just took a look in my database and yes! You are right, they are indeed TIMESTAMPS. Sorry.

1 like
robjbrain's avatar

Sorry for the delayed reply, it seems I stopped getting email notifications for some reason.

It seems the recommended solution is to set timezone manually each you want to display a date?

Obviously this is time consuming so we want a helper function such as:

Time Published: {{ show_date($article->time_published, 'd-m-Y H:i:s') }}

show_date($date, $format) {
    return $date->setTimezone(Auth::user()->timezone)->format($format);
}

Or even better to handle all possibilities:

show_date($date, $format) {
    if(!($date instanceof Carbon)) {
        if(is_numeric($date)) {
             // Assume Timestamp
             $date = Carbon::createFromTimestamp($date);
        } else {
            $date = Carbon::parse($date);
        }
    }

    return $date->setTimezone(Auth::user()->timezone)->format($format);
}

Is that generally the best implementation then?

Possibly a blade syntax such as @date() might be better, but I haven't had the time to look into extending blade yet.

2 likes
jcgivens21's avatar

I spent several hours trying to figure out what I was doing wrong, as I kept getting the error message:

"FatalErrorException in 573ab1ec620aca0ede57c90e557ae02937b375bf.php line 16:
Call to a member function setTimezone() on string"

I tried so many different things that I'm embarassed to even mention some of the things I did. When it all resolved and I had figured out my issue, what I found was that you CAN NOT use ->setTimezone after you use ->format. If I put ->setTimezone before using ->format, it works perfectly.

In my code, I had a blade file that pulled out all $users as $user with a foreach loop, and the WORKING CODE ended up like this:

    @foreach ($company->users as $user)
        {{--*/
            $date = Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $user->last_login_time, 'UTC')->setTimezone('America/Chicago')->format('F d, Y @ h:i:s A T');
        /*--}}

        {{ $date }}
@endforeach     

You can ignore the $company-> part, as it's specific to my application. The above code was located in a blade file in the @section('content') section. I used {{--/ insert-code-here /--}} for this because I was performing PHP within the blade file. You can do this with <?= insert-code-here ?> as well, but it will print the php code to the page once it loads. So to prevent that from happening, use the bracketed method.

I hope this helps another newbie like me so they don't spend so many hours looking into this. *Note: For reference, my \app\config\app.php file has the app timezone set to 'UTC' to avoid DST issues, and dates that I enter into the database are also in UTC for timestamp accuracy. My goal was to print something in a specific timezone (Central Standard Time, which I called with the 'America/Chicago' timezone in the code above, while also displaying it in an updated format).

Yamen's avatar

I've displayed the the local time by saving the timezone in session variable with help of moment.js, here is the steps:

1 - use momentjs cdn (or with npm as you like)

<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.0/moment.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.13/moment-timezone-with-data.js"></script>

2 - In login.blade.php make a hidden input within login form

<input type="hidden" name="timezone" id="timezone">

and push this script

var timezone = moment.tz.guess();
$('#timezone').val(timezone);

now we have a timezone variable in the login request.

3 - We'll access this variable in AuthenticatesUsers trait in LoginController

/**
     * The user has been authenticated.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  mixed  $user
     * @return mixed
     */
    protected function authenticated(Request $request, $user)
    {
        session(['timezone' => $request->timezone]); // saving to session
    }

4 - Finally to view any date as local time just add

$date->timezone(session('timezone'));

Note: Make sure that $data is a Carbon instance, otherwise you'll get using timezone method on string error, in this case just parse it Carbon::parse($date)->timezone(session('timezone'));

1 like
mccaulay's avatar

Still not sure if there is a better way to do it, so Im using a variation of this.

Helper Function

/**
 * Convert the given date to the $timezone or logged in users timezone.
 *
 * @param mixed $date A Carbon date or a parsable date format.
 * @param string $timezone A PHP timezone. If null it will use the logged in users timezone.
 * @return string The localized date.
 */
function localizeDate($date, $timezone = null)
{
    if (!($date instanceof Carbon)) {
        $date = is_numeric($date) ? Carbon::createFromTimestamp($date) : Carbon::parse($date);
    }

    return $date->setTimezone($timezone ?? (Auth::check() ? Auth::user()->timezone : 'UTC'));
}

/**
 * Convert the given date to the $timezone or logged in users timezone.
 * Then format it to the given format.
 *
 * @param mixed $date A Carbon date, array of parameters or a parsable date format.
 * @param string $format A PHP date time format.
 * @param string $timezone A PHP timezone. If null it will use the logged in users timezone.
 * @return string The formatted date.
 */
function localizeDateFormat($date, $format = 'jS M Y, g:ia', $timezone = null)
{
    // Support array input as primary arg
    if (is_array($date) && !empty($date)) {

        // If format exists
        if (count($date) >= 2) {
            $format = $date[1];
        }

        // If timezone exists
        if (count($date) >= 3) {
            $timezone = $date[2];
        }

        $date = $date[0];
    }

    return localizeDate($date, $timezone)->format($format);
}

Blade Directive (AppServiceProvider.php)

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Blade::directive('datetime', function ($expression) {
            return "<?php echo e(localizeDateFormat(" . $expression . ")); ?>";
        });
    }

Using Blade Directive

@datetime(Auth::user()->created_at)

Using Blade Directive (Custom Format)

@datetime([Auth::user()->created_at, 'd-m-Y H:i:s'])
jasonomarion's avatar

@robjbrain I did it by getters in Model. For example in Order Model:

    private function localTimezone($value = null)
    {
        return $value ? Carbon::parse($value)->setTimezone(auth()->user()->timezone ?? 'UTC')->toDateTimeString() : null;
    }

    public function getCreatedAtAttribute($value)
    {
        return $this->localTimezone($value);
    }

    public function getUpdatedAtAttribute($value)
    {
        return $this->localTimezone($value);
    }

    public function getBlockedAtAttribute($value)
    {
        return $this->localTimezone($value);
    }

In User model I have a timezone column which fills on user registration. Now when Laravel returns $order or paginated $orders variable it automatically converts dates for authenticated user's timezone. It is very convenient!

2 likes

Please or to participate in this conversation.