brick/date-time
Immutable, ISO-8601–focused date/time API for PHP 8.2+ built on top of native DateTime, adding missing types like LocalDate, LocalTime, YearMonth, and MonthDay. Inspired by Java’s JSR-310, well-tested, production-ready, Composer installable.
Immutability & Type Safety: The package’s immutable design aligns well with modern PHP/Laravel practices, reducing unintended side effects in stateful operations (e.g., date manipulations in business logic). The use of enums (DayOfWeek, Month) and strict typing (e.g., int ranges) improves code reliability and IDE support.
ISO 8601 Compliance: The adherence to ISO 8601 standards ensures consistency with global systems (e.g., APIs, databases), reducing parsing/formatting edge cases. This is critical for Laravel applications handling internationalized date logic (e.g., e-commerce, scheduling).
Clock Abstraction: The Clock interface (with SystemClock, FixedClock, etc.) enables time-travel testing and deterministic behavior in tests—a significant advantage over PHP’s native DateTime, which relies on system time. This is particularly valuable for:
Complementary to Laravel: While Laravel’s Carbon dominates date handling, this library fills gaps:
LocalDate, LocalTime, YearMonth simplify domain-specific logic (e.g., calendar systems, fiscal periods).Duration) and calendar-based periods (Period), useful for billing or scheduling.brick/date-time-doctrine package bridges this library with Eloquent, enabling typed date fields (e.g., YearMonth for monthly subscriptions).use Brick\DateTime\LocalDate) make adoption straightforward. The library’s API mirrors Laravel conventions (e.g., now() methods).use Brick\DateTime\LocalDateTime;
use Carbon\Carbon;
$carbon = Carbon::parse('2023-01-01');
$brickDateTime = LocalDateTime::ofEpochSecond($carbon->getTimestamp());
This enables gradual migration of critical date logic.DateTime fields with typed alternatives (e.g., YearMonth for recurring events). However, this requires schema migrations and ORM updates.0.x versioning indicates instability. Key risks:
DayOfWeek::of() → DayOfWeek::from() or Duration string formatting changes may require refactoring. Mitigation: Lock to a patch version (e.g., 0.9.*) and monitor changelogs.FixedClock, travelTo()) require disciplined test teardown (e.g., DefaultClock::reset()). Mitigation: Use PHPUnit’s tearDown() or a testing library like Laravel’s travel() as a wrapper.YearMonth instead of Carbon for subscription logic.FixedClock for all date-dependent tests, or use a hybrid approach (e.g., Carbon for some, Brick for others)?DateTime fields in the database that could benefit from stricter typing (e.g., YearMonth for recurring events)?use Brick\DateTime\YearMonth;
use Brick\DateTimeDoctrine\Types\YearMonthType;
class Subscription extends Model {
protected $casts = [
'billingCycle' => YearMonthType::class,
];
}
FixedClock to test time-sensitive jobs (e.g., delayed notifications).LocalDateTime instead of Carbon).Carbon::setTestNow() with DefaultClock::freeze() for deterministic tests.travelTo()/travelBy() for scenario testing (e.g., "what happens 30 days after subscription?").LocalDateRange for a new "event series" feature.class CarbonAdapter {
public static function toBrickLocalDateTime(Carbon $carbon): LocalDateTime {
return LocalDateTime::ofEpochSecond($carbon->getTimestamp());
}
}
spatie/laravel-activitylog) may rely on Carbon’s internals. Mitigation: Test thoroughly or fork the package.DateTime (e.g., nesbot/carbon) may need wrappers. Example:
function toCarbon(LocalDateTime $dateTime): Carbon {
return Carbon::instance($dateTime->toDateTime());
}
composer require brick/date-time
Lock to a patch version (e.g., 0.9.*) to avoid breaking changes.DefaultClock (e.g., a TestCase trait).DateHelper) to encapsulate Brick usage.DateTimeException provide clear error messages (e.g., "Invalid day of month: 32").0.x versioning requires careful dependency management. Mitigation: Use composer require brick/date-time:0.9.* and monitor GitHub releases.How can I help you explore Laravel packages today?