cakephp/chronos
Chronos is an immutable date and datetime library for PHP. It offers Chronos and ChronosDate objects that prevent accidental modification, keeping data predictable, and adds handy conveniences beyond native DateTime. Install via Composer with cakephp/chronos.
Architecture fit. Chronos is a framework-agnostic value-object library, so it slots into a Laravel codebase as a domain-layer dependency rather than an infrastructure one. The 3.x design is the interesting part for a TPM: Chronos no longer extends DateTimeImmutable and no longer implements DateTimeInterface; it is a wrapper with ChronosDate (date-only, time frozen at 00:00:00) and ChronosTime (time-only) as distinct types. That is a deliberate trade: it gives you clean domain modeling — no accidental mixing of a calendar date with an instant — and it costs you drop-in interop with every API that type-hints DateTimeInterface. For a Laravel app, that trade must be priced explicitly before adoption, because Carbon is the framework's ambient time type (now(), Eloquent date casts, validation rules, whereDate binding paths, queued job timestamps, Passport/Sanctum TTLs).
Integration feasibility. Feasible and cheap to start, because there is no framework coupling to fight: composer require cakephp/chronos, and the three genuinely differentiating capabilities are immediately usable — first-class date-only and time-only value objects, DST-safe elapsed arithmetic (addElapsedHours/Minutes/Seconds in 3.5.0, documented as true inverses of diffIn*), and a PSR-20 ClockFactory for injectable "now". The PSR-20 clock is the cleanest seam into Laravel: bind Psr\Clock\ClockInterface to ClockFactory in a service provider and inject it into domain services. The friction appears at persistence and serialization boundaries, where conversion to native DateTimeImmutable or a formatted string becomes mandatory.
Technical risk.
DateTimeInterface means no direct hand-off to Eloquent casts, the query builder, Symfony/Laravel validation, or third-party SDKs that accept DateTimeInterface. Mitigation is a thin boundary adapter (.toNativeDateTimeImmutable(), .format('Y-m-d')) applied consistently — architecturally fine, but it must be a rule, not a habit.$date->modify('+2 hours'); silently discarding the result — is the classic failure mode. Static analysis and code review catch some of it; not all.ChronosDate uses the server default timezone for its zeroed time component. Correct for calendar dates, hazardous in a multi-region deployment where "server default" is an implicit variable. Requires an explicit timezone policy (date_default_timezone_set('UTC')) as a prerequisite.createFromTimestamp() signature match. Mixed majors across a monorepo or shared package set will produce confusing type errors.createFromFormat/testNow regression that 3.3.3 reverted as a BC break. Pin exact versions and let CI gate minor/patch bumps.Dependents: 0, which is not credible for a package the CakePHP framework itself consumes — treat that field as unmetured, not as evidence of low adoption. The Last release: 2026-09-06 timestamp also appears forward-dated; verify against Packagist before using release cadence in any planning commitment. The 1,362 stars and 36.47 composite score describe a mature, modest-footprint utility, not a fast-growing one.Key questions.
DateTimeInterface and would reject a Chronos object?DATETIME at midnight today? If so, is a data migration in scope, or do we adapt at the boundary only?addElapsed* the fix?CarbonImmutable?Stack fit. Keep Laravel's native time handling at the edges and adopt Chronos inside the domain. Concretely: Carbon remains the type of Eloquent date casts, now(), validation, and HTTP serialization; Chronos types are used in services, value objects, and business rules where immutability, date-only, or time-only semantics are the point. Bind Psr\Clock\ClockInterface to Cake\Chronos\ClockFactory as a container singleton so now() becomes an injected dependency rather than a global — this is the single highest-value, lowest-risk change and it decouples the domain from both Carbon and Chronos.
Migration path (incremental, reversible).
DateTimeImmutable / string) and require them at the persistence and serialization edges. Codify with an architecture test that fails if Cake\Chronos\* appears in Eloquent models, controllers, or HTTP resources.ClockInterface → ClockFactory in a service provider; refactor the pilot module to inject it. Do not attempt to replace now() application-wide.finally (prefer Chronos 3.4's scoped withTestNow() inside the helper). Without this, travel() in tests will not affect domain code and you will get flaky, order-dependent suites.Compatibility. Confirm the PHP version map against the lowest PHP in the fleet before pinning a major; avoid running 2.x and 3.x in the same dependency graph. Pin exact versions in composer.json and let Renovate/Dependabot open minor bumps as reviewable PRs — the 3.3.2/3.3.3 episode justifies not auto-merging. Treat DateTimeInterface as the interop contract: any boundary that needs it gets a conversion, and that conversion is tested.
Sequencing. Pilot (1 sprint) → boundary adapter + clock binding + test helper (1 sprint, reusable) → 2–3 additional contexts (opportunistic) → convention doc and onboarding material. Total credible effort for a mid-sized team: 4–6 weeks of one engineer's partial time plus review overhead, with the pilot delivering value in week 2.
Maintenance. Low surface area, MIT license, semantic versioning, and release notes that document behavior changes rather than just commits — a good sign for upgrade predictability. Releases through the 3.5 line are additive and small (startOfHour, toArray, shiftTimezone, comparison methods), which means upgrade cost is mostly "read the notes, run the suite." The real maintenance burden is not the package; it is the internal convention ("when do I use ChronosDate vs. Chronos vs. CarbonImmutable?") and the conversion layer. Budget for a living one-page standard, owned by whoever owns the domain model.
Support. No commercial support tier; issues and PRs go through the CakePHP core team's volunteer cadence, which has been consistent but is not an SLA. Bus factor is concentrated in a handful of named maintainers. For a business-critical scheduling or billing subsystem, that is acceptable because the library is small, MIT, and replaceable — not because support is guaranteed. Mitigate with version pinning and an internal fork-readiness note; the codebase is small enough that a fork is a viable contingency.
Scaling. No runtime, network, or framework coupling; computation is equivalent in cost to native DateTime with modest overhead from wrapper object allocation and immutable copy-on-modify. That is irrelevant for typical request-path use and potentially relevant in tight loops over large date ranges (reporting, recurrence expansion) — measure before optimizing, and prefer arithmetic on native types inside hot loops.
Failure modes to instrument.
->modify/->add call not assigned or returned.DateTimeInterface is required. Detect: architecture tests, plus static analysis with no mixed escape hatches.ChronosDate inheriting a non-UTC server default. Detect: explicit date_default_timezone_set in bootstrap, an assertion test, and a lint of deployment configs differing per region.setTestNow directly.Ramp-up. Low for anyone fluent in Carbon or DateTime — the API is intentionally familiar, and the modify/add return-value discipline lands within an hour. Two things slow people down: un-learning Carbon shims and mutable idioms (copy-pasted Carbon snippets will not work), and AI coding assistants, which will confidently suggest Carbon methods that do not exist on Chronos and pattern-match toward DateTimeInterface-based code. Ramp-up is therefore roughly half a day of hands-on work plus a short internal note listing the five most common mistakes, and a standing expectation that reviewers check return-value assignment. A single brown-bag session and the convention doc are sufficient; no formal training program is warranted.
How can I help you explore Laravel packages today?