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 Laravel Package

tempest/datetime

tempest/datetime offers a clean, immutable date and time API for PHP, with parsing, formatting, timezone handling, and convenient helpers for comparing and manipulating dates. A lightweight option for everyday datetime work.

View on GitHub
Deep Wiki
Context7

Getting Started

tempest/datetime is the standalone distribution of the Tempest framework's date/time layer: an immutable DateTime value object plus supporting types (timezone, timestamp, duration/interval helpers) built on top of PHP's DateTimeImmutable. Treat it as a nicer, stricter replacement for raw \DateTimeImmutable rather than as a new concept to learn.

Install

composer require tempest/datetime

If you are already inside a Tempest application you almost certainly have it transitively via the framework — check composer show tempest/datetime before adding it explicitly, and don't pin a version that fights the framework's constraint.

Where to look first

  1. src/ in this repo — the classes are small and readable; the public API is the source of truth.
  2. The tests/ directory — the de facto specification for parsing, formatting, and DST edge cases.
  3. The upstream monorepo (tempestphp/tempest-framework) for docs, issues, and the changelog. This split repo is read-only and carries no history of its own.

First use case

Replace ad-hoc native date handling in a single service with the package's value object and see whether it reads better:

use Tempest\DateTime\DateTime;

$now = DateTime::now();
$publishedAt = DateTime::parse('2024-03-01 09:30:00');

if ($publishedAt->isBefore($now)) {
    // ...
}

That is the whole pitch: one import, explicit intent, and no accidental mutation. Once that feels natural, expand outward into formatting, comparison, and duration math.

Implementation Patterns

Construction is explicit. Prefer named constructors over new. DateTime::now() for the current instant, DateTime::parse($string) for input, and a timestamp-based constructor for data coming out of storage. Keep the parsed string visible at the call site so reviewers can see the expected format.

Arithmetic returns new instances. Everything is immutable, so $date->plusDays(7) yields a new value; the variable you called it on is untouched. The idiomatic pattern is chained reassignment:

$trialEndsAt = $user->created_at
    ->plusDays(14)
    ->atStartOfDay();

This is the reason to adopt the package. Bug classes around "someone mutated the shared DateTime mid-request" simply disappear.

Formatting at the boundary, not in the model. Keep domain objects holding DateTime values and convert to strings only at the edges — controllers, view models, API resources. A single helper that maps a DateTime to your house format (ISO 8601 for APIs, a localized string for UI) keeps format strings from scattering.

Comparison instead of subtraction. Use the package's comparison helpers (isBefore, isAfter, equals, and day/period-boundary checks) rather than comparing timestamps by hand. They read better and respect timezone rules that a naive strtotime diff will get wrong.

Durations and intervals for TTLs and scheduling. Reach for the duration/interval types when expressing cache TTLs, lock windows, retry backoff, or "expires in" displays. Expressing Duration::minutes(15) beats a bare 900 scattered through config.

Integration with persistence. For Eloquent, Doctrine, or a mapper layer, write a cast/type that converts the database column into a DateTime on read and back to a string on write. Do the conversion in exactly one place per storage layer — this is where most timezone bugs enter an application.

Testing with a controlled clock. Any code that calls DateTime::now() is untestable unless "now" is injectable. Either inject a clock/DateTime provider into the service, or wrap now() in a small Clock interface of your own. Freezing time in tests is the single highest-value investment you can make when adopting this package.

Serialization. When emitting JSON, serialize to ISO 8601 with an explicit offset. Don't let the default json_encode of a custom object decide for you — implement JsonSerializable or convert in the resource layer.

Gotchas and Tips

This repo is read-only. Issues and pull requests belong upstream in tempestphp/tempest-framework. A PR here will be closed. If you need to patch behavior, fork or pin, and file upstream.

Low adoption means low blast radius — verify before relying on it. Zero stars and zero dependents is not a quality signal either way, but it does mean fewer eyes on edge cases and fewer Stack Overflow answers. Lean on the test suite and read the source for anything involving DST, leap seconds, or unusual timezones. Budget for discovering rough edges yourself.

Don't mix object families. A Tempest\DateTime\DateTime is not a \DateTimeImmutable, and type hints elsewhere in your app (or in third-party libraries) will reject it. Convert at integration seams — repositories, HTTP clients, mailers — and keep native objects from leaking inward. A stray ->toNative() in the middle of business logic usually means a boundary is in the wrong place.

Timezone defaults are ambient. The default timezone still comes from date_default_timezone_set() / php.ini. Set it explicitly at bootstrap and prefer passing an explicit timezone when parsing user-supplied input. "Works on my machine, off by an hour in prod" is almost always this.

DST arithmetic is not associative. "Plus one day" and "plus 24 hours" differ across a DST transition. Decide which one your domain means and use the corresponding operation. Test around a transition date for anything schedule-related.

Parsing is permissive until it isn't. Ambiguous formats like 01/02/2024 will resolve according to the parser's rules, not your intent. For anything user-facing, validate the format explicitly or parse with a known pattern, and surface a clear error rather than silently accepting a surprising date.

Version skew with the framework. Because this is a split, its tags may lag the monorepo. If a method you saw in the framework docs is missing, check the installed version before assuming a bug — composer show tempest/datetime first, docs second.

Debugging tip. When a comparison misbehaves, dump the underlying instant plus the timezone: dump($date->format('c'), $date->getTimezone()). Nine times out of ten the values are equal-but-different-zoned, or one side silently fell back to the server default.

Extension points. Prefer composition — a thin wrapper or a Clock abstraction — over subclassing the value object. Subclasses tend to break equality checks and casts, and they make future upgrades of the split package harder than they need to be.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core