Lets say my website or web application deals with dates and times and that my users are from all over the world. How could I create a timezone settings configuration for my users with CakePHP?

Working with Time Helper

First of all we need to work with the time helper that CakePHP offers us. This way will be easier for us. CakePHP has a configuration called Config.timezone which affects the results showed by the TimeHelper.

We can modify at any moment this configuration using the Configure::write statement:

Configure::write('Config.timezone', 'Europe/London');

It works with the PHP list of timezones.

Allowing the user to change it

If we want to allow the user the change the timezone, we first will need to create a form allowing them to choose it. It can sound very simple but I struggled a lot to find a simple list (not that massive ones which each capital in the world). Also, the list should deal with timezones and not with offset time differences.

We will also need to save the selected timezone in the user’s profile table in the database and in the user session we are working with in order to start working with the new timezone in all the site from now on.

So well, here’s what I did in the profile controller:

public function profile(){

    //submitting data?
    if($this->request->is('post')){
        $this->Session->write('Auth.User.timezone', $this->request->data['selectedZone']);

        if($this->User->updateTimezone($this->request->data)){
            $this->Session->setFlash(__('The profile has been updated.'));
        }else{
            $this->Session->setFlash(__('There was an error updating the profile.'), 'default', array('class' => 'error-message'));
        }
    }

    //Our lovely list of Timezones
    $timezoneTable = array(
            "Pacific/Kwajalein" => "(GMT -12:00) Eniwetok, Kwajalein",
            "Pacific/Samoa" => "(GMT -11:00) Midway Island, Samoa",
            "Pacific/Honolulu" => "(GMT -10:00) Hawaii",
            "America/Anchorage" => "(GMT -9:00) Alaska",
            "America/Los_Angeles" => "(GMT -8:00) Pacific Time (US & Canada)",
            "America/Denver" => "(GMT -7:00) Mountain Time (US & Canada)",
            "America/Chicago" => "(GMT -6:00) Central Time (US & Canada), Mexico City",
            "America/New_York" => "(GMT -5:00) Eastern Time (US & Canada), Bogota, Lima",
            "Atlantic/Bermuda" => "(GMT -4:00) Atlantic Time (Canada), Caracas, La Paz",
            "Canada/Newfoundland" => "(GMT -3:30) Newfoundland",
            "Brazil/East" => "(GMT -3:00) Brazil, Buenos Aires, Georgetown",
            "Atlantic/Azores" => "(GMT -2:00) Mid-Atlantic",
            "Atlantic/Cape_Verde" => "(GMT -1:00 hour) Azores, Cape Verde Islands",
            "Europe/London" => "(GMT) Western Europe Time, London, Lisbon, Casablanca",
            "Europe/Brussels" => "(GMT +1:00 hour) Brussels, Copenhagen, Madrid, Paris",
            "Europe/Helsinki" => "(GMT +2:00) Kaliningrad, South Africa",
            "Asia/Baghdad" => "(GMT +3:00) Baghdad, Riyadh, Moscow, St. Petersburg",
            "Asia/Tehran" => "(GMT +3:30) Tehran",
            "Asia/Baku" => "(GMT +4:00) Abu Dhabi, Muscat, Baku, Tbilisi",
            "Asia/Kabul" => "(GMT +4:30) Kabul",
            "Asia/Karachi" => "(GMT +5:00) Ekaterinburg, Islamabad, Karachi, Tashkent",
            "Asia/Calcutta" => "(GMT +5:30) Bombay, Calcutta, Madras, New Delhi",
            "Asia/Dhaka" => "(GMT +6:00) Almaty, Dhaka, Colombo",
            "Asia/Bangkok" => "(GMT +7:00) Bangkok, Hanoi, Jakarta",
            "Asia/Hong_Kong" => "(GMT +8:00) Beijing, Perth, Singapore, Hong Kong",
            "Asia/Tokyo" => "(GMT +9:00) Tokyo, Seoul, Osaka, Sapporo, Yakutsk",
            "Australia/Adelaide" => "(GMT +9:30) Adelaide, Darwin",
            "Pacific/Guam" => "(GMT +10:00) Eastern Australia, Guam, Vladivostok",
            "Asia/Magadan" => "(GMT +11:00) Magadan, Solomon Islands, New Caledonia",
            "Pacific/Fiji" => "(GMT +12:00) Auckland, Wellington, Fiji, Kamchatka"
    );

    $this->set('timezone', $timezoneTable);
}

And this is my view for that action:

echo $this->element('settingsMenu');

echo $this->Form->create(false);

echo '
    <table class="tlm zebra zebraColors padding">
        <tr>
            <th>Profile</th>
            <th></th>
        </tr>

        <tr>
            <td> Time Zone</td>
            <td>

                <select name="selectedZone">
';
                    foreach($timezone as $key=>$value){
                        $selected = '';

                        if($key == $this->Session->read('Auth.User.timezone')){
                            $selected = 'selected';
                        }
                        echo '<option value="'.$key.'" '.$selected.'>'.$value.'</option>';
                    }
echo'
                </select>
            </td>
        </tr>
    </table>
';

echo $this->Form->end(array('label' => __('Save'), 'name' => 'submit', 'class' => 'button', 'div' => false));
?>

Setting the timezone from the session

We will always need to have the timezone stored in a variable, that way we avoid querying the database every time we want to check the user’s timezone. (as users can change it at any time from the previous form we created)

To set the timezone we just need to add this in the beforeFilter of the controller in which we want to deal with date and times (or in the AppController if you want to affect all the application):

public function beforeFilter(){
    parent::beforeFilter();

    $this->Auth->deny();

    //setting the timezone for all dates using TimeHelper
    Configure::write('Config.timezone', $this->Session->read('Auth.User.timezone'));
}

Needless to say that we will also need to store this value in the Session at the login. In that case we want to add a predefined timezone for users who didn’t set any timezone.

Now, everytime we use the timeHelper the result will get affected by our timezone configuration:

//time affected by our timezone configuration
echo $this->Time->format('d/m H:i:s' , $post['date']);