· yebor974 · Getting Started, Advanced Techniques, Tutorials
Timezone setup for Forms, Tables, and Filters in Filament
Learn how to configure timezones for forms, tables, and filters in Filament Resources, ensuring accurate date handling based on user preferences.
Managing timezones effectively is crucial for any application dealing with global users. In Filament, you can simplify timezone handling by leveraging built-in methods like timezone()
for your components. This article demonstrates how to configure timezone management for forms, tables, and filters, ensuring your users see dates in their preferred timezone.
Prerequisite: Adding a string timezone attribute We assume that your User model includes a timezone attribute to store the user's preferred timezone. If it doesn’t, you can add this attribute and configure it to default to the application’s timezone (e.g., config('app.timezone')) when not set. For guidance on adding it during registration, check out this article.
Case 1: Global timezone configuration
Instead of setting the timezone for each field individually, you can declare a global configuration in the ServiceProvider
for components like DateTimePicker
and TextColumn
.
Global configuration in AppServiceProvider
Add the following code to your AppServiceProvider
in the boot()
method:
use Filament\Forms\Components\DateTimePicker;
use Filament\Tables\Columns\TextColumn;
public function boot(): void
{
// Set default timezone for DateTimePicker
DateTimePicker::configureUsing(function (DateTimePicker $component): void {
$component->timezone(auth()->user()?->timezone ?? config('app.timezone'));
});
// Set default timezone for TextColumn for datetime attibutes
TextColumn::configureUsing(function (TextColumn $component): void {
if (in_array($component->getName(), ['created_at', 'updated_at', 'published_at', 'email_verified_at'])) {
$component->timezone(auth()->user()?->timezone ?? config('app.timezone'));
}
});
}
Explanation:
- The
DateTimePicker
component will now always display dates using the user's timezone or fall back to the application's default timezone. Dates are automatically saved using the application's default timezone. - The
TextColumn
will apply the timezone to commonly used datetime fields likecreated_at
,updated_at
, andpublished_at
.
Case 2: Applying timezone configuration in a Filament Resource
For further customization, you can configure a resource to use timezone-aware components. Below is an example using a BlogPostResource
with a published_at
attribute.
Table columns
For TextColumn
, you can directly use the timezone()
method to ensure the displayed published_at
respects the user's timezone.
use Filament\Tables;
protected function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('published_at')
->label('Published At')
->dateTime()
->timezone(auth()->user()?->timezone ?? config('app.timezone')),
]);
}
Form components
For DateTimePicker
, simply use the timezone()
method to manage timezones dynamically.
use Filament\Forms;
protected function form(Form $form): Form
{
return $form
->schema([
Forms\Components\DateTimePicker::make('published_at')
->label('Published At')
->timezone(auth()->user()?->timezone ?? config('app.timezone')),
]);
}
Manage Table filters
For filtering data using Tables\Filters
, ensure that the date values are converted to the correct timezone during the query.
Here’s an example of a filter for created_at
with support for user-specific timezones:
use Filament\Tables;
use Filament\Tables\Filters\Filter;
use Filament\Tables\Filters\Components\DatePicker;
use Illuminate\Database\Eloquent\Builder;
use Carbon\Carbon;
protected function table(Table $table): Table
{
return $table
->filters([
Filter::make('created_at')
->form([
DatePicker::make('created_from')
->label(__('users.filters.created_from'))
->native(false)
->displayFormat('d/m/Y'),
DatePicker::make('created_until')
->label(__('users.filters.created_until'))
->native(false)
->displayFormat('d/m/Y'),
])
->indicateUsing(function (array $data): array {
$indicators = [];
if ($data['created_from'] ?? null) {
$indicators[] = Tables\Filters\Indicator::make(
__('users.filters.created_from') . ' ' . Carbon::parse($data['created_from'])->toFormattedDateString()
)->removeField('created_from');
}
if ($data['created_until'] ?? null) {
$indicators[] = Tables\Filters\Indicator::make(
__('users.filters.created_until') . ' ' . Carbon::parse($data['created_until'])->toFormattedDateString()
)->removeField('created_until');
}
return $indicators;
})
->query(function (Builder $query, array $data): Builder {
return $query
->when(
$data['created_from'],
fn (Builder $query, $date): Builder => $query->where(
'created_at',
'>=',
Carbon::parse($date, auth()->user()?->timezone ?? config('app.timezone'))
->startOfDay()
->timezone(config('app.timezone'))
)
)
->when(
$data['created_until'],
fn (Builder $query, $date): Builder => $query->where(
'created_at',
'<=',
Carbon::parse($date, auth()->user()?->timezone ?? config('app.timezone'))
->endOfDay()
->timezone(config('app.timezone'))
)
);
}),
]);
}
Explanation
-
Filter Form:
-
DatePicker
is used for selectingcreated_from
andcreated_until
. - It displays dates in a
d/m/Y
format, ensuring clarity for users.
-
-
Indicators:
- When a user applies filters, indicators show the selected range using
Carbon::toFormattedDateString()
.
- When a user applies filters, indicators show the selected range using
-
Query Logic:
- The
created_from
andcreated_until
values are parsed to the user's timezone and then converted to the platform's timezone (config('app.timezone')
) for consistent querying.
- The
-
Timezone Conversion:
- Using
Carbon::parse($date, auth()->user()->timezone)
ensures the dates are interpreted correctly based on the user's timezone. - The
startOfDay()
andendOfDay()
methods ensure proper inclusivity of the date range.
- Using
Benefits of global configuration
With the AppServiceProvider
setup, the following benefits apply:
-
Reduced Redundancy: No need to manually configure
timezone()
for each field. - Consistency: All components handle timezones uniformly.
- Easy Maintenance: Adding or changing timezone behavior is centralized.
By using Filament’s built-in methods like timezone()
and setting global defaults in the AppServiceProvider
, you can efficiently manage timezone behavior across your application. This setup ensures a consistent and user-friendly experience, especially for date and time fields in resources.