Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Timezonelist Laravel Package

jackiedo/timezonelist

Laravel package that generates a clean, configurable list of timezones for forms and selects. Filter by continent or region, output arrays or ready-to-use select options, and keep timezone data consistent across your application.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package with Composer; Laravel's package discovery registers the service provider and the Timezonelist facade automatically on modern Laravel versions.

composer require jackiedo/timezonelist

Publish the configuration file so you can see (and adjust) the display format and grouping behaviour:

php artisan vendor:publish --provider="Jackiedo\Timezonelist\TimezonelistServiceProvider"

Look first at config/timezonelist.php. That file is the whole personality of the package — it controls how each timezone is rendered (timezone code only, or augmented with region/continent) and whether the result is a flat array or grouped for <optgroup> output. Everything else is a thin wrapper around PHP's DateTimeZone data.

First real use case — a timezone picker in a profile or settings form. Drop this into a Blade view:

<select name="timezone" id="timezone">
    @foreach (Timezonelist::getList() as $code => $label)
        <option value="{{ $code }}" @selected(old('timezone', $user->timezone) === $code)>
            {{ $label }}
        </option>
    @endforeach
</select>

The array keys are the IANA identifiers (Asia/Ho_Chi_Minh, America/New_York), which is exactly what you want to persist on your model — never the human-readable label.

Implementation Patterns

Grouped select for long lists. A flat list of ~400 timezones is painful to scan. Use the grouped variant to produce <optgroup> sections by continent:

<select name="timezone">
    @foreach (Timezonelist::getGroupList() as $group => $zones)
        <optgroup label="{{ $group }}">
            @foreach ($zones as $code => $label)
                <option value="{{ $code }}">{{ $label }}</option>
            @endforeach
        </optgroup>
    @endforeach
</select>

Wire it into validation. Laravel ships a timezone validation rule, so you don't need to cross-check against the package list — but if you want to restrict to the package's curated set (e.g. you've filtered continents in config), use Rule::in:

$request->validate([
    'timezone' => ['required', 'string', Rule::in(array_keys(Timezonelist::getList()))],
]);

Reusable Blade component. Wrap the picker so every form in the app renders it consistently:

// app/View/Components/TimezoneSelect.php
public function render()
{
    return view('components.timezone-select', [
        'zones' => Timezonelist::getGroupList(),
    ]);
}

Store the identifier, render the label. Persist the raw code (Europe/Berlin) on the user record. When displaying it later, look up the friendly name from the same list rather than storing the presentation string:

$label = Timezonelist::getList()[$user->timezone] ?? $user->timezone;

Caching. The list is static for a given config, so memoize it if you render many pickers in one request or call it in an API loop:

$zones = Cache::rememberForever('timezonelist.grouped', fn () => Timezonelist::getGroupList());

Clear that key in your config-deploy step (or on config:cache).

API / SPA integration. Expose the grouped list as JSON in a small endpoint and let your front-end build the picker — the package still saves you from writing the region-mapping logic yourself:

Route::get('/meta/timezones', fn () => Timezonelist::getGroupList());

Gotchas and Tips

Check framework compatibility first. The last tagged release is from March 2022. Before adopting on Laravel 11/12 or PHP 8.3+, run the suite against your target — the package is thin, so even a stale composer.json constraint (^8.0/^9.0) can block install. If it does, a "replace" or fork is trivial given the small surface area.

Config cache will bite you. Editing config/timezonelist.php in production without php artisan config:cache (or config:clear) leaves the old format in place — a classic "why didn't my label change?" moment. Treat it like any config file in your deploy pipeline.

Values vs. labels. It is very easy to accidentally submit the localized label instead of the identifier because the package returns code => label. Always bind value="{{ $code }}". If you store labels, Carbon::setTimezone() and DateTimeZone will both fail downstream.

Never persist offsets. The package (and PHP itself) can render +01:00 style entries, but offsets shift with DST. Store the IANA name and compute the offset at render time with Carbon::now($tz)->format('P').

Grouping is presentational only. Group keys (Europe, Asia, …) are not stable identifiers — don't key business logic off them. If you need to restrict to specific continents, use the config filter rather than post-filtering grouped output, and remember a handful of zones (e.g. UTC, Etc/GMT+5) may fall outside common continent buckets.

Localization quirks. Region/continent display names come from PHP's built-in timezone data, which is English-only. If you need translated labels, override the formatter in your own wrapper rather than fighting the config — the package has no locale switching beyond what PHP provides.

Facade in tests. Because the facade resolves through the container, mock it to keep tests fast and deterministic:

Timezonelist::shouldReceive('getList')->once()->andReturn(['UTC' => 'UTC']);

Otherwise your assertions on rendered HTML will depend on the host's PHP timezone database version — which differs between CI images.

It's a convenience, not a dependency. The core is essentially DateTimeZone::listIdentifiers() plus formatting. If the package ever becomes a liability (abandonment, PHP constraint), you can replace getList() with ~15 lines of your own and keep the Blade components untouched. Keep that boundary — call the facade from a single service or component, not scattered across controllers — so the swap is a one-file change.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console