Ruben
Ruben

Reputation: 5095

PHP timezone select

I've been doing some research for providing the user with a usable timezone select box, but so far I'm not happy with the result. I'm using PHP's built-in DateTime and DateTimeZone to get the list of timezones (the Olson database). But if you've ever used this you know it is a very long list (about 300 items), so unusable in a select box. I formatted it a little using optgroups to group it by zone and also concatenate cities with the same UTC offset. Still the list is quite long and I'm not happy with it. I would actually like to only get the major cities in each area of the world and not the relatively unknown ones, if that makes sense.

I want to keep the identifiers like they are, because I can use those with the DateTime and DateTimeZone classes to perform conversions and stuff. Any ideas of how to make this list more usable/shorter?

The code I have right now is as follows (a bit messy right now but it's a work in progress):

    public function getListOfTimezones()
    {
        $options = array();
        $utc = new DateTimeZone('UTC');

        //$regions = array('Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific');
        $regions = array('America', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Pacific');
        $tzs = DateTimeZone::listIdentifiers();
        $optgroup = null;
        $timezoneGroup = array();
        sort($tzs);

        foreach ($tzs as $tz)
        {
            $timezonepieces = explode('/', $tz, 2);

            if (count($timezonepieces) != 2 || !in_array($timezonepieces[0], $regions)) continue;

            $region = $timezonepieces[0];
            $location = $timezonepieces[1];             

            if (count($timezoneGroup) > 0)
            {
                $lastTimeZone = $timezoneGroup[count($timezoneGroup) - 1];

                $utcTime = new DateTime(null, $utc);
                $timezoneInfo = new DateTimeZone($tz);
                $lastTimeZoneInfo = new DateTimeZone($lastTimeZone['timezoneid']);

                if ($timezoneInfo->getOffset($utcTime) == $lastTimeZoneInfo->getOffset($utcTime))
                {
                    $addTz = array('timezoneid' => $tz, 'timezonelabel' => str_replace('_', ' ', $location));
                    $timezoneGroup[] = $addTz;
                }
                else
                {
                    $labels = array();

                    if (count($timezoneGroup) > 5)
                    {
                        $timezoneGroup = array_slice($timezoneGroup, 0, 5);
                    }

                    $country = '';
                    foreach ($timezoneGroup as $curTimezone)
                    {
                        $pieces = explode('/', $curTimezone['timezonelabel'], 2);

                        if (count($pieces) == 2)
                        {
                            if ($pieces[0] == $country)
                            {
                                continue;
                            }
                            else
                            {
                                $country = $pieces[0];
                            }   
                        }

                        $labels[] = $curTimezone['timezonelabel'];
                    }
                    $optgroup['options'][htmlentities($timezoneGroup[0]['timezoneid'])] = implode(', ', $labels);

                    // New timezone group
                    $timezoneGroup = array();
                    $addTz = array('timezoneid' => $tz, 'timezonelabel' => str_replace('_', ' ', $location));
                    $timezoneGroup[] = $addTz;
                }
            }
            else
            {
                $addTz = array('timezoneid' => $tz, 'timezonelabel' => str_replace('_', ' ', $location));
                $timezoneGroup[] = $addTz;
            }

            if (!$optgroup || (is_array($optgroup) && array_key_exists('label', $optgroup) && $optgroup['label'] != htmlentities($region)))
            {
                if ($optgroup)
                {
                    // End of previous optgroup, start of new one
                    $options[] = $optgroup;
                }

                $optgroup = array('label' => htmlentities($region), 'options' => array());
            }
        }

        if (is_array($optgroup))
        {
            $options[] = $optgroup;
        }

        return $options;
    }

Upvotes: 1

Views: 609

Answers (1)

Jon
Jon

Reputation: 437514

There are limits to what you can do with pure HTML when there is need for specialized data entry like there is here.

I recommend using some JavaScript component that enhances the selection of the timezone; for example a very good jQuery plugin that I also use is Select2. It transforms a drop down menu with automatic find as you type and a lot of additional features.

Upvotes: 2

Related Questions