ariaieboy/filament-jalali-datetime
Adds Persian (Jalali/Shamsi) date and time pickers to Filament forms and tables. The plugin offers ready-to-use Jalali date picker components, accurate Gregorian–Jalali conversion, and localized Persian formatting so admin panels can accept and display dates in the Iranian calendar.
ariaieboy/filament-jalali-datetime is a Filament form-field plugin that swaps Filament's Gregorian date/time pickers for Jalali (Shamsi) equivalents while still persisting standard Gregorian values to your database.
Minimal setup:
composer require ariaieboy/filament-jalali-datetime
php artisan vendor:publish --tag="filament-jalali-datetime-config" # optional
Then use the Jalali fields in place of Filament's native pickers:
use Ariaieboy\FilamentJalaliDatetime\JalaliDatePicker;
use Ariaieboy\FilamentJalaliDatetime\JalaliDateTimePicker;
use Ariaieboy\FilamentJalaliDatetime\JalaliTimePicker;
JalaliDatePicker::make('birth_date'),
JalaliDateTimePicker::make('published_at')->seconds(false),
JalaliTimePicker::make('opens_at'),
Where to look first:
DateTimePicker/DatePicker), so anything you know from Filament's fluent API (minDate(), maxDate(), seconds(), displayFormat(), timezone(), disabledDates(), reactive ->live()) still applies.orders or posts resource where an admin in Tehran needs to pick a publish date and read it back in Shamsi, while created_at/published_at remain Gregorian datetime columns in MySQL.First useful thing to build: take an existing resource form, replace one DateTimePicker with JalaliDateTimePicker, save a record, and confirm the DB row is Gregorian and the re-rendered form is Jalali. That round-trip is the whole value proposition — verify it before rolling out everywhere.
Drop-in replacement on forms. The components are subclasses, so a Jalali field accepts the same fluent chain as Filament's own. Migration pattern: change the use statement and the class name, keep the rest of the chain, test the round-trip. This makes incremental adoption in a large codebase low-risk.
Table columns and filters stay partly manual. The package targets form fields; for tables you'll typically use TextColumn::make('published_at')->formatStateUsing(...) with a Jalali conversion, or the dateTime() formatting helpers, since the picker components don't render table cells. Pair the field with a table column that formats the same raw Gregorian value into Shamsi, and keep the two format strings in one config/helper so they never drift.
Keep conversion in one place. For exports, PDFs, notifications and API resources you'll want a single Jalali helper (or a small casts/accessor) rather than sprinkling conversion logic. A common pattern is a HasJalaliDates trait on models exposing getPublishedAtJalaliAttribute() used by mail templates and Blade views, while the picker handles input.
Reactive dashboards. Because the fields are Livewire-bound like any Filament component, ->live() + ->afterStateUpdated() works normally. Useful for "select Jalali date → show weekday name / holiday warning" UX that Persian users expect.
Validation and cross-field rules. Combine with ->rules(['after_or_equal:today']) carefully: Laravel validates the state value. Confirm whether the component stores a Gregorian string in state (usual) or a Jalali string, then write rules against that representation. For ranges (starts_at / ends_at), validate in Gregorian and only localise labels.
Multi-panel / multi-locale apps. If one panel is Persian-only and another is English-only, guard field selection behind a helper like isJalaliPanel() rather than duplicating resources — swapping the field class at runtime in form() is cheap and keeps a single schema definition.
Asia/Tehran plus an app on UTC plus a DB column without timezone awareness can shift dates by a day near midnight. Set the field's timezone() deliberately and decide whether users pick in local time. Store UTC, display Tehran, and write a test that saves 23:30 local and asserts the stored value.1403/12/30 exists or not depending on the year and no Carbon shortcut saves you — don't hand-roll +1 month arithmetic on Jalali strings; convert to Carbon, manipulate, convert back.Y-m-d H:i:s or an ISO string into state before it hits your model cast. If you have 'published_at' => 'datetime' casts, an unexpected format will throw on save — reproduce in a test with a real SQLite/MySQL column, not just a Livewire component test.php artisan config:clear; Filament panels under config:cache in production will silently keep old formats. This is the first thing to check when "the format didn't change".composer.json, and read this package's changelog for supported Filament majors after any Filament upgrade.--ignore-platform-req. If you're still on 8.1–8.3 nothing changes for you.afterStateUpdated(), then check the model attribute after save, then the formatted output. The bug is nearly always in one of those three, and knowing which one halves the fix time.getView() or the format method rather than editing vendor code — the package's views are publishable/taggable, and subclasses survive composer update.How can I help you explore Laravel packages today?