How to Manage User Timezone in Laravel 11

Websolutionstuff | Jan-09-2025 | Categories : Laravel PHP

Managing timezones in Laravel applications can be crucial when you have users from different regions. In this article, I’ll walk you through a step-by-step process to manage user-specific timezones in Laravel 11.

We’ll set up authentication with Laravel UI, add a timezone column to the users table, and dynamically display timestamps based on the user’s timezone. Along the way, I’ll show you how to create a profile page for updating timezones and a posts page that displays "created at" dates tailored to each user.

Let’s make your application timezone-friendly and user-centric!

How to Manage User Timezone in Laravel 11

 

Step 1: Install Laravel 11

In this step, we'll install laravel 11 application using the following command.

composer create-project laravel/laravel example-app

 

Step 2: Create Auth using Scaffold

Then, we'll install laravel UI and also install laravel scaffold auth using the following command.

composer require laravel/ui 
php artisan ui bootstrap --auth 
npm install
npm run build

 

Step 3: Create Migrations

Next, we'll create a migration using the following command.

php artisan make:migration add_timezone_column_users_table
php artisan make:migration create_posts_table

Migration:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('timezone')->nullable();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        //
    }
};
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('body');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

Migrate the table into the database using the following command.

php artisan migrate

 

Step 4: Create HandlesTimezones Trait

Now, create the Traits folder in the app and create the HandlesTimezones.php file.

app/Traits/HandlesTimezones.php

<?php

namespace App\Traits;

use Auth;
use Carbon\Carbon;

trait HandlesTimezones{

    /**
     * Write code on Method
     *
     * @return response()
     */
    public function getCreatedAtAttribute($value)
    {
        return $this->convertTimeZoneToUserTimezone($value);
    }

    /**
     * Write code on Method
     *
     * @return response()
     */
    public function getUpdatedAtAttribute($value)
    {
        return $this->convertTimeZoneToUserTimezone($value);
    }

    /**
     * Write code on Method
     *
     * @return response()
     */
    protected function convertTimeZoneToUserTimezone($value){
        $timezone = Auth::check() & Auth::user()->timezone ? Auth::user()->timezone : config("app.timezone");

        return Carbon::parse($value)->timezone($timezone)->format("Y-m-d H:i:s");
    }

}

 
Step 5: Create and Update Models

Next, we'll create a model using the following command.

php artisan make:model Post

app/Models/Post.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use App\Traits\HandlesTimezones;

class Post extends Model
{   
    use HandlesTimezones;
    
    protected $fillable = ["title", "body"];
}

app/Models/User.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'email',
        'password',
        'timezone'
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }
}

 
Step 6: Create Routes

Then, we'll define the routes in the web.php file.

routes/web.php

<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');

Route::get('/posts', [App\Http\Controllers\PostController::class, 'index']);
Route::post('/posts', [App\Http\Controllers\PostController::class, 'store'])->name("posts.store");

Route::get('/profile', [App\Http\Controllers\ProfileController::class, 'profile']);
Route::post('/profile', [App\Http\Controllers\ProfileController::class, 'profileUpdate'])->name("profile.update");

 
Step 7: Create Controller

Now, we'll create a controller and add the following code to that file.

app/Http/Controllers/PostController.php

<?php

namespace App\Http\Controllers;

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

class PostController extends Controller
{
    /**
     * Write code on Method
     *
     * @return response()
     */
    public function index(Request $request)
    {
        $posts = Post::latest()->get();
        return view("posts", compact('posts'));
    }

    /**
     * Write code on Method
     *
     * @return response()
     */
    public function store(Request $request)
    {
        Post::create([
            "title" => $request->title,
            "body" => $request->body,
        ]);

        return back()->with('success', "post created.");
    }
}

app/Http/Controllers/ProfileController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ProfileController extends Controller
{
    /**
     * Write code on Method
     *
     * @return response()
     */
    public function profile(Request $request)
    {
         $timezones = [
            'Asia/Kolkata' => 'Asia/Kolkata',
            'America/New_York' => 'America/New_York',
            'Europe/London' => 'Europe/London',
            'Asia/Tokyo' => 'Asia/Tokyo',
            'Australia/Sydney' => 'Australia/Sydney',
        ];
        return view("profile", compact('timezones'));
    }

    /**
     * Write code on Method
     *
     * @return response()
     */
    public function profileUpdate(Request $request)
    {
        $user = auth()->user();

        $user->name = $request->name;
        $user->email = $request->email;
        $user->timezone = $request->timezone;
        $user->save();

        return back()->with('success', "profile updated.");
    }
}

 
Step 8: Create and Update Blade Files

In this step, we'll update the app.blade.php file, create posts, and profile blade file.

resources/views/layouts/app.blade.php

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Fonts -->
    <link rel="dns-prefetch" href="//fonts.bunny.net">
    <link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet">

    <!-- Scripts -->
    @vite(['resources/sass/app.scss', 'resources/js/app.js'])
    
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" />
    @yield('script')
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
            <div class="container">
                <a class="navbar-brand" href="{{ url('/') }}">
                    How to Manage User Timezone in Laravel 11
                </a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
                    <span class="navbar-toggler-icon"></span>
                </button>

                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <!-- Left Side Of Navbar -->
                    <ul class="navbar-nav me-auto">

                    </ul>

                    <!-- Right Side Of Navbar -->
                    <ul class="navbar-nav ms-auto">
                        <!-- Authentication Links -->
                        @guest
                            @if (Route::has('login'))
                                <li class="nav-item">
                                    <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
                                </li>
                            @endif

                            @if (Route::has('register'))
                                <li class="nav-item">
                                    <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
                                </li>
                            @endif
                        @else
                            <li class="nav-item">
                                <a class="nav-link" href="/posts">Posts</a>
                            </li>
                            <li class="nav-item">
                                <a class="nav-link" href="/profile">Profile</a>
                            </li>
                            <li class="nav-item dropdown">
                                <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                                    {{ Auth::user()->name }}
                                </a>

                                <div class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
                                    <a class="dropdown-item" href="{{ route('logout') }}"
                                       onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                                        {{ __('Logout') }}
                                    </a>

                                    <form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
                                        @csrf
                                    </form>
                                </div>
                            </li>
                        @endguest
                    </ul>
                </div>
            </div>
        </nav>

        <main class="py-4">
            @yield('content')
        </main>
    </div>
</body>

</html>

resources/views/posts.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-12">
            <div class="card">
                <div class="card-header">{{ __('Posts') }}</div>

                <div class="card-body">

                    <form method="POST" action="{{ route('posts.store') }}">
                        @csrf
                    
                        @session('success')
                            <div class="alert alert-success">
                                {{ $value }}
                            </div>
                        @endsession
                    
                        <div class="mt-2">
                            <label>Title:</label>
                            <input type="text" name="title" class="form-control">
                            @error('title')
                                <span class="text-danger">{{ $message }}</span>
                            @enderror
                        </div>

                        <div class="mt-2">
                            <label>Body:</label>
                            <textarea class="form-control" name="body"></textarea>
                            @error('body')
                                <span class="text-danger">{{ $message }}</span>
                            @enderror
                        </div>
                    
                        <div class="mt-2">
                            <button class="btn btn-success">Submit</button>
                        </div>
                    </form>

                    <table class="table table-striped table-bordered mt-3">
                        <thead>
                            <tr>
                                <th>ID</th>
                                <th>Title</th>
                                <th>Body</th>
                                <th>Creation Date</th>
                            </tr>
                        </thead>
                        <tbody>
                            @foreach($posts as $post)
                            <tr>
                                <td>{{ $post->id }}</td>
                                <td>{{ $post->title }}</td>
                                <td>{{ $post->body }}</td>
                                <td>{{ $post->created_at }}</td>
                            </tr>
                            @endforeach
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

resources/views/profile.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-12">
            <div class="card">
                <div class="card-header">{{ __('Profile') }}</div>

                <div class="card-body">

                    <form method="POST" action="{{ route('profile.update') }}">
                        @csrf
                    
                        @session('success')
                            <div class="alert alert-success">
                                {{ $value }}
                            </div>
                        @endsession
                    
                        <div class="mt-2">
                            <label>Name:</label>
                            <input type="text" name="name" class="form-control" value="{{ auth()->user()->name }}">
                            @error('name')
                                <span class="text-danger">{{ $message }}</span>
                            @enderror
                        </div>

                        <div class="mt-2">
                            <label>Email:</label>
                            <input type="email" name="email" class="form-control" value="{{ auth()->user()->email }}">
                            @error('email')
                                <span class="text-danger">{{ $message }}</span>
                            @enderror
                        </div>

                        <div class="mt-2">
                            <label>Timezone:</label>
                            <select class="form-control" name="timezone">
                                @foreach($timezones as $key => $label)
                                    <option value="{{ $key }}" {{ auth()->user()->timezone == $key ? 'selected' : '' }}>
                                        {{ $label }}
                                    </option>
                                @endforeach
                            </select>
                            @error('email')
                                <span class="text-danger">{{ $message }}</span>
                            @enderror
                        </div>
                    
                        <div class="mt-2">
                            <button class="btn btn-success">Submit</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
 
Step 9: Run Laravel 11 Application

Now, run the laravel 11 application using the following command.

php artisan serve

 


You might also like:

Recommended Post
Featured Post
How To Delete File From Public Folder In Laravel
How To Delete File From Public...

In this article, we will see how to remove/delete files from public folders. We will give you a demo of how to remo...

Read More

Sep-14-2020

How To Validate Email Using jQuery
How To Validate Email Using jQ...

In this article, we will see how to validate email using jquery. we will use regular expression(regex) for email va...

Read More

Nov-09-2022

Laravel 11 Install Yajra Datatable Example
Laravel 11 Install Yajra Datat...

In this tutorial, we'll explore an example of installing Yajra Datatable in Laravel 11. In every project, it's e...

Read More

Apr-10-2024

Mail: Laravel 11 Send Email using Queue
Mail: Laravel 11 Send Email us...

In this guide, we'll see how to send email using a queue in laravel 11. Here we'll see the concept of queue...

Read More

Apr-12-2024