Calendar

Available versions:

Installation

composer require aeon-php/calendar

Getting Started

To get current time initialize GregorianCalendar through __construct or by one of available static constructors.

<?php

use Aeon\Calendar\Gregorian\GregorianCalendar;

echo GregorianCalendar::UTC()->now()->toISO8601();

GregorianCalendar implements Calendar interface which should be used for type hinting.

Calendar is always initialized for specific TimeZone

Initializing DateTime

Initialization time from string is just as easy as using \DateTime or \DateTimeInterface. Just take the string and pass it to fromString static constructor.

<?php

use Aeon\Calendar\Gregorian\DateTime;

echo DateTime::fromString('2020-01-01 00:00:00 UTC')->toISO8601();
Defining period of time

There are few ways to define period of time, it can be done by initializing two DateTime instances and creating time period since one date until another.

<?php

use Aeon\Calendar\Gregorian\DateTime;

$newYear = DateTime::fromString('2020-01-01 00:00:00 UTC');
$timePeriod = $newYear->until(
    DateTime::fromString('2020-01-10 00:00:00 UTC')
);
Iterating over time

TimePeriod is the very first step to iterate over time. In following example we are going to iterate between two points in time by 24 hours periods. Iterate method generates instance of TimePeriods which is collection of TimePeriod instances representing chunks of time between two points.

<?php

use Aeon\Calendar\Gregorian\DateTime;

$newYear = DateTime::fromString('2020-01-01 00:00:00 UTC');
$newYear->until(
        DateTime::fromString('2020-01-10 00:00:00 UTC')
    )->iterate(TimeUnit::day())
    ->each(function(TimePeriod $timePeriod) : void {
        var_dump(
            $timePeriod->start()->toISO8601(),
            $timePeriod->end()->toISO8601(),
            $timePeriod->distance()->inHours()
        );
    });
Measure elapsed time

It might look tempting to use measuring difference between two points in time to measure elapsed time but if you are looking for precise results use Stopwatch class instead which is built on top of \hrtime high resolution time php function.

<?php

use Aeon\Calendar\Stopwatch;
use Aeon\Calendar\TimeUnit;

$stopwatch = new Stopwatch();

$stopwatch->start();

\usleep(TimeUnit::milliseconds(500)->microsecond());

$stopwatch->lap();

\usleep(TimeUnit::milliseconds(700)->microsecond());

$stopwatch->stop();

var_dump($stopwatch->elapsedTime(1)->inSecondsPrecise());
var_dump($stopwatch->firstLapElapsedTime()->inSecondsPrecise());
var_dump($stopwatch->lastLapElapsedTime()->inSecondsPrecise());
var_dump($stopwatch->elapsedTime(2)->inSecondsPrecise());
var_dump($stopwatch->totalElapsedTime()->inSecondsPrecise());
Relative format interface

PHP DateTime Relative Formats are powerful and flexible, Aeon brings similar experience through object oriented API.

<?php

use Aeon\Calendar\Gregorian\GregorianCalendar;
use Aeon\Calendar\TimeUnit;

$calendar = GregorianCalendar::UTC();

$calendar->currentYear()
    ->january()
    ->lastDay()
    ->noon($calendar->timeZone())
    ->sub(TimeUnit::days(3));
Day Value Set - Collection

One of the most common use cases is to assign values into given days and then filter/map/sum/split this collection.

Let say one would like to calculate average sales from last 10 days knowing there is a gap in sales. Following example explains how to do define DayValueSet and work on it.

<?php

use Aeon\Calendar\Gregorian\Day;
use Aeon\Collection\DayValue;
use Aeon\Collection\DayValueSet;

$initialSet = DayValueSet::createWith(
    Day::fromString('-10 days'),
    Day::fromString('yesterday'),
    $initialValue = 0
);

// Fetch those values from database or any other source
$sales = [
    new DayValue(Day::fromString('-1 day'), 100),
    new DayValue(Day::fromString('-2 day'), 150),
    new DayValue(Day::fromString('-3 day'), 125),
    // Day 4 business was closed, no sales
    new DayValue(Day::fromString('-5 day'), 180),
    new DayValue(Day::fromString('-6 day'), 100),
    new DayValue(Day::fromString('-7 day'), 108),
    new DayValue(Day::fromString('-8 day'), 100),
    new DayValue(Day::fromString('-9 day'), 150),
    new DayValue(Day::fromString('-10 day'), 130),
];

$salesSet = $initialSet->put(...$sales)->sortDescending();
$totalSales = $salesSet->reduce(
    fn(int $total, DayValue $dayValue) : int => $dayValue->value() + $total, 0
);

$T10AvgSales = floor($totalSales / $salesSet->count());

/**

2020-10-08 - 100
2020-10-07 - 150
2020-10-06 - 125
2020-10-05 - 0
2020-10-04 - 180
2020-10-03 - 100
2020-10-02 - 108
2020-10-01 - 100
2020-09-30 - 150
2020-09-29 - 130
---
AVG Sales: 114

*/