DEV Community

david duymelinck
david duymelinck

Posted on

Dates and periods in PHP

After seeing the following post

The deep dive instinct in me wanted to explore more about PHP date handling.

The thing that put me over the edge to start it is easter_date. Especially the workaround for the timezone.

The basics

I hope everyone has moved away from most of the date/time functions and uses the DateTime or even better the DateTimeImmutable classes.

$year = 2026;
$month = 4;

// old
$lastDayOfMonth = date('d', strtotime("last day of $year-$month"));
// new
$lastDayOfMonth = new DateTimeImmutable("last day of $year-$month")->format('d');
// alternative
$lastDayOfMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
Enter fullscreen mode Exit fullscreen mode

The alternative function comes from the calendar extension. Most of the times this extension is added to PHP. And it provides a few useful functions, like easter_day I mentioned before. But be aware that it could be missing.

To manipulate the date there are the add and sub methods.

$year = 2026;
$month = 4;

$firstDayOfMonth = new DateTimeImmutable("$year-$month-1");
$lastDayOfMonth = $firstDayOfMonth->add(DateInterval::createFromDateString("1 month - 1 day"));
Enter fullscreen mode Exit fullscreen mode

The example is a long way to get the last day of the month. It does introduce the DateInterval class.

The DateInterval class is useful when periods come into play.

$year = 2026;
$month = 4;

$firstDate = new DateTimeImmutable("$year-$month-23")->add(new DateInterval('P1M'));
$secondDate = new DateTimeImmutable("$year-$month-14");

echo $firstDate->diff($secondDate)->format('%r%a') . ' days between both dates'; 
// -39 days between both dates
Enter fullscreen mode Exit fullscreen mode

More advanced

The Period library that started my curiosity provides a nicer way of working with periods, but PHP also provides a DatePeriod class.

$year = 2026;
$month = 4;
// library
$period = Period::fromMonth($year, $month);

$period->startDate;
$period->endDate;
$period->dateInterval();

foreach ($period->dateRangeForward(new DateInterval('P1D')) as $datepoint) {
    $datepoint->format('Y-m-d');
}

//  PHP
$period = $period = new DatePeriod(new DateTimeImmutable("$year-$month-1"), new DateInterval('P1D'), new DateTimeImmutable("last day of $year-$month"));

$period->start;
$period->end;
$period->interval;

foreach($period as $datepoint) {
    $datepoint->format('Y-m-d');
} 
Enter fullscreen mode Exit fullscreen mode

Where the Period library shines is by providing shortcuts and comparison functions.
But if you don't need all those functions a Dateperiod child class can make the code easier to work with.

class ComparableDatePeriod extends DatePeriod
{
    public static function createMonth(int $year, int $month): static {
        return new static(new DateTimeImmutable("$year-$month-1"), new DateInterval('P1D'), new DateTimeImmutable("last day of $year-$month"));
    }

    public static function withInterval(DateTimeImmutable $start, DateInterval $interval): static  {
        return new static($start, new DateInterval('P1D'), $start->add($interval));
    }

    public function containsDay(DateTimeImmutable $day) : bool {
        foreach($this as $d) {
            if($d == $day) {
                return true;
            }
        }

        return false;
    }

    public function containsPeriod(ComparableDatePeriod $period) {
        $hasBeginAndEnd = 0;

        foreach($this as $day) {
            if($day == $period->start) {
                $hasBeginAndEnd++;
            }

            if($day == $period->end) {
                $hasBeginAndEnd++;
            }
        }

        return $hasBeginAndEnd == 2;
    }
}


$year = 2026;
$month = 4;
$period = ComparableDatePeriod::createMonth($year, $month);

$period->containsDay(new DateTimeImmutable("$year-5-1"));

$period->containsPeriod(ComparableDatePeriod::withInterval(new DateTimeImmutable("$year-$month-4"), new DateInterval('P1M')));
Enter fullscreen mode Exit fullscreen mode

Conclusion

The relative formats in PHP are very powerful because they not only provide a more human way of setting the date, but it also comes with date calculations.

Next time people suggest the Carbon library as the default, think of what the application needs before adding it.

Top comments (0)