Blog Detail

22

Dec
Store UTC Dates in DB & Work with Custom Timezone in Laravel cover image

arrow_back Store UTC Dates in DB & Work with Custom Timezone in Laravel

Dealing with time zones can be a frustrating experience. Here’s an attempt to brighten your day.

The problem: it is commonly agreed that dates should be stored as UTC date-times in the database, which generally means they also need to be adapted for the local timezone before manipulation or display. Laravel provides a app.timezone configuration, making it possible to start working with timezones. However, changing that configuration will affect both the stored and manipulated date’s time zones. This package tries to address this by providing a timezone conversion mechanism that should perform most of the repetitive timezone configurations out of the box.

Getting started

The app.timezone configuration setting has to be set to the timezone that should be used when saving dates in the database. We highly recommend keeping it as UTC since it’s a global standard for dates storage.

For in-app date manipulation and display, one would expect more flexibility. That’s why it is possible to set the application’s timezone dynamically by updating the timezone singleton instance. Depending on the app’s context, please choose one that suits your situation best:

1. Using middleware

Useful when the app’s timezone should be set by the user’s settings.

namespace App\Http\Middleware;
 
use Closure;
use Whitecube\LaravelTimezones\Facades\Timezone;
 
class DefineApplicationTimezone
{
    public function handle($request, Closure $next)
    {
        Timezone::set($request->user()->timezone ?? 'Europe/Brussels');
 
        return $next($request);
    }
}

2. Using a Service Provider

Useful when the app’s timezone should be set by the application itself. For instance, in App\Providers\AppServiceProvider:

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Whitecube\LaravelTimezones\Facades\Timezone;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Timezone::set('America/Toronto');
    }
}

Usage

Once everything’s setup, the easiest way to manipulate dates configured with the app’s current timezone is to use the TimezonedDatetime or ImmutableTimezonedDatetime cast types on your models:

use Whitecube\LaravelTimezones\Casts\TimezonedDatetime;
use Whitecube\LaravelTimezones\Casts\ImmutableTimezonedDatetime;

/**
 * The attributes that should be cast.
 *
 * @var array
 */
protected $casts = [
    'published_at' => TimezonedDatetime::class,
    'birthday' => ImmutableTimezonedDatetime::class . ':Y-m-d',
]

In other scenarios, feel free to use the Timezone Facade directly for more convenience:

use Carbon\Carbon;
use Whitecube\LaravelTimezones\Facades\Timezone;

// Get the current date configured with the current timezone:
$now = Timezone::now();

// Create a date using the current timezone:
$date = Timezone::date('2023-01-01 00:00:00');
// Alternatively, set the timezone manually on a Carbon instance:
$date = new Carbon('2023-01-01 00:00:00', Timezone::current());


// Convert a date to the current timezone:
$date = Timezone::date(new Carbon('2023-01-01 00:00:00', 'UTC'));
// Alternatively, set the application timezone yourself:
$date = (new Carbon('2023-01-01 00:00:00', 'UTC'))->setTimezone(Timezone::current());

// Convert a date to the storage timezone:
$date = Timezone::store(new Carbon('2023-01-01 00:00:00', 'Europe/Brussels'));
// Alternatively, set the storage timezone yourself:
$date = (new Carbon('2023-01-01 00:00:00', 'Europe/Brussels'))->setTimezone(Timezone::storage());

Assigning values to cast attributes

Many developers are used to assign Carbon instances to date attributes:

$model->published_at = Carbon::create($request->published_at);

This can lead to unexpected behavior because the assigned Carbon instance will default to the UTC timezone, whereas the provided value was probably meant for another timezone. The datetime string will be stored as-is without shifting its timezone accordingly first.

In order to prevent this, it is recommended to let the Cast do the heavy lifting:

$model->published_at = $request->published_at;

The package will now treat the provided datetime string using the correct Timezone (for instance, Europe/Brussels) and store the shifted UTC value in the database correctly.

A more verbose (but also correct) method would be to create the Carbon instance using the Timezone facade :

$model->published_at = Carbon::create($request->published_at, Timezone::current());
// Or, shorthand:
$model->published_at = Timezone::date($request->published_at);

This is not a bug, it is intended behavior since one should be fully aware of the Carbon instance’s timezone before assigning it.

For more details, please visit Github.

Published at : 22-12-2022

Author : Rizwan Aslam
AUTHOR
Rizwan Aslam

I am a highly results-driven professional with 12+ years of collective experience in the grounds of web application development especially in laravel, native android application development in java, and desktop application development in the dot net framework. Now managing a team of expert developers at Codebrisk.

Launch your project

Launch project