jagdeepbanga/nova-date-time
jagdeepbanga/nova-date-time provides a simple date and time field for Laravel Nova, making it easy to add date/time selection to resource forms and manage datetime attributes in your admin panel.
This is a small, single-purpose Nova field package that gives you a combined date + time picker in a single form input. Treat it as a utility field, not a framework — read the source before you rely on it.
Install
composer require jagdeepbanga/nova-date-time
Then register the field's service provider (Nova 3-era packages usually require this explicitly):
// config/app.php
'providers' => [
// ...
Jagdeepbanga\NovaDateTime\FieldServiceProvider::class,
],
Run php artisan vendor:publish --tag=nova-date-time if a publish tag exists, and php artisan nova:publish so the compiled assets land in public/vendor/nova-date-time.
Where to look first
vendor/jagdeepbanga/nova-date-time/src/ — the field class. Note the exact namespace and constructor signature; the README may lag behind the code.resources/js/field.js (or components/) — tells you which JS datepicker it wraps and what props it accepts (format, displayFormat, twelveHourTime, etc.).dist/js/field.js — the pre-compiled asset that actually ships. If this is stale, the source is irrelevant.First use case
Drop it into a resource form in place of a plain Text field for something like a scheduled publish time:
use Jagdeepbanga\NovaDateTime\NovaDateTime;
public function fields(Request $request)
{
return [
NovaDateTime::make('Publishes At')
->rules('required', 'date'),
];
}
Make sure the underlying column is a real datetime (not timestamp, which caps at 2038 on MySQL) and that your model casts it:
protected $casts = ['publishes_at' => 'datetime'];
Baseline field with explicit formats
Always set both the storage format and the display format, because the picker's JS format string and PHP's date() format are different languages and the package may pass one straight through to the other:
NovaDateTime::make('Starts At')
->format('YYYY-MM-DD HH:mm:ss') // what the JS picker emits
->displayFormat('DD MMM YYYY, HH:mm') // what index/detail show
->required()
->sortable(),
Timezone handling — be deliberate
Nova's own DateTime field stores UTC and renders in the app timezone via ->timezone(). A 2020 community field frequently does neither. Pick one of these and be consistent:
// Option A: let the app timezone do the work
// config/app.php => 'timezone' => 'UTC', render with ->displayUsing()
NovaDateTime::make('Starts At')
->displayUsing(fn ($value) => $value?->timezone(config('app.display_timezone'))->format('M j, Y g:i A')),
// Option B: store local time, document it loudly, never convert
If you need both display and input in a non-UTC zone, pair the field with a Timezone helper or a static Select and convert in the model accessor/mutator rather than fighting the field.
Default to "now" and nullable clearing
NovaDateTime::make('Responded At')
->default(fn () => now())
->nullable()
->hideFromIndex(),
Test the clear button explicitly — custom pickers often emit null on one code path and an empty string on another, and empty string will fail a date rule while null passes.
Resource-wide consistency via a subclass
Since the package has no global config, wrap it once and use your wrapper everywhere:
namespace App\Nova\Fields;
class AppDateTime extends \Jagdeepbanga\NovaDateTime\NovaDateTime
{
public function __construct($name, $attribute = null, $resolveCallback = null)
{
parent::__construct($name, $attribute, $resolveCallback);
$this->format('YYYY-MM-DD HH:mm:ss')
->displayFormat('D, M j Y · H:i')
->sortable();
}
}
This is your single upgrade point if you later swap to Nova's native DateTime field.
Filters and metrics still need plain SQL
The field only affects presentation. Date-range Nova filters work against the raw column:
public function apply(Request $request, $query, $value)
{
[$from, $to] = explode(' - ', $value);
return $query->whereBetween('publishes_at', [$from, $to]);
}
Keep filter inputs as Date fields — reusing the picker in filters is usually more trouble than it's worth.
Validation that survives the JS layer
Client-side picker constraints are cosmetic; validate server-side:
NovaDateTime::make('Ends At')
->rules('required', 'date', 'after:starts_at'),
Use after_or_equal with ->creationRules() / ->updateRules() when the same column is compared to itself on update.
Nova::script / Nova::style replacing the old public/vendor convention) and the Nova 4/5 field lifecycle. Verify it against your Nova major version on a scratch resource before committing. If it breaks, Nova ships a native DateTime field in modern versions — migrating is usually the cheaper path than patching.dist/ is current. Small packages often ship source changes without recompiling. If the picker doesn't reflect documented props, diff resources/js against dist/js and run the build yourself.format() may feed moment/flatpickr syntax while displayFormat() feeds PHP. Setting one and assuming the other is a common source of "shows fine, saves garbage."config/app.php timezone and the picker emits local time, you can silently shift timestamps by hours. Write one test that asserts the exact UTC string in the database after a form save.composer update, run php artisan nova:publish and hard-refresh; a stale field.js in public/vendor is the most likely cause of "my change did nothing."->sortable() sorts by the raw column; mixed NULLs sort inconsistently across MySQL/Postgres. Add a COALESCE via ->sortable(fn ($query, $direction) => ...) if it matters.displayFormat('Y-m-d H:i') or ->hideFromIndex() and surface the value in detail only.->type() after ->click(), or skip the UI and test the save path via the Nova API.app/Nova/Fields (or a private vendor path) so a future composer update can't yank your field out from under you.How can I help you explore Laravel packages today?