Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Datetime Field Type Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

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.

Implementation Patterns

Defining and configuring fields

The field type is driven entirely by its config block. Typical patterns:

  • Date-only column (e.g. a birthday): 'mode' => 'date', drop time_format.
  • Time-only column (e.g. a daily schedule): 'mode' => 'time', drop date_format.
  • Full timestamp: 'mode' => 'datetime' (the default).
  • Server-side bounds: set '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.

Reading values in the presenter layer

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 }}

Reading values in PHP

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);

Querying

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.

Forms and validation

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' => [/* ... */],
],

Gotchas and Tips

  • Timezone drift is the #1 footgun. The field stores a value in the configured timezone (default 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.
  • PHP format vs. JavaScript format. 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.
  • DB column type must match the mode. A '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.
  • Auto-escaping in Twig. 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.
  • Assets not loading / picker missing. Re-run php artisan addon:publish and hard-refresh. In cached production setups, clear the view cache after publishing (php artisan view:clear).
  • Overriding behaviour. Prefer publishing and overriding the Twig input view or the config defaults rather than editing vendor/. For custom logic, extend DatetimeFieldType and register your own field type slug — the add-on is MIT licensed and small enough to fork.
  • Low maintenance signal. With only a handful of stars and zero dependents listed, treat upstream fixes as unlikely. Pin the Composer version, and if you rely on timezone conversion, cover it with your own feature tests (assertSee(date(...)) around a known UTC value) so a Streams Platform upgrade that changes the presenter is caught early.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums