agelgil/filament-ethiopic-calendar
Adds Ethiopic (Ge'ez) calendar support to Filament forms, tables, and date pickers, so Ethiopian dates can be entered, displayed, and converted alongside Gregorian ones. A lightweight integration that brings localized dates to admin panels and forms.
What it is: a Filament table column that renders Gregorian datetime values using the Ethiopic (Ge'ez) calendar — 13 months, 30 days each plus Pagume — so Amharic/Ethiopian-speaking staff see መስከረም 12, 2017 instead of September 22, 2024.
Install and register:
composer require agelgil/filament-ethiopic-calendar
Check composer.json inside the package before you install; the README ships as a very small surface area, so the filament/filament version constraint is the first thing to verify against your app (Filament 3 vs 4 era plugins differ mostly in column base classes and Filament\Tables\Columns\Column API).
If the package exposes a plugin object, register it in your panel provider:
public function panel(Panel $panel): Panel
{
return $panel
->plugin(\Agelgil\EthiopicCalendar\EthiopicCalendarPlugin::make());
}
If it only ships a column class, skip the panel registration entirely.
Where to look first, in this order:
src/ — find the column class. There is almost certainly exactly one, and it is the whole package.formatStateUsing() / getState() method — this tells you the conversion library it delegates to (likely andegna/calender) and the output format string.README.md for the column name and any ->format()/->locale() options.composer.json require block for the Filament and PHP constraints.First use case: replace a single dateTime() column on a resource table you already have, confirm the output in the browser, then decide whether to roll it out further:
use Agelgil\EthiopicCalendar\Columns\EthiopicDateTimeColumn;
// before
TextColumn::make('created_at')->dateTime(),
// after
EthiopicDateTimeColumn::make('created_at'),
Keep the change behind one column until you've validated the year offset and the month-name rendering against a date you can hand-check (Sept 11 Gregorian ↔ Meskerem 1 Ethiopic).
Treat it as a presentation layer only. The column should never change what's stored or what's queried. Persist created_at, approved_at, etc. as normal Gregorian UTC timestamps and let the column do the visual transform. This keeps sorting, filtering, and exports coherent — ->sortable() will still sort by the real underlying timestamp, which is exactly what you want.
Pattern: mixed audience tables. When both Ethiopian and international staff use the same admin, render both calendars side by side rather than guessing:
EthiopicDateTimeColumn::make('created_at')
->label('Date (E.C.)')
->sortable(),
TextColumn::make('created_at')
->label('Date (G.C.)')
->dateTime('d M Y')
->sortable()
->toggleable(),
Pattern: keep the raw value accessible in exports and detail views. A TextColumn is not used by Infolist, widgets, or ->exportColumns(). For those surfaces write a tiny helper and reuse it, rather than hoping the plugin reaches them:
// app/Support/Ethiopic.php
public static function format(?Carbon $dt, string $tz = 'Africa/Addis_Ababa'): ?string
{
return $dt ? \Andegna\DateTimeFactory::fromDateTime($dt->copy()->setTimezone($tz))->format('F j, Y') : null;
}
Then use TextColumn::make('created_at')->formatStateUsing(fn ($state) => Ethiopic::format($state)) in infolists and repeaters.
Pattern: forms and filters stay Gregorian. Filament's DatePicker speaks Gregorian; there is no Ethiopic date picker here. Provide a Gregorian picker in the form and an Ethiopic read-only display beside it in the same form section. Attempting to round-trip user input through the plugin's formatter is where teams usually burn a day.
Pattern: localize the output. Store a format/locale config in a custom config file (or pass it through a subclassed column) so a single change flips month names between Amharic (መስከረም) and transliterated (Meskerem). Wrap your own if the column doesn't expose it:
class AmharicDateTimeColumn extends EthiopicDateTimeColumn
{
protected function formatStateUsing($state): string
{
return parent::formatStateUsing($state); // then post-process month names
}
}
Pattern: test the conversion boundary explicitly. Freeze time around Enkutatash and around Pagume, because those are the two places the Gregorian↔Ethiopic offset shifts:
$this->travelTo(Carbon::parse('2024-09-11 12:00:00', 'Africa/Addis_Ababa'));
livewire(ManagePosts::class)
->assertTableColumnFormattedStateSet('created_at', 'መስከረም 1, 2017');
filament-ethipic-calendar in the GitHub path vs filament-ethiopic-calendar as the composer name). Bookmark the composer name; the repo link in the package's own metadata may 404 or redirect. A typo'd repo on a 2-star package is also a decent proxy for how much proofreading the rest of it has had.Africa/Addis_Ababa before formatting, or events between 21:00 and 24:00 UTC will render on the wrong Ethiopic day. Verify whether the column does this for you or just formats the UTC value.month * 30 math in a hand-rolled formatter fails here.subtract(7) will be wrong for roughly a third of the year. Confirm the package uses a real conversion routine.formatStateUsing() and andegna/calender directly — you lose nothing but the composer require.TextColumns on created_at (one Ethiopic, one Gregorian) work fine for rendering but break assertTableColumn... helpers and ->searchable() on the duplicate. Name the Ethiopic one distinctly with ->name('created_at_ec') and a ->state() closure if you hit collisions.->filter() on these columns will use Filament's Gregorian date filter. That's the correct behavior, but tell your users, or they'll file a ticket about the picker.formatStateUsing(). Whatever options the package ships, that method is the hook. Subclass rather than fork; it survives a composer update."agelgil/filament-ethiopic-calendar": "1.0.*") and keep a one-file fallback so a broken release doesn't block a deploy.How can I help you explore Laravel packages today?