Sean O' Neill_

Laravel with Timezones

Dealing with timezones was a lot harder than I expected it to be. Since there is over 24 timezones and then you have to account for daylight savings as well.

I did some digging around the internet for the best way to deal with timezones or if there was any library's to help me, but after sending more time than I care to admit, I ended up generating a timezone list with PHP, but the problem is the list is huge and is not that user friendly, so to help with this I added a search box to the drop down with select2 and also use JQuery to try and detect the users timezone.  

To keep track of each users timezone we are going to add a timezone field to the user table in migrations.

$table->string('timezone', 60);

Now we need to override the default register page so we can generate a list of time zones to be able to display in our dropdown. The code below needs to be put in the RegisterController and it will get all timezones into an array and there offset, then we sort it by the offset to get a structured list.

public function showRegistrationForm(){
	$timezone = array();
	$timestamp = time();
	
foreach(timezone_identifiers_list(\DateTimeZone::ALL) as $key => $t) {
		date_default_timezone_set($t);
		$timezone[$key]['zone'] = $t;
		$timezone[$key]['GMT_difference'] =  date('P', $timestamp);
}
	$timezone = collect($timezone)->sortBy('GMT_difference');

	return view('auth.register',compact('timezone'));
}

We also validate the timezone by using the built in validation rule in Laravel. We need to add this into the validator function in the RegisterController.

'timezone' => ['required', 'timezone', 'max:60']

As we are still in the RegisterController we will add the below code in the create function so that the input from the timezone dropdown will be added to the database with the rest of the user details.

Next we need to edit our our register view and add the follow code to loop over all  our timezones and offsets and display then in our dropdown.

 <div class="form-group row">
   <label for="name" class="col-md-4 col-form-label text-md-right">TimeZone</label>
<div class="col-md-6">
   <select  class="js-example-basic-single" id="timezone"  name="timezone" required>
                 @foreach($timezone as $time)
<option value="{{$time['zone']}}"> ({{$time['GMT_difference']. ' ) '.$time['zone']}}</option>
                 @endforeach
      </select>
 </div>
 </div>

The class (js-example-basic-single) in the dropdown will be used to add the search box to the dropdown using select2 which we are going to add now.

We will add two scripts using CDN to our register view to make the search box work for us, jQuery & select2.

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css"></script>

The first stylesheet below is for select2

and the second stylesheet is to make the select2 look like the other bootstrap inputs. I got this select2 bootstrap 4 theme from GitHub. You will need to download the CSS file and add it to your CSS folder in Laravel and then add it to your register page.  

<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" /> 
<link href="{{ asset('css/single.css') }}" rel="stylesheet">

After this we need to add the below function to the register page also to get the select2 and bootstrap theme to work,  100% width was add to make the field the same width as the other input fields on the register page.

 <script>
	$(document).ready(function() {
	$('.js-example-basic-single').select2({
   		theme: 'bootstrap4',
    	width: '100%'
	});
});
</script>

Now we can add this little bit of jQuery code to try and auto detect the users timezone so we can set a default for the timezone dropdown.

 <script>
   var timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
    $("#timezone option[value='"+timezone+"']").attr("selected", "selected");
 </script>

When I view the register page, you can see that my timezone(Europe/Dublin) was detected and set as the default for me.

And here is the dropdown with the search bar so the user can also search if they do not want to scroll through all the timezones.

Usually A server uses UTC time as default time and does not have daylight saving either to take into account. All timestamps will be saved to the database as UTC and we can covert it to the users timezone using carbon.

Here is an example, after the user registers and logs into there dashboard they will see the date and time in there timezone. This is very simple to achieve. below in the code we will the current date/time with carbon and then convert it to the timezone the user picked when they registered. Just add this code into the HomeController under index function.

 public function index()
    {
         $now = Carbon::now();
          $time = Carbon::parse($now)->timezone(\Auth::user()->timezone);
         return view('home',compact('time'));
    }

we can display the users timezone date/time by adding this bit of code to the home view.

<p><b>Current Date/Time:</b> {{$time}}</p>

Now we should see the date/time in the user timezone.

The complete code can be found on github.