eslam-reda-div/filament-timezone-detector
Composer 安装命令:
composer require eslam-reda-div/filament-timezone-detector
包简介
A Filament v5 plugin that automatically detects user timezone from the browser and provides comprehensive helpers, facades, macros, and middleware for seamless timezone conversion across your entire application.
关键字:
README 文档
README
Automatically detects the user's browser timezone and provides Facade, Helpers, Carbon macros, Filament table/form macros, and an Eloquent trait for seamless timezone conversion.
Store in UTC. Display in the user's local time. Automatically.
Features
- Auto browser detection — JavaScript detects IANA timezone via
Intl.DateTimeFormatwith 10+ fallback methods - Livewire v4 / SPA compatible — uses
Livewire.interceptRequest(),wire:navigate, fetch/XHR interceptors, cross-tab BroadcastChannel sync - Middleware — captures timezone from header, cookie, query param, or form input → stores in session
- One-liner macros —
TextColumn::toUserTimezone(),TextEntry::toUserTimezone(),DateTimePicker::fromUserTimezone() - Carbon macros —
$date->toUserTimezone(),->toSystemTimezone(),->formatInUserTimezone() - Facade & helpers —
TimezoneDetector::forDisplay(),to_user_timezone(),user_now(), etc. - Eloquent trait —
InteractsWithTimezonefor per-attribute conversion - Fully configurable — toggle every feature on/off, customize header/cookie/session names
Requirements
- PHP >= 8.2, Laravel >= 11.0, Filament >= 5.0
Installation
composer require eslam-reda-div/filament-timezone-detector
Register the plugin in your panel provider:
use EslamRedaDiv\TimezoneDetector\TimezoneDetectorPlugin; public function panel(Panel $panel): Panel { return $panel // ... ->plugin(TimezoneDetectorPlugin::make()); }
Optionally publish the config:
php artisan vendor:publish --tag="timezone-detector-config"
That's it. The plugin auto-loads JS detection, registers middleware, Carbon macros, and Filament macros.
How It Works
Browser (JS) Server (Middleware) Your Code
───────────── ────────────────── ─────────
Intl.DateTimeFormat() detects → Reads X-Timezone header → Facade / Helper / Carbon macro
timezone and sends it via: (or cookie / query param) converts any datetime between
• Livewire interceptRequest Stores in session('user_tz') user ↔ system timezone
• fetch / XHR / Axios interceptors
• Cookie + hidden form inputs
• BroadcastChannel (cross-tab)
Recommended: Store all datetimes in UTC (config/app.php → 'timezone' => 'UTC'). The plugin handles display conversion automatically.
Usage
Filament Table Columns
TextColumn::make('created_at')->dateTime()->toUserTimezone(), TextColumn::make('published_at')->dateTime('M d, Y h:i A')->toUserTimezone(), TextColumn::make('updated_at')->since()->toUserTimezone(),
Filament Infolist Entries
TextEntry::make('created_at')->dateTime()->toUserTimezone(), TextEntry::make('published_at')->dateTime('M d, Y h:i A')->toUserTimezone(), TextEntry::make('updated_at')->since()->toUserTimezone(),
Filament Form Fields
DateTimePicker::make('starts_at')->toUserTimezone(), // display + save in user TZ DateTimePicker::make('event_time')->fromUserTimezone(), // alias, clearer intent DatePicker::make('event_date')->toUserTimezone(),
Facade
use EslamRedaDiv\TimezoneDetector\Facades\TimezoneDetector; // Info TimezoneDetector::getUserTimezone(); // "America/New_York" TimezoneDetector::getSystemTimezone(); // "UTC" TimezoneDetector::getOffsetFromSystem(); // -4.0 TimezoneDetector::getUserUtcOffset(); // "-04:00" // Convert to user TZ (for display) TimezoneDetector::toUserTimezone($model->created_at); // Carbon TimezoneDetector::toUserTimezone('2025-06-15 14:00:00', 'M d, Y h:i A'); // "Jun 15, 2025 10:00 AM" TimezoneDetector::forDisplay($model->created_at); // formatted string TimezoneDetector::diffForHumans($model->created_at); // "2 hours ago" // Convert to system TZ (for storage) TimezoneDetector::toSystemTimezone($request->starts_at); // Carbon in UTC TimezoneDetector::forStorage($request->input('event_time')); // alias TimezoneDetector::fromUserInput('06/15/2025 10:00 AM', 'm/d/Y h:i A'); // parse + convert // Between any two TZs TimezoneDetector::convertTimezone($datetime, 'UTC', 'Asia/Tokyo', 'H:i'); // Current time TimezoneDetector::userNow(); // Carbon in user TZ TimezoneDetector::systemNow(); // Carbon in system TZ
Helper Functions
user_timezone(); // "America/New_York" system_timezone(); // "UTC" to_user_timezone($model->created_at, 'M d, Y h:i A'); // formatted in user TZ to_system_timezone($userInput); // Carbon in UTC convert_timezone($datetime, 'UTC', 'Asia/Tokyo'); // between any TZs user_now(); // Carbon in user TZ format_user_datetime($model->created_at); // display string diff_for_humans_user($model->created_at); // "2 hours ago"
Carbon Macros
$date->toUserTimezone(); // Carbon in user TZ $date->toSystemTimezone(); // Carbon in system TZ $date->formatInUserTimezone('M d, Y h:i A'); // formatted string $date->diffForHumansInUserTimezone(); // "2 hours ago" // Chaining with Eloquent $model->created_at->toUserTimezone()->format('H:i'); $model->deadline->toUserTimezone()->isPast();
Eloquent Model Trait
use EslamRedaDiv\TimezoneDetector\Concerns\InteractsWithTimezone; class Event extends Model { use InteractsWithTimezone; protected $casts = ['starts_at' => 'datetime', 'ends_at' => 'datetime']; } $event->toUserTimezone('starts_at'); // Carbon in user TZ $event->getDatetimeForUser('starts_at', 'd/m/Y H:i'); // formatted string $event->setDatetimeFromUser('starts_at', $input); // converts to UTC and sets $event->diffForHumansInUserTimezone('starts_at'); // "in 3 days" $event->convertAttributeTimezone('starts_at', 'Asia/Tokyo'); // any TZ
Blade Views
{{ to_user_timezone($post->created_at, 'M d, Y h:i A') }} {{ $post->created_at->formatInUserTimezone('M d, Y h:i A') }} {{ diff_for_humans_user($post->published_at) }} Your timezone: {{ user_timezone() }}
Controllers
// Store: convert user input → UTC $event->starts_at = TimezoneDetector::toSystemTimezone($request->starts_at); // Display: convert UTC → user TZ $display = TimezoneDetector::forDisplay($event->starts_at);
API Resources
'starts_at_local' => TimezoneDetector::toUserTimezone($this->starts_at)?->toISOString(), 'starts_at_display' => TimezoneDetector::forDisplay($this->starts_at),
Queued Jobs
The session is unavailable in jobs. Pass the timezone explicitly:
$userTz = user_timezone(); SendReminder::dispatch($event, $userTz); // In the job: $localTime = convert_timezone($this->event->starts_at, system_timezone(), $this->userTimezone, 'M d, Y h:i A');
Using Outside Filament Panels
Register the middleware manually and include the JS:
// bootstrap/app.php (Laravel 11+) use EslamRedaDiv\TimezoneDetector\Http\Middleware\DetectUserTimezone; ->withMiddleware(function (Middleware $middleware) { $middleware->web(append: [DetectUserTimezone::class]); })
{{-- Option 1: Use FilamentAsset (recommended, no publishing needed) --}} <script src="{{ \Filament\Support\Facades\FilamentAsset::getScriptSrc('timezone-detector', 'eslam-reda-div/filament-timezone-detector') }}"></script> {{-- Option 2: Publish and use from public/ --}} <script src="{{ asset('vendor/eslam-reda-div/filament-timezone-detector/timezone-detector.js') }}"></script>
For option 2, publish first: php artisan filament:assets
Configuration
// config/timezone-detector.php return [ 'system_timezone' => config('app.timezone', 'UTC'), 'fallback_timezone' => config('app.timezone', 'UTC'), 'header_name' => env('TIMEZONE_DETECTOR_HEADER', 'X-Timezone'), 'cookie_name' => env('TIMEZONE_DETECTOR_COOKIE', 'user_timezone'), 'session_key' => env('TIMEZONE_DETECTOR_SESSION_KEY', 'user_timezone'), 'query_param' => env('TIMEZONE_DETECTOR_QUERY_PARAM', 'timezone'), 'auto_middleware' => env('TIMEZONE_DETECTOR_AUTO_MIDDLEWARE', true), 'register_carbon_macros' => env('TIMEZONE_DETECTOR_CARBON_MACROS', true), 'register_column_macros' => env('TIMEZONE_DETECTOR_COLUMN_MACROS', true), 'register_field_macros' => env('TIMEZONE_DETECTOR_FIELD_MACROS', true), ];
Plugin Options (Per-Panel)
->plugin(TimezoneDetectorPlugin::make()) // default ->plugin(TimezoneDetectorPlugin::make()->withoutMiddleware()) // manual middleware ->plugin(TimezoneDetectorPlugin::make()->autoMiddleware(false)) // same as above
API Reference
Facade / Core Methods
| Method | Returns | Description |
|---|---|---|
getUserTimezone() |
string |
User's IANA timezone |
getSystemTimezone() |
string |
System/database timezone |
toUserTimezone($dt, $format?, $fromTz?) |
Carbon|string|null |
System → user TZ |
toSystemTimezone($dt, $format?, $toTz?) |
Carbon|string|null |
User → system TZ |
convertTimezone($dt, $from, $to, $format?) |
Carbon|string|null |
Any → any TZ |
userNow() / systemNow() |
Carbon |
Current time in user/system TZ |
forDisplay($dt, $format?) |
string|null |
Shorthand display format |
forStorage($dt, $format?) |
Carbon|string|null |
Shorthand for toSystemTimezone |
fromUserInput($dt, $format?) |
Carbon |
Parse user input → system TZ |
diffForHumans($dt) |
string|null |
"2 hours ago" in user TZ |
getOffsetFromSystem() |
float |
Offset in hours (e.g., -4.0) |
getUserUtcOffset() |
string |
UTC offset (e.g., "-04:00") |
isValidTimezone($tz) |
bool |
Validate IANA timezone |
getAvailableTimezones() |
array |
All IANA identifiers |
Carbon Macros
| Method | Returns | Description |
|---|---|---|
->toUserTimezone() |
Carbon |
Copy in user TZ |
->toSystemTimezone() |
Carbon |
Copy in system TZ |
->formatInUserTimezone($format?) |
string |
Format in user TZ |
->diffForHumansInUserTimezone() |
string |
Diff for humans in user TZ |
Filament Macros
| Target | Method |
|---|---|
TextColumn |
->toUserTimezone() |
TextEntry |
->toUserTimezone() |
DateTimePicker |
->toUserTimezone() / ->fromUserTimezone() |
DatePicker |
->toUserTimezone() |
Model Trait (InteractsWithTimezone)
| Method | Returns |
|---|---|
->toUserTimezone('attr') |
Carbon|null |
->toSystemTimezone('attr') |
Carbon|null |
->getDatetimeForUser('attr', $format?) |
string|null |
->setDatetimeFromUser('attr', $value, $format?) |
static |
->diffForHumansInUserTimezone('attr') |
string|null |
->convertAttributeTimezone('attr', $tz, $format?) |
Carbon|string|null |
Helper Functions
| Function | Returns |
|---|---|
user_timezone() |
string |
system_timezone() |
string |
to_user_timezone($dt, $format?, $fromTz?) |
Carbon|string|null |
to_system_timezone($dt, $format?, $toTz?) |
Carbon|string|null |
convert_timezone($dt, $from, $to, $format?) |
Carbon|string|null |
user_now() |
Carbon |
format_user_datetime($dt, $format?) |
string|null |
diff_for_humans_user($dt) |
string|null |
FAQ
Q: UTC shows on first page load?
Expected — JS hasn't run yet. After the first request, the real timezone is detected and used for all subsequent requests. The fallback_timezone config covers this initial load.
Q: Works with Filament SPA mode / wire:navigate?
Yes. The JS listens to livewire:navigate and livewire:navigated events and injects X-Timezone via Livewire.interceptRequest(), fetch/XHR interceptors, and cookies.
Q: Works outside Filament panels? Yes. See Using Outside Filament Panels.
Q: Timezone in queued jobs? The session is unavailable in jobs. Pass it explicitly when dispatching. See Queued Jobs.
Q: Does this change PHP's global timezone?
No. It never calls date_default_timezone_set(). Only converts individual values when you call the conversion methods.
Q: Livewire v4 compatible?
Yes. The JS uses Livewire.interceptRequest() (the new v4 API) with a fallback to Livewire.hook('request') for backward compatibility.
Testing
composer test
Credits
License
MIT — see LICENSE.md
eslam-reda-div/filament-timezone-detector 适用场景与选型建议
eslam-reda-div/filament-timezone-detector 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 2.15k 次下载、GitHub Stars 达 2, 最近一次更新时间为 2026 年 03 月 04 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「laravel」 「timezone」 「detector」 「filament」 「filament-plugin」 「timezone-converter」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 eslam-reda-div/filament-timezone-detector 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 eslam-reda-div/filament-timezone-detector 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 eslam-reda-div/filament-timezone-detector 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Shim repository for phpmd/phpmd
Datetime helpers for timezone handling
A custom Doctine DBAL type to use PHP DateTime objects set to the system's default timezone.
Official PHP SDK for the IPGeolocation.io IP Location API with single and bulk lookup support.
Parses a user agent string to detect Internet Explorer
The Azure package for opentelemetry-php
统计信息
- 总下载量: 2.15k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 2
- 点击次数: 33
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-03-04