Manage Timezones in Laravel (UTC to User’s timezone)

Ali
2 min readAug 5, 2019

--

Option 1 — Change on front-end: (Preferred)

The idea is that you always stored your time in UTC in database and then you convert it on front-end; it’s nothing specific to Laravel. Suppose a user registers in London, travel to New York and then come back. In New York we don’t want to present him/her with London time right! So what we do is we query the DB, and change on the front-end. You can make a mixin in Vue or utility function in React like so:

import moment from 'moment'

const timeConverter = {
utcToLocal: (timestamp, format = 'YYYY-MM-DD') => {
return moment.unix(timestamp).local().format(format)
},
localToUTC: (timestamp, format = 'YYYY-MM-DD') => {
return moment(timestamp).utc().format(format)
},
}

export default timeConverter

Option 2— Change on back-end:

Bonus:

https://github.com/brainlet-ali/laravel-convert-timezone

If front-end conversion is not an option for you then read on.

Step 1:

Simply take the user time zone at the time of registration and stored it in users table like so:

<select name="tz" class="form-control m-input"
required>
<option selected value="">
Select
</option>
@foreach (timezone_identifiers_list() as $timezone)
<option value="{{ $timezone }}">{{ $timezone }}</option>
@endforeach
</select>
...
$attributes['tz'] = request('tz');
User::create($attributes);...

Step 2:

Make a trait which will converts the time from UTC to User’s timezone (act as accessor):

trait SetTimeZone
{
public $tz = 'UTC';
public function getTz(){
$this->tz = auth()->user()->tz;
}

public function getCreatedAtAttribute($value){
try {
return (new Carbon($value))->setTimezone(new CarbonTimeZone($this->getTz()));
} catch (\Exception $e) {
return 'Invalid DateTime Exception: '.$e->getMessage();
}
}
public function getUpdatedAtAttribute($value){
try {
return (new Carbon($value))->setTimezone(new CarbonTimeZone($this->getTz()));
} catch (\Exception $e) {
return 'Invalid DateTime Exception: '.$e->getMessage();
}
}
}
____________________________________________________________________
class MyModel extends Model
{
use SetTimeZone;

...
}

At this point you will have UTC to user’s timezone conversion out of the box; BUT when you query the database for ranges it’ll be in UTC right?

To get around this we’ll be using CONVERT_TZ (MySQL) a built-in function so that our query fetches the results keeping the users timezone into account.

YOUR_MODEL::whereRaw("CONVERT_TZ(field.updated_at, '+00:00', '{$tzOffset}') BETWEEN '{$startDate}' AND '{$endDate}'")

You can get the off set timezone difference using:

$tzOffset =
Carbon::createFromTimestamp(0, auth()->user()->tz)->getOffsetString()

NOTE: Carbon::now() is tricky when querying the database.

Use Carbon::now() OR Carbon::today() passing in the user timezone, otherwise it will query using UTC and that’s something you may not want.

Vola !! have a good day.

--

--

Responses (1)