Managing Timezone in Laravel and Lumen

When we are working on a large web application then there’s more possibility that it’s visiting users will be from different countries, and so we need to manage their Time Zone so we can display correct time to the user that makes sense to them and other time related operations can be performed accurately. 

In this post, we will see how we can manage/change the timezone in Laravel. It’s very easy to work with a timezone in laravel.

How to change timezone laravel or lumen:

Laravel allows setting up timezone in an application via a configuration file. When we install Laravel, it provides ‘UTC’ timezone by default. If you are working on a web application that may have users from only one country, then you can change the default timezone from ‘UTC’ to ‘your_timezone’. It’s very easy to set up, just follow the steps below and the things are ready!

  1. Go to ‘your_project_dir/config/app.php’. You can see the below section of the code.

    /*                
    |—————————————————————-
    | Application Timezone   
    |—————————————————————-
    |
    |Here you may specify the default timezone for your application,
    |which will be used by the PHP date and date-time functions. We
    |have gone ahead and set this to a sensible default for you out
    |of the box.
    |
    */

    // Default value of timezone is ‘UTC’, I have changed it to ‘Asia/Kolkata’

    ‘timezone’ => ‘Asia/Kolkata’,

  2. After updating the timezone in ‘your_project_dir/config/app.php’ file, run below commands manually:

    a) php artisan config:cache

  3. To verify if the timezone is updated or not, you can simply print it using:

    date(“Y-m-d H:i:s”);

    This prints the date & time according to your new timezone!

Manage Timezone in Laravel:

As we have seen above, you can set a static timezone in application when your visitors are from the same country, But what if your users are from worldwide? Then you have to manage time for users according to their timezone. In this case just keep the laravel’s default timezone which is set to ‘UTC’, This will act as our base time zone in the backend, using UTC, it just becomes easy in the long run in managing, but you can use any timezone as base, but don’t change it in future or else it will be mess to handle.

To manage timezone for individual users, first we need to get their timezone and store it so we can use it in future.

There are 2 ways we can get the timezone of a user and format the date:

  • From the frontend,
  • From the backend.

1) Detect User Timezone From Frontend (Javascript):

Yes, we can get users’ timezone from frontend using Javascript, but for it we need to use ‘moment JS’ which can do all sorts of things with date & time like parse, validate, formatting and many more.

Put the following JS in your blade:

<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.14/moment-timezone.min.js"></script>

After including both JS files, we can get the user timezone using moment.tz.guess() and this returns value based on your current timezone. For me, this gives me ‘Asia/Kolkata’.

You can save this timezone for the respective user in DB. I have provide one example for understanding below:

I have a registration form with Laravel layouts. I’ve already put the moment JS files in my master.blade.php file.

Now, i have following line of code in my registration.blade.php file:

<form class="form-horizontal" method="POST" action="{{ route('register') }}">
    {{ csrf_field() }}
    <input type="hidden" name="tz" id="tz">
</form>

Here I’ve taken the input type hidden to bind the timezone with id=”tz”. Now, at the end of file, i’ll put the JS code to bind the timezone like below:

<script>
        $(function () {
            // guess user timezone 
            $('#tz').val(moment.tz.guess())
        })
</script>

Now this JS code bind the timezone after submitting the registration code and make it like:

<input type="hidden" name="tz" id="tz" value="Asia/Kolkata">

You can get this in your controller in $request->get(‘tz’) and save it to the DB table.

Detect User Timezone From Backend (PHP):

We can also detect users’ timezone at the server side from users’ IP address, we’ll use: https://ipstack.com service to get timezone from IP address, you can use any other service but make sure that it provides timezone data. As for the ‘ipstack’, it gives us a JSON data as below:

