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

kdyby/datetime-provider

Kdyby/DatetimeProvider is a Nette Framework extension that registers a DateTime provider service for obtaining the current date and time. Inject it instead of creating DateTime instances directly, making time-dependent code testable, mockable, and configurable via NEON configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and register its Nette DI extension:

composer require kdyby/datetime-provider
# config/common.neon
extensions:
    datetimeProvider: Kdyby\DateTimeProvider\DI\DateTimeProviderExtension

The package exposes a single injectable abstraction — a provider that returns a DateTimeImmutable representing "now." Everything else in the package (current vs. fixed providers, timezone handling, convenience getters) is built around that contract. Look first at the interfaces/classes under src/Provider/ (a ProviderInterface plus a "current" implementation backed by the system clock and a "fixed" implementation for tests) and the DI extension that wires the current provider as the default service.

Your first use case is almost always replacing new \DateTime() / new \DateTimeImmutable() / date() scattered through services with injected time:

use Kdyby\DateTimeProvider\Provider\ProviderInterface;

final class InvoiceIssuer
{
    public function __construct(private ProviderInterface $clock) {}

    public function issue(Order $order): Invoice
    {
        return new Invoice($order, $this->clock->getNow());
    }
}

Once the current provider is bound by the extension, the rest of the request reads a time consistent with a single call, which is what the package was designed for: log correlation, cache TTLs, "issued at" timestamps, and audit fields all reflecting the same instant rather than a clock that drifts between calls.

Implementation Patterns

Constructor injection. Always inject the provider; never fetch it from the container inside methods. This makes dependencies explicit and lets you swap the implementation in tests without touching production wiring.

Freeze time in integration tests. Override the provider service with the fixed implementation in your test NEON:

services:
    kdyby.datetimeProvider.provider:
        factory: Kdyby\DateTimeProvider\Provider\FixedProvider('2024-06-01 12:00:00')

(Confirm the exact service ID and constructor signature against config/services.neon in the package — it's small and the source is the README.) Now every getNow() in the request returns the same value, and assertions like assertSame('2024-06-01', $invoice->getIssuedAt()->format('Y-m-d')) become deterministic.

Freeze mid-test, not just at boot. For code paths that care about "now" changing between two operations (e.g., token expiry), build a small mutable test double that implements the provider interface and returns successive values. Since the interface is tiny, this is a two-line class and does not need the package's fixed provider.

Decorate instead of subclass. When you need a provider that always returns UTC, or that returns rounded-to-second values, write a decorator over ProviderInterface and rebind the service. The package's contract is narrow enough that composition is cleaner than any other approach.

Pass DateTimeImmutable across boundaries. The provider returns an immutable object, so it is safe to pass the value out of services into entities, DTOs, and serializers without defensive copying. Do not convert to DateTime (mutable) or to a string inside the service — leave formatting to the presentation layer and keep timezone information intact.

Consistent timezone. If your app has a canonical timezone, set it on the provider (or in php.ini) rather than sprinkling setTimezone() calls through the codebase. The package's "timezone across the request" promise breaks the moment one service calls date_default_timezone_set().

Use it as a seam for scheduled work. In console commands and cron jobs, inject the provider too. It lets you run "what would this look like at midnight on Dec 31" simulations by swapping the provider, without patching sleep() or system time.

Gotchas and Tips

Treat this as an unmaintained dependency. Last release was February 2018 and there is no active maintainer signal (10 stars, no dependents). It is small enough that the risk is bounded — you can vendor it or reimplement the interface in an afternoon — but do not expect a fix for PHP 8.2+ deprecations or Nette 3.x DI changes. If you are starting fresh today, psr/clock plus a fixed clock test double gives you the same benefit with a maintained ecosystem. If the package is already in the tree, use it consistently rather than half-migrating.

License is NOASSERTION. GitHub could not auto-detect it, which means you must actually open LICENSE and confirm before shipping. Kdyby packages were historically permissive (MIT/BSD), but "probably fine" is not a compliance answer — check it, and note it in your third-party notices.

"Per request" semantics break in long-running workers. The whole value proposition is "one consistent now per request." In PHP-FPM that is trivially true. Under RoadRunner, Swoole, or a queue worker, the DI container (and therefore the provider) may outlive the request. If you use workers, explicitly rebind or reset the time source at the start of each job; otherwise your "current" time is the time the worker booted.

Do not use it to measure elapsed time. This provides wall-clock time, which is subject to NTP adjustments and DST shifts. For durations, use hrtime() or a stopwatch. Reaching for the provider in a profiler is a common and confusing mistake.

Beware stray date()/new DateTime() calls. The abstraction only helps if it is actually used. Add a PHPStan or Rector rule banning new \DateTime(), new \DateTimeImmutable(), date(), and time() outside tests and the provider itself. Otherwise new code will silently reintroduce the inconsistency the package exists to eliminate.

Verify the DI extension key. Kdyby extensions follow a naming convention but the exact NEON key (datetimeProvider, datetime, or the class name) and the default service ID are easy to get wrong. Run bin/console (or vendor/bin/nette) di:container --debug after registration and confirm the provider service exists and is autowired; a missing binding fails at container compile time with a confusing message.

Fixed provider is not a mock. It returns the same instant forever — it will not advance, and it ignores sleep(). If you need advancing time, write your own test double; if you need to simulate the passage of time without waiting, inject the provider and manipulate it directly rather than sleeping in the test.

Extension point is the interface, not the class. Everything useful happens through ProviderInterface. Keep new code typed against the interface (not the concrete provider) so your decorators, fixed providers, and test doubles drop in without recompiling DI configuration.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin