frostealth / kronika
DateTime value objects such as Date, Time, LocalDateTime, etc.
Installs: 232
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/frostealth/kronika
Requires
- php: ^8.4
Requires (Dev)
- ext-bcmath: *
- ext-gmp: *
- doctrine/dbal: ^4.3
- illuminate/config: ^12.0
- illuminate/database: ^12.0
- illuminate/support: ^12.0
- jms/serializer: ^3.32
- phpunit/phpunit: ^12.4
- psr/clock: ^1.0
- symfony/serializer: ^7.3
- vimeo/psalm: ^6.13
- yiisoft/yii2: ^2.0.53
Provides
- psr/clock-implementation: ^1.0
README
The library provides date-time value objects such as "Date", "Time", "LocalDateTime", etc.
Installation
The recommended way to install Kronika is through Composer.
composer require frostealth/kronika
Version Guidance
| Version | Status | Branch | PHP Version |
|---|---|---|---|
| 0.2 | latest | 0.x | ^8.4 |
| 0.1 | support | 0.1 | >=8.3,<=8.5 |
Usage
- Date
- Time
- LocalDateTime
- ZonedDateTime
- Clock
- Range:
- Extensions:
Date
Kronika\Date represents a date without specifying a time of day.
The following units of date are available:
YearMonthDayOfMonthDayOfWeekDayOfYear
// creating the "Date" instance $date = Date::of(year: 2025, month: 12, day:31); // or from "\DateTimeInterface" $date = Date::ofDateTime(new \DateTimeImmutable('2025-12-31')); // or from a date string $date = Date::parse('2025-12-31'); // formatting the "Date" echo $date->format('Y-m-d'); // '2025-12-31' echo $date->format('l, F jS, Y.'); // 'Wednesday, December 31st, 2025.' $year = $date->year(); // Year::of(2025) $month = $date->month(); // Month::of(12) $day = $date->day(); // DayOfMonth::of(31) $dayOfWeek = $date->dayOfWeek(); // DayOfWeek::of(3) // changing the year, month, day, weekday $date = $date->with(Year::of(2026)); echo $date->format('l, F jS, Y.'); // 'Thursday, December 31st, 2026.' echo $date->is(Year::of(2026)); // true $date = $date->with(Month::January)->with(DayOfMonth::of(12)); echo $date->format('l, F jS, Y.'); // 'Monday, January 12th, 2026.' echo $date->is(DayOfMonth::of(12)); // true $date = $date->startOfMonth(); echo $date->format('l, F jS, Y.'); // 'Thursday, January 1st, 2026.' // changing the day of week $date = $date->with(DayOfWeek::Friday); echo $date->format('l, F jS, Y.'); // 'Friday, January 2nd, 2026.' echo $date->is(DayOfWeek::Friday); // true // adding an amount of days $date = $date->add(Duration::of(days: 3)); echo $date->format('l, F jS, Y.'); // 'Monday, January 5th, 2026.' // subtracting an amount of days $date = $date->sub(Duration::of(hours: 48)); echo $date->format('l, F jS, Y.'); // 'Saturday, January 3rd, 2026.' // getting the duration from one date to another one $duration = $date->until(Date::of(year: 2026, month: 1, day: 5)); echo $duration->days(); // 2 echo $duration->hours(); // 0 echo $duration->minutes(); // 0
Time
Kronika\Time represents a time of day without specifying a date.
The following units of time are available:
HourMinuteSecond
// creating the "Time" instance $time = Time::of(hour: 9, minutes: 10, seconds:30); // or from "\DateTimeInterface" $time = Time::ofDateTime(new \DateTimeImmutable('09:10:30')); // or from time string $time = Time::parse('09:10:30.000000'); // formatting the "Time" echo $time->format('H:i:s'); // '09:10:30' echo $time->format('H:i:s.u'); // '09:10:30.000000' $hour = $time->hour(); // Hour::of(9) $minute = $time->minute(); // Minute::of(10) $second = $time->second(); // Second::of(30) // changing the hour, minute and second $time = $time->with(Hour::of(12)); echo $time->format('H:i:s'); // '12:10:30' echo $time->is(Hour::of(12)); // true $time = $time->with(Minute::of(30))->with(Second::zero()); echo $time->format('H:i:s'); // '12:30:00' // comparing time or its unit to another one $other = $time->with(Second::of(0, micro: 999999)); echo $time->is($other); // false echo $time->is($other, Precision::Second); // true echo $time->is( // true $other->with(Second::of(59)), Precision::Minute, ); // adding an amount of hours, minutes, seconds $time = $time->add(Duration::of(hours: 3, minutes: 30, seconds: 30)); echo $time->format('H:i:s'); // '16:00:30' // subtracting an amount of hours, minutes, seconds $time = $time->sub(Duration::of(hours: 2, minutes: 120, seconds: 30)); echo $time->format('H:i:s'); // '12:00:00' // getting the duration from one time to another one $duration = $time->until(Time::of(hour: 18, minute: 30, second: 30)); echo $duration->hours(); // 6 echo $duration->minutes(); // 30 echo $duration->second(); // 30 echo $duration->inMinutes(); // 390
LocalDateTime
Kronika\LocalDateTime represents a local date-time without time-zone.
// creating the "LocalDateTime" instance $date = Date::of(year: 2025, month: 12, day: 31); $time = Time::midday(); $datetime = LocalDateTime::of($date, $time); // or $datetime = $date->at($time); // or from "\DateTimeInterface" $datetime = LocalDateTime::ofDateTime(new \DateTimeImmutable('2025-12-31 12:00:00')); // or from a date-time string $datetime = LocalDateTime::parse('2025-12-31 12:00:00'); // formatting the "LocalDateTime" echo $datetime->format('Y-m-d H:i:s'); // '2025-12-31 12:00:00' $year = $datetime->year(); // Year::of(2025) $month = $datetime->month(); // Month::of(12) $day = $datetime->day(); // DayOfMonth::of(31) $hour = $datetime->hour(); // Hour::of(12) $minute = $datetime->minute(); // Minute::zero() $second = $datetime->second(); // Second::zero() $date = $datetime->date(); // Date::of(2025, 12, 31) $time = $datetime->time(); // Time::of(12, 0, 0) // changing the year, month, day, hour, minute and second is similar to "Date" and "Time" $datetime = $datetime->with(Hour::of(18))->with(Minute::of(30)); echo $datetime->format('Y-m-d H:i:s'); // '2025-12-31 18:30:00' echo $datetime->is(Hour::of(18)); // true // adding and subtracting an amount of days, hours, // minutes and seconds are similar to "Date" and "Time" $datetime = $datetime->add(Duration::of(hours: 6, minutes: 30, seconds: 30)); echo $datetime->format('Y-m-d H:i:s'); // '2026-01-01 01:00:30' $datetime = $datetime->sub(Duration::of(hours: 12, minutes: 60, seconds: 30)); echo $datetime->format('Y-m-d H:i:s'); // '2025-12-31 12:00:00' // getting the duration from one "LocalDateTime" to another one $duration = $datetime->until( LocalDateTime::of(Date::of(year: 2026, month: 1, day: 14), Time::midnight()), ); echo $duration->days(); // 13 echo $duration->hours(); // 12 echo $duration->minutes(); // 0 echo $duration->second(); // 0 echo $duration->inHours(); // 324 // getting "\DateTimeImmutable" and "\DateTime" $immutable = $datetime->toNative(new \DateTimeZone('UTC')); // "\DateTimeImmutable" $mutable = $datetime->toNativeMutable(new \DateTimeZone('UTC')); // "\DateTime"
ZonedDateTime
Kronika\ZonedDateTime represents a date-time with time-zone.
The API of Kronika\ZonedDateTime is similar to Kronika\LocalDateTime.
Kronika\ZonedDateTime class extends \DateTimeImmutable.
// creating the "ZonedDateTime" instance $date = Date::of(year: 2025, month: 12, day: 31); $time = Time::midday(); $timezone = new \DateTimeZone('UTC') $datetime = ZonedDateTime::of($date, $time, $timezone); // or $datetime = $date->at($time)->at($timezone); // or $datetime = ZonedDateTime::ofLocal(LocalDateTime::of($date, $time), $timezone); // or $datetime = LocalDateTime::of($date, $time)->at($timezone); // or $datetime = ZonedDateTime::utcOf($date, $time); // or with current time and specified time-zone $datetime = now($timezone); // or from "\DateTimeInterface" $datetime = ZonedDateTime::ofDateTime(new \DateTimeImmutable('2025-12-31 12:00:00 UTC')); // or from a date-time string with time-zone $datetime = ZonedDateTime::parse('2025-12-31 12:00:00 UTC'); // or from a date-time string without time-zone $datetime = ZonedDateTime::parse('2025-12-31 12:00:00', new \DateTimeZone('UTC')); // formatting the "ZonedDateTime" echo $datetime->format(\DateTimeInterface::ATOM); // '2025-12-31T12:00:00+00:00' $year = $datetime->year(); // Year::of(2025) $month = $datetime->month(); // Month::of(12) $day = $datetime->day(); // DayOfMonth::of(31) $hour = $datetime->hour(); // Hour::of(12) $minute = $datetime->minute(); // Minute::zero() $second = $datetime->second(); // Second::zero() $date = $datetime->date(); // Date::of(2025, 12, 31) $time = $datetime->time(); // Time::of(12, 0, 0) $timezone = $datetime->timezone(); // \DateTimeZone('UTC') $timestamp = $datetime->timestamp(); // float(1767182400.001234) // changing the year, month, day, hour, minute and second is similar to "LocalDateTime" $datetime = $datetime->with(Hour::of(18))->with(Minute::of(30)); echo $datetime->format(\DateTimeInterface::ATOM); // '2025-12-31T18:30:00+00:00' echo $datetime->is(Hour::of(18)); // true // changing the time-zone doesn't shift the time, // to shift the time use "shift()" method echo $datetime->with(new \DateTimeZone('+01:00')) ->format(\DateTimeInterface::ATOM); // '2025-12-31T18:30:00+01:00 // adding and subtracting an amount of days, hours, minutes // and seconds are similar to "LocalDateTime" $datetime = $datetime->add(Duration::of(hours: 6, minutes: 30, seconds: 30)); echo $datetime->format(\DateTimeInterface::ATOM); // '2026-01-01T01:00:30+00:00' $datetime = $datetime->sub(Duration::of(hours: 12, minutes: 60, seconds: 30)); echo $datetime->format(\DateTimeInterface::ATOM); // '2025-12-31T12:00:00+00:00' // shifting the timezone $datetime = $datetime->shift(new \DateTimeZone('+01:00')); echo $datetime->format(\DateTimeInterface::ATOM); // '2025-12-31T13:00:00+01:00' // getting the duration from one "ZonedDateTime" to another one $duration = $datetime->until(new \DateTime('2026-01-14T12:30:15+00:00')); $days = $duration->days(); // 14 $hours = $duration->hours(); // 0 $minutes = $duration->minutes(); // 30 $seconds = $duration->second(); // 15 $inHours = $duration->inHours(); // 336 // getting "\DateTimeImmutable" and "\DateTime" $immutable = $datetime->toNative(); // "\DateTimeImmutable" $mutable = $datetime->toNativeMutable(); // "\DateTime"
Clock
Kronika\Clock decouples your code from the system clock
and has the following implementations:
SystemClockreturns the current time, this is the same as doingnew \DateTime().InaccurateClockignores a second or microsecond of the current time.PsrClockimplements PSR-20: Clock.FrozenClockdoesn't move forward on its own, useful in tests.MutableClockallows to manipulate with clock, useful in tests.
TimeRange
Kronika\Range\TimeRange represents a range between two moments of day.
// creating "TimeRange" $range = TimeRange::of( since: Time::midday(), // inclusive till: Time::of(13, 30), // exclusive ); echo $range->since()->format('H:i:s'); // 12:00:00 echo $range->till()->format('H:i:s'); // 13:30:00 echo $range->contains(Time::midday()); // true echo $range->contains(Time::of(13, 30)); // false // getting each item of this range with the specified step // minimal step is 1 second foreach ($range->each(Duration::of(minutes: 30)) as $item) { echo $item->format('H:i:s'); } // 12:00:00 // 12:30:00 // 13:00:00
DateRange
Kronika\Range\DateRange represents a range between two dates.
// creating "DateRange" $range = DateRange::of( since: Date::of(2025, 12, 15), // inclusive till: Date::of(2025, 12, 18), // exclusive ); echo $range->since()->format('Y-m-d'); // 2025-12-15 echo $range->till()->format('Y-m-d'); // 2025-12-18 echo $range->contains(Date::of(2025, 12, 15)); // true echo $range->contains(Date::of(2025, 12, 18)); // false // getting each item of this range with the specified step // minimal step is 1 day foreach ($range->each(Duration::of(days: 2)) as $item) { echo $item->format('Y-m-d'); } // 2025-12-15 // 2025-12-17
DateTimeRange
Kronika\Range\DateTimeRange represents a range between two moments of time.
Both Kronika\ZonedDateTime and Kronika\LocalDateTime are supported.
// creating "DateTimeRange" $range = DateTimeRange::of( since: ZonedDateTime::parse('2025-12-15 12:30:45 +01:00'), // inclusive till: LocalDateTime::parse('2025-12-18 10:00:30'), // exclusive ); echo $range->since()->format('Y-m-d H:i:s P'); // 2025-12-15 12:30:45 +01:00 echo $range->till()->format('Y-m-d H:i:s'); // 2025-12-18 10:00:30 echo $range->contains(LocalDateTime::parse('2025-12-15 12:30:45')); // true echo $range->contains(LocalDateTime::parse('2025-12-18 10:00:30')); // false // getting each item of this range with the specified step // the type of each item will be the same as "since" // minimal step is 1 second foreach ($range->each(Duration::ofDay()) as $item) { echo $item->format('Y-m-d H:i:s P'); } // 2025-12-15 12:30:45 +01:00 // 2025-12-16 12:30:45 +01:00 // 2025-12-17 12:30:45 +01:00