{
  "ip": "134.201.250.155",
  "hostname": "134.201.250.155",
  "type": "ipv4",
  "continent_code": "NA",
  "continent_name": "North America",
  "country_code": "US",
  "country_name": "United States",
  "region_code": "CA",
  "region_name": "California",
  "city": "Los Angeles",
  "zip": "90013",
  "latitude": 34.0453,
  "longitude": -118.2413,
  "location": {
    "geoname_id": 5368361,
    "capital": "Washington D.C.",
    "languages": [
        {
          "code": "en",
          "name": "English",
          "native": "English"
        }
    ],
    "country_flag": "https://assets.ipstack.com/images/assets/flags_svg/us.svg",
    "country_flag_emoji": "🇺🇸",
    "country_flag_emoji_unicode": "U+1F1FA U+1F1F8",
    "calling_code": "1",
    "is_eu": false
  },
  "time_zone": {
    "id": "America/Los_Angeles",
    "current_time": "2018-03-29T07:35:08-07:00",
    "gmt_offset": -25200,
    "code": "PDT",
    "is_daylight_saving": true
  },
  "currency": {
    "code": "USD",
    "name": "US Dollar",
    "plural": "US dollars",
    "symbol": "$",
    "symbol_native": "$"
  },
  "connection": {
    "asn": 25876,
    "isp": "Los Angeles Department of Water & Power"
  },
  "security": {
    "is_proxy": false,
    "proxy_type": null,
    "is_crawler": false,
    "crawler_name": null,
    "crawler_type": null,
    "is_tor": false,
    "threat_level": "low",
    "threat_types": null
}

This gives us lot’s of information, but we will only use timezone field. I’ll use the PHP(cURL) example to get the information.

// set IP address and API access key 
$ip = \Request::ip();
$access_key = 'YOUR_ACCESS_KEY';

// Initialize CURL:
$ch = curl_init('https://api.ipstack.com/'.$ip.'?access_key='.$access_key.'');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// Store the data:
$json = curl_exec($ch);
curl_close($ch);

// Decode JSON response:
$api_result = json_decode($json, true);

// Output the "id" object from the "timezone" field
echo $api_result[time_zone][id];

Above code echoes the user’s timezone and we will save it to our DB. Now that we have users’ timezone in our database, in future we will use this timezone to convert all date & time from our base timezone to users’ timezone before presenting it to users and vise versa when we collect date & time from users, then we will convert collected date & time from users timezone to our base timezone before storing it in our database.

How to use user specific timezone

Now we have users’ timezone, it’s time to use it in our application. In laravel, using eloquent, it becomes much easier using an accessor. For example we have a users modal in which we will convert created_at field to user specific timezone to display registration date & time.

<?php

namespace App\Models;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    protected $table = "tbl_users";
    protected $primaryKey = 'email_id';
    public $incrementing = false;
    public $timestamps = false;
	
	// Get this from database
    public $timezone = 'Asia/Kolkata';

	// As we are converting our created_at field, we should add one field without conversation to be used at other places
    protected $appends = ['created_at_original'];

    public function getUserData(string $email) {
        try {
            $user = $this->where('email_id', $email)->first();

            if (isset($user)) {
                return $user;
            }

            return null;
        } catch (QueryException $ex) {
			// Log error and return null, of handle it in your way
            Log::error($ex->getMessage());
            return null;
        }
    }

	// Our accessor, that will convert our created_at field whenever we access it
    public function getCreatedAtAttribute($value)
    {
	   // Here, Base timezone is fixed to 'UTC', You can also get it from config file to make in centrally managed
        return Carbon::createFromFormat('Y-m-d H:i:s', $value, 'UTC')->setTimezone($this->timezone)->format('d/m/Y h:i:s A');
    }
	
	// Our accessor to get original created_at field
    public function getCreatedAtOriginalAttribute()
    {
        return $this->attributes['created_at'];
    }

}

Now, whenever you access a created field like $user->created_at, it will provide converted date & time.

Same, When you collect date & time from user, you have to convert it from users timezone to our base timezone.

You can do this like below

Carbon::parse($input_date, $this->timezone)->setTimezone(‘UTC’);

You can convert date & time like this in may be controller or in modal using mutator, anything will work.

Leave a Reply

Your email address will not be published. Required fields are marked *