mohebifar/date-time-bundle
A Symfony bundle that provides date and time form types with picker integration, making it easier to add date/time inputs and handle DateTime values cleanly in PHP applications.
The first thing to internalize: this is a Symfony 2.x bundle, not a Laravel package. There is no service provider, no facade, no config file in Laravel's config/ sense, and the Datepicker FormType extends Symfony\Component\Form\AbstractType. Do not attempt composer require mohebifar/date-time-bundle in a Laravel 9/10/11 app — the symfony/* constraints are Symfony 2-era and will either fail resolution or force a downgrade across your symfony/* transitive tree. The working path is to clone the repo into a scratch directory, read it, and lift the calendar formatting logic into app/Support.
Where to look first, in order:
DependencyInjection/ and DateTimeBundle.php — skim only; you will delete all of this.strftime, format, or a Formatter/Calendar name) — this is the one file with portable value. Identify the pure functions: Gregorian→target-calendar conversion, month-length and leap-year rules, and format-token expansion.Form/Type/* — read it for the pattern (how they wire a datepicker to a transformed value), not the code. In Laravel this maps to a Blade component + a custom cast.Minimal first use case — a framework-agnostic formatter:
// app/Support/Calendar/JalaliFormatter.php (extracted, no Symfony imports)
namespace App\Support\Calendar;
class JalaliFormatter
{
public function __construct(
protected CalendarConverter $converter,
protected string $format = 'Y/m/d',
) {}
public function format(\DateTimeInterface $date, ?string $tz = null): string
{
$tz = $tz ? new \DateTimeZone($tz) : $date->getTimezone();
$local = (new \DateTimeImmutable($date->format('c')))->setTimezone($tz);
return $this->converter->format($local, $this->format);
}
}
Bind it in a service provider, inject it into a helper/facade, and call it from a Blade directive. That's your "first use case": render created_at in a target calendar in a view. Everything else in the bundle is a reference, not a dependency.
Before committing, check the repo's composer.json for the license and for whether the conversion math is original or vendored — you need both answers before this code ships anywhere.
Provider + singleton for the formatter. Calendar conversion is stateless and CPU-trivial; bind one instance so format/locale defaults live in config/calendar.php rather than being passed at every call site. Do not reach for a facade immediately — inject the interface so tests can swap a fake and so you can later swap engines (e.g. to morilog/jalali) without touching call sites.
// app/Providers/CalendarServiceProvider.php
public function register(): void
{
$this->app->singleton(CalendarFormatter::class, fn ($app) =>
new JalaliFormatter(
converter: new JalaliConverter(),
format: config('calendar.format', 'Y/m/d'),
)
);
}
Blade directive for display, component for input. Keep formatting and picking separate — the bundle conflates them, and that's the piece you should not carry over.
Blade::directive('calendar', fn ($expr) =>
"<?php echo e(app(\App\Support\Calendar\CalendarFormatter::class)->format($expr)); ?>"
);
{{-- resources/views/components/datepicker.blade.php --}}
<div x-data x-init="flatpickr($refs.input, { plugins: [jalaliPlugin], dateFormat: 'Y-m-d' })">
<input x-ref="input" type="text" name="{{ $name }}" value="{{ old($name, $value) }}">
</div>
The client-side picker (flatpickr + a Jalali plugin, or a maintained Persian datepicker) replaces the bundle's FormType entirely. This is the correct layer for date entry in a Laravel app — no Symfony Form bridge required, no server round-trip for the widget.
Eloquent cast for storage boundary. Keep DateTime in the model (the bundle gets this right), and format only at render.
class JalaliDate implements CastsAttributes
{
public function get($model, $key, $value, $attributes): ?string
{
if ($value === null) return null;
return app(CalendarFormatter::class)->format(
new \DateTimeImmutable($value, new \DateTimeZone('UTC'))
);
}
public function set($model, $key, $value, $attributes): ?string
{
return $value; // store Gregorian; never persist a formatted string
}
}
Validation rule for user-entered calendar dates. The bundle's FormType implies validation; you have to supply it yourself.
class ValidCalendarDate implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
try { app(JalaliConverter::class)->parse($value); }
catch (\Throwable) { $fail(__('calendar.invalid_date')); }
}
}
Day-to-day workflow. Most of the team will only ever touch the Blade directive and the cast. The provider and converter are touched once and then tested. Treat the fork as an internal package under app/Support/Calendar with attribution in a header comment, and pin it in code review — never as a VCS dependency.
composer require is the good outcome; a silent downgrade of a symfony/* component that Laravel's illuminate/* stack depends on is the bad one. If you must poke at the bundle directly, use a throwaway Symfony skeleton in a container, not your app.date()/strftime() but reinterpret m, d, Y, or l for the target calendar. A developer passing 'Y-m-d' out of habit can get correct-looking-but-wrong output for months > 9 or month names. Enforce a distinct token namespace in your extracted formatter, or expose a typed API (->iso(), ->longDate()) instead of raw tokens.date_default_timezone_get(). If the bundle's formatter assumes server-local time, that assumption has to be removed during extraction, not preserved. Off-by-one-day errors around midnight and DST boundaries are the symptom.FormType has no Laravel analogue. Resist building a "Symfony Form bridge". The bundle's AbstractType/FormBuilderInterface machinery is dead weight in Laravel; map the ergonomics (a named field type with sensible defaults) to a Blade component and a validation rule.->jalali_created_at) or format at render time, not in the query loop.CalendarConverter so a Hijri or Coptic engine (or morilog/jalali) can be swapped in without touching the provider, directive, or cast. That interface is the only architectural contribution worth carrying forward from this package.How can I help you explore Laravel packages today?