anomaly/datetime-field_type
A datetime field type for the Anomaly CMS (Streams platform) on Laravel, adding date and time pickers to admin forms and handling storage and formatting of datetime values for your entries.
anomaly/datetime-field_type is not a standalone Laravel package — it is a Streams Platform add-on (PyroCMS / Anomaly CMS). If you are not inside an application that already depends on anomaly/streams-platform, this package will not do anything useful for you.
Install it as a normal Composer dependency from the root of your Anomaly/PyroCMS application:
composer require anomaly/datetime-field_type
The add-on's service provider is auto-discovered by the Streams add-on loader, but you may need to publish its assets and views if the datetime widget does not render:
php artisan addon:publish anomaly.datetime_field_type
where the first argument is the add-on namespace (sometimes written anomaly/datetime_field_type — check the namespace/slug in the package's addon.php if the command rejects it).
Where to look first when orienting yourself in the repo:
src/DatetimeFieldType.php — the field type class and its default $config (mode, timezone, formats).src/DatetimeFieldTypePresenter.php — the methods you will actually call in templates (format(), date(), time(), iso(), timezone(), …).resources/views/input.twig — the form input and the JavaScript picker configuration.resources/config/ and resources/lang/ — defaults and labels you may want to override.First use case — add a published_at datetime to an existing stream. In a stream/migration class:
protected $fields = [
'published_at' => [
'type' => 'anomaly.field_type.datetime',
'config' => [
'mode' => 'datetime', // 'date', 'time', or 'datetime'
'timezone' => 'UTC',
'date_format' => 'Y-m-d',
'time_format' => 'H:i',
],
],
];
Run the migration, the field appears in the Streams admin, and published_at is now a first-class field on your entries.
The field type is driven entirely by its config block. Typical patterns:
'mode' => 'date', drop time_format.'mode' => 'time', drop date_format.'mode' => 'datetime' (the default).'min' / 'max' so the picker and the underlying widget constrain the value.Because config is stored on the field definition, you can change the format or timezone later without touching code — just re-save the field in the admin or re-run the migration.
In templates (Twig), entry is a presenter, so entry.published_at is a DatetimeFieldTypePresenter:
{# Default rendering — usually the configured date_format/time_format #}
{{ entry.published_at }}
{# Explicit formatting; accepts any PHP date() format string #}
{{ entry.published_at.format('M j, Y g:i A') }}
{# Convenience accessors #}
{{ entry.published_at.date() }} {# date portion in the configured format #}
{{ entry.published_at.time() }} {# time portion #}
{{ entry.published_at.iso() }} {# ISO-8601, safe for <time datetime="..."> #}
{{ entry.published_at.timezone() }} {# the configured timezone label #}
A common pattern for semantic markup:
<time datetime="{{ entry.published_at.iso() }}">
{{ entry.published_at.format('F j, Y') }}
</time>
toArray() is useful when you need the decomposed pieces (datetime/date/time/iso) for an API response:
{% set parts = entry.published_at.toArray() %}
{{ parts.iso }}
On the raw Eloquent model, the attribute is the stored string, not the presenter:
$model->published_at; // raw DB string (e.g. "2024-05-01 12:00:00")
$model->present()->published_at; // DatetimeFieldTypePresenter
If you need a Carbon instance, either use the presenter or cast yourself:
$date = \Illuminate\Support\Carbon::parse($model->published_at);
Because the value is persisted as a real date/datetime column, query it directly and keep the timezone in mind:
$posts = $posts->where('published_at', '<=', now())->orderBy('published_at', 'desc')->get();
For "published today" logic, prefer whereDate() / whereBetween() over string comparison.
The field type registers its own input view, so the form builder renders the widget automatically:
{{ form.fields.published_at.input|raw }}
Validation is just standard Laravel rules passed through the field definition:
'published_at' => [
'type' => 'anomaly.field_type.datetime',
'rules' => ['required', 'date', 'after:now'],
'config' => [/* ... */],
],
UTC). Displaying with format() shows the stored zone, not the visitor's. If you need per-user timezones, convert explicitly in the presenter or with Carbon before rendering. Be consistent: pick UTC for storage and convert at the edges.date_format and time_format are PHP date() strings. The client-side picker has its own format tokens and they do not always map 1:1. A mismatch shows a correct value in the DB but a mangled input in the admin UI — verify both sides after changing formats.nullable fields still produce a presenter object. Guard before calling methods: {% if entry.published_at %}…{% endif %} or $entry->published_at ? $entry->published_at->format('Y') : null. Calling methods on a null value will blow up.'mode' => 'date' field should sit on a date column; 'time' on a time column; 'datetime' on datetime/timestamp. Streams will create the column from the field definition, but existing columns need manual migrations.min/max are UI hints, not validation. The picker restricts input in the browser; always add a rules entry (after:, before:, date_format:) if the constraint matters server-side.iso() and format() return strings that Twig escapes by default. That's normally what you want; if you are injecting into an attribute, use |raw only when you control the format string.php artisan addon:publish and hard-refresh. In cached production setups, clear the view cache after publishing (php artisan view:clear).vendor/. For custom logic, extend DatetimeFieldType and register your own field type slug — the add-on is MIT licensed and small enough to fork.assertSee(date(...)) around a known UTC value) so a Streams Platform upgrade that changes the presenter is caught early.How can I help you explore Laravel packages today?