In this tutorial, i’ll show you how to add multiple timezones functionality into your laravel application.
Dates must be presented to a guest or user in his local timezone so it’s better to store those in UTC timezone (default in laravel).
So when visitor come to the website we have to get his local timezone and to solve this problem, we need to install laravel-geoip package.
To install it run the commande line
1 2 3 | composer require torann/geoip |
Once installed you need to register the service provider with the application. Open upΒ config/app.php
Β and find theΒ providers
Β key.
1 2 3 4 5 6 7 | 'providers' => [ //... \Torann\GeoIP\GeoIPServiceProvider::class, ] |
This package also comes with an optional facade, which provides an easy way to call the the class. Open upΒ config/app.php
Β and find the aliases key.
1 2 3 4 5 6 7 | 'aliases' => [ //... 'GeoIP' => \Torann\GeoIP\Facades\GeoIP::class, ]; |
Run this on the command line:
1 2 3 | php artisan vendor:publish --provider="Torann\GeoIP\GeoIPServiceProvider" --tag=config |
A configuration file will be publish toΒ config/geoip.php
.
While using the library, it can be possible you will get the error βThis cache store does not support taggingβ with BadMethodCallException. If so, just change the below line fromΒ config/geoip.php
Β file.
Replace
1 2 3 | 'cache_tags' => ['torann-geoip-location'], |
With
1 2 3 | 'cache_tags' => [], |
Great ! now Geoip is ready and we can get geographical location of visitors based on their IP addresses.
Now we have to store timezone visitor in cookie. Let’s create middleware
1 2 3 | php artisan make:middleware TimezoneMiddleware |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <?php namespace App\Http\Middleware; use Closure; use DateTimeZone; use Cookie; class TimezoneMiddleware { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { $response = $next($request); $default_timezone = 'UTC'; $timezone_cookie = cookie::get('timezone'); $visitor_timezone = geoip()->getLocation(request()->ip())->timezone; if (($timezone_cookie === null) || (!in_array($timezone_cookie, DateTimeZone::listIdentifiers()))) { setTimezone($visitor_timezone); return $response->withCookie(cookie()->forever('timezone', $visitor_timezone)); } elseif (in_array($timezone_cookie, DateTimeZone::listIdentifiers())) { setTimezone($timezone_cookie); return $response->withCookie(cookie()->forever('timezone', $timezone_cookie)); } else { setTimezone($default_timezone); return $response->withCookie(cookie()->forever('timezone', $default_timezone)); } return $response; } public function setTimezone($timezone) { config(['app.timezone' => $timezone]); date_default_timezone_set($timezone); } } |
We have to exclude encryption for timezone cookie. go to app/Http/Middleware/EncryptCookies.php
file and add timezone to except array.
1 2 3 4 5 | protected $except = [ 'timezone' ]; |
Add middleware to kernel app/Http/Kernel.php.
1 2 3 4 5 6 7 8 9 10 11 | //... protected $middlewareGroups = [ 'web' => [ //... \App\Http\Middleware\TimezoneMiddleware::class, ], //... ] //... |
Okey, now for example we have news model contains title, content, published_at, created_at, updated_at. So if we want to show the date based on user timezone it’s very easy.
To convert date we have to use accessors and mutators. for more information about accessors and mutators in laravel read this https://laravel.com/docs/5.8/eloquent-mutators
After you create news model and the migration, now we have to edit news model.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | <?php namespace App; use Illuminate\Database\Eloquent\Model; use Carbon\Carbon; class News extends Model { protected $dates = [ 'created_at', 'updated_at', 'published_at', ]; // CreatedAt public function getCreatedAtAttribute($value) { $date = Carbon::createFromFormat('Y-m-d H:i:s', $value, 'UTC'); $date->setTimezone(config('app.timezone')); return $date; } public function setCreatedAtAttribute($value) { $this->attributes['created_at'] = Carbon::parse($value, config('app.timezone'))->setTimezone('UTC'); } // UpdatedAt public function getUpdatedAtAttribute($value) { $date = Carbon::createFromFormat('Y-m-d H:i:s', $value, 'UTC'); $date->setTimezone(config('app.timezone')); return $date; } public function setUpdatedAtAttribute($value) { $this->attributes['updated_at'] = Carbon::parse($value, config('app.timezone'))->setTimezone('UTC'); } // PublishedAt public function getPublishedAtAttribute($value) { $date = Carbon::createFromFormat('Y-m-d H:i:s', $value, 'UTC'); $date->setTimezone(config('app.timezone')); return $date; } public function setPublishedAtAttribute($value) { $this->attributes['published_at'] = Carbon::parse($value, config('app.timezone'))->setTimezone('UTC'); } } |
Great !, now published_at, created_at, updated_at attributes when we add or edit, the dates will be converted to UTC timezone and when we retrieve them they will be converted to local timezone user. For example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // when we add new news post, // we have datetime input with published_at name in the form. // so we have to set date time in local timezone // because it will be converted to UTC timezone before it save or update to database // add new post news $news = new News; $news->title = $request->title; $news->content = $request->content; $news->published_at = new Carbon($request->published_at); $news->save(); // retrieve published_at data $news->published_at // or $news->published_at->diffForHumans() |
If you want to save and retieve timezone from user table, you need to add timezone field in user table.
1 2 3 4 5 6 7 8 9 10 11 | ... Schema::create('users', function (Blueprint $table) { ... $table->string('timezone')->default('UTC'); ... }); ... |
Also we have to edit TimezoneMiddleware.
In middleware
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <?php namespace App\Http\Middleware; use Closure; use DateTimeZone; use Cookie; class TimezoneMiddleware { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { $response = $next($request); $default_timezone = (auth()->check()) ? auth()->user()->timezone : 'UTC'; $timezone_cookie = cookie::get('timezone'); $visitor_timezone = (auth()->check()) ? auth()->user()->timezone : geoip()->getLocation(request()->ip())->timezone; if (($timezone_cookie === null) || (!in_array($timezone_cookie, DateTimeZone::listIdentifiers()))) { setTimezone($visitor_timezone); return $response->withCookie(cookie()->forever('timezone', $visitor_timezone)); } elseif (in_array($timezone_cookie, DateTimeZone::listIdentifiers())) { setTimezone($timezone_cookie); return $response->withCookie(cookie()->forever('timezone', $timezone_cookie)); } else { setTimezone($default_timezone); return $response->withCookie(cookie()->forever('timezone', $default_timezone)); } return $response; } public function setTimezone($timezone) { config(['app.timezone' => $timezone]); date_default_timezone_set($timezone); } } |
Thatβs it!. I hope you got to know how to add multiple timezones functionality into your laravel website.
Leave a Reply