cakephp/chronos
Chronos provides immutable date/time objects for PHP, helping prevent accidental mutations and side effects. Includes ChronosDate for calendar dates fixed at 00:00:00, plus convenient APIs inspired by Carbon but no longer extending DateTime.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require cakephp/chronos
Ensure PHP 8.1+ is used (check version map).
First Use Case:
Replace native DateTime with Chronos for immutability:
use Cake\Chronos\Chronos;
$now = Chronos::now(); // Immutable DateTime
$tomorrow = $now->addDay(); // Returns new instance
Key Classes:
Chronos: Immutable datetime (extends DateTimeImmutable).ChronosDate: Calendar-only (time frozen to 00:00:00).ChronosTime: Time-only (date irrelevant).Where to Look First:
$deadline = Chronos::now()
->addDays(7)
->setTime(23, 59, 59);
add*()/sub*() (e.g., addHours(2)).modify() (returns new instance; assign to variable).set*() (e.g., setYear(2025)).ChronosDate for date arithmetic (ignores time):
$eventDate = new ChronosDate('2024-12-25');
$nextWeek = $eventDate->addWeek(); // '2025-01-01'
ChronosTime for recurring schedules:
$openingTime = ChronosTime::create(9, 0, 0);
$closingTime = $openingTime->addHours(8);
Chronos::setTestNow('2024-01-15 10:00:00');
// All Chronos instances return this time.
Chronos::setTestNow(null); // Reset.
Chronos::withTestNow('2024-01-15 10:00:00', fn() => {
$now = Chronos::now(); // Only affected inside closure.
});
$clock = new Cake\Chronos\ClockFactory('UTC');
class OrderService {
public function __construct(private Cake\Chronos\ClockInterface $clock) {}
public function createOrder() {
return new Order(createdAt: $this->clock->now());
}
}
$period = ChronosDate::today()->period('P1D', 7); // 7 days
foreach ($period as $date) {
// Process each date.
}
$recurrence = Chronos::now()->nextOccurrenceOf('next Monday');
$utcTime = Chronos::now()->shiftTimezone('UTC');
$localTime = $utcTime->shiftTimezone('America/New_York');
$elapsed = $start->addElapsedHours(2); // Avoids DST pitfalls.
$data = Chronos::now()->toArray(); // ['year' => 2024, ...]
$native = Chronos::now()->toDateTimeImmutable();
Eloquent Models:
Chronos:
protected $casts = [
'created_at' => Chronos::class,
];
public function getFormattedDateAttribute() {
return $this->created_at->format('Y-m-d');
}
Request Handling:
use Cake\Chronos\Chronos;
$date = Chronos::createFromFormat('Y-m-d', request('event_date'));
Jobs/Queues:
ClockInterface for time-aware jobs:
class SendReminderJob implements ShouldQueue {
public function __construct(private ClockInterface $clock) {}
public function handle() {
$dueTime = $this->clock->now()->addHours(1);
// ...
}
}
API Responses:
Chronos to ISO strings:
return response()->json(['event' => $event->date->toIso8601String()]);
Validation:
use Illuminate\Validation\Rule;
$validator->addRules([
'end_date' => [
'required',
Rule::unique('events')->ignore($event),
function ($attribute, $value, $fail) {
if ($value < Chronos::now()->toDateTimeImmutable()) {
$fail('Date must be in the future.');
}
},
],
]);
Mutability Missteps:
$date = Chronos::now();
$date->modify('+1 day'); // Silent failure (no effect).
$date = $date->modify('+1 day');
Time Zone Confusion:
Chronos::now() uses the server’s default timezone unless specified.$utcTime = Chronos::now('UTC');
DST Transitions:
addHours() may skip/duplicate hours during DST changes.$safeTime = $start->addElapsedHours(1); // Uses Unix timestamps.
TestNow Leaks:
setTestNow() can cause flaky tests.withTestNow() for scoped freezing.DatePeriod Edge Cases:
P0D) cause infinite loops.if ($interval === 'P0D') {
throw new InvalidArgumentException('Invalid interval');
}
Carbon Compatibility:
Carbon or DateTime. Avoid mixing APIs.Carbon::parse() won’t work with Chronos instances.Inspect Objects:
toArray() for debugging:
dd(Chronos::now()->toArray());
Chronos::now()->getTimezone()->getName();
Format Strings:
$date->format('Y-m-d H:i:s'); // ISO-like
$date->format('D, M j, Y'); // "Mon, Jan 1, 2024"
Time Zone Issues:
$utcTime = Chronos::now('UTC');
Performance:
Chronos::now() calls in loops (cache the result).How can I help you explore Laravel packages today?