Demo-Project: Managing Timezones for Every User in Laravel

To demonstrate how Laravel works with timezones, we've created a small demo-project. User registration is expanded with Timezone field, and then every user sees the data in their own timezone.

The subject is really simple - event management system with one field: events.start_time, which would be stored in UTC timezone in the database, and then converted to/from timezone when needed.

The project was partly built with our QuickAdminPanel generator, so I won't explain all the code, only the parts that are needed for timezone topic. The whole code is available on Github for reference.


Step 1. Registration form

In the database, we need a timezone column, as a string with default value Europe/London:

Schema::create('users', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
    $table->string('email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('password');
    $table->string('timezone')->default('Europe/London');
    $table->rememberToken();
    $table->timestamps();
});

In app/User.php model we add that as a fillable:

class User extends Authenticatable
{
    use Notifiable;
    protected $fillable = ['name', 'email', 'password', 'remember_token', 'timezone'];
    // ... other model code

In registration view we add it as a dropdown field:

To do that, we use camroncade/timezone package. In app/Http/Controllers/Auth/RegisterController.php we override showRegistrationForm() method, that is originally located in RegistersUsers trait:

/**
 * Show the application registration form.
 *
 * @return \Illuminate\Http\Response
 */
public function showRegistrationForm()
{
    $timezone_select = Timezone::selectForm(
        'Europe/London',
        '',
        ['class' => 'form-control', 'name' => 'timezone']
    );
    return view('auth.register', compact('timezone_select'));
}

And then in resources/views/auth/register.blade.php we just use the variable to show the dropdown:

{!! $timezone_select !!}

Finally, in the same app/Http/Controllers/Auth/RegisterController.php we override create() method to save the timezone field:

/**
 * Create a new user instance after a valid registration.
 *
 * @param  array $data
 * @return User
 */
protected function create(array $data)
{
    $user = User::create([
        'name'     => $data['name'],
        'email'    => $data['email'],
        'password' => bcrypt($data['password']),
        'timezone' => $data['timezone'],
    ]);

    return $user;
}

Step 2. Saving the Event in User's Timezone

In the event adding form, there is a simple datetime picker, without any timezone. So we assume that user enters the time in their own timezone.

When saving the time, we use Laravel Eloquent mutator function, in app/Event.php:

use Camroncade\Timezone\Facades\Timezone;
// ...

/**
 * Set attribute to date format
 * @param $input
 */
public function setStartTimeAttribute($input)
{
    $this->attributes['start_time'] =
        Timezone::convertToUTC($input, auth()->user()->timezone,  'Y-m-d H:i:s');
}

Step 3. Viewing Event in User's Timezone

Final step - viewing the event list in user's timezone. So what if event is entered by one user in London, and want to be viewed by another user in San Francisco?

We use Laravel Eloquent accessor function, again in app/Event.php we add this method:

/**
 * Get attribute from date format
 * @param $input
 *
 * @return string
 */
public function getStartTimeAttribute($input)
{
    return Timezone::convertFromUTC($input, auth()->user()->timezone, 'Y-m-d H:i:s');
}

And that's it, all you need to do now is create a list, so in app/Http/Controllers/Admin/EventsController.php we have this:

public function index()
{
    $events = Event::all();
    return view('admin.events.index', compact('events'));
}

And in resources/views/events/index.blade.php, we have a simple Blade table:

<table class="table table-bordered table-striped">
    <thead>
        <tr>
            <th>@lang('global.events.fields.title')</th>
            <th>@lang('global.events.fields.start-time')</th>
            <th> </th>

        </tr>
    </thead>

    <tbody>
        @if (count($events) > 0)
            @foreach ($events as $event)
                <tr data-entry-id="{{ $event->id }}">
                    <td field-key='title'>{{ $event->title }}</td>
                        <td field-key='start_time'>{{ $event->start_time }}</td>
                        <td>
                            @can('event_view')
                                <a href="{{ route('admin.events.show',[$event->id]) }}" class="btn btn-xs btn-primary">@lang('global.app_view')</a>
                            @endcan
                            @can('event_edit')
                                <a href="{{ route('admin.events.edit',[$event->id]) }}" class="btn btn-xs btn-info">@lang('global.app_edit')</a>
                            @endcan
                            @can('event_delete')
                                {!! Form::open(array(
                                    'style' => 'display: inline-block;',
                                    'method' => 'DELETE',
                                    'onsubmit' => "return confirm('".trans("global.app_are_you_sure")."');",
                                    'route' => ['admin.events.destroy', $event->id])) !!}
                                {!! Form::submit(trans('global.app_delete'), array('class' => 'btn btn-xs btn-danger')) !!}
                                {!! Form::close() !!}
                            @endcan
                        </td>

                </tr>
            @endforeach
        @else
            <tr>
                <td colspan="7">@lang('global.app_no_entries_in_table')</td>
            </tr>
        @endif
    </tbody>
</table>

As a result, you see the list of events, like this:

Repository on Github: https://github.com/LaravelDaily/Laravel-Timezones-Demo

No comments or questions yet...

Like our articles?

Become a Premium Member for $129/year or $29/month
What else you will get:
  • 22 courses (477 lessons, total 38 h 20 min)
  • 2 long-form tutorials (one new every week)
  • access to project repositories
  • access to private Discord