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.