PHP Dates and Times

Show, calculate and compare dates in PHP with date(), strtotime and the DateTimeImmutable class, including formats, intervals, differences and time zones.

Lesson 13 of 86Intermediate17 min read

Websites are full of dates: "posted 3 days ago", "your order will arrive on Friday", "offer ends in 4 hours", "member since March 2023", a copyright year in the footer. Behind each one is PHP code that gets the current date, formats it for people, calculates with it, or compares it with another date.

PHP has two ways to work with dates. The quick one is the date() function, which formats the current moment or a timestamp in one line. The more powerful one is the DateTimeImmutable class, which handles calculations, differences, user input and time zones reliably.

In this lesson you'll format dates for display, set the right time zone, add and subtract days and months, work out the difference between two dates, read dates typed by users, and store dates in a format databases understand.

date() and format characters

date(format) returns the current date and time as text, using format characters:

CharacterMeaningExample
d / jDay with / without a leading zero05 / 5
D / lShort / full weekdayFri / Friday
m / nMonth number with / without zero07 / 7
M / FShort / full month nameJul / July
YFour-digit year2024
H:i:s24-hour time14:05:09
g:i A12-hour time2:05 PM

So date("l j F Y") gives Friday 5 July 2024, and date("Y") is the classic way to show the current year in a footer. Any other character, such as /, - or a space, is printed as it is.

Timestamps and time zones

A timestamp is the number of seconds since 1 January 1970 (UTC). time() gives the current one, and date(format, $timestamp) formats any timestamp. strtotime("next Monday") or strtotime("2024-12-25") turns text into a timestamp.

PHP needs to know your time zone, or times may be hours out. Set it once, early in your app:

PHP
date_default_timezone_set('Europe/London');

Better still, set date.timezone in php.ini. Time zone names come from a standard list, such as America/New_York and Asia/Kolkata.

DateTimeImmutable

For anything beyond simple display, use DateTimeImmutable:

PHP
$order = new DateTimeImmutable('2024-07-05 14:30');
$delivery = $order->modify('+3 days');
echo $delivery->format('l j F');        // Monday 8 July
  • format() uses the same characters as date().
  • modify('+1 month'), add(new DateInterval('P2W')) and sub() return a new object. "Immutable" means the original never changes, which prevents a whole class of bugs.
  • Compare two dates directly with <, > and ==.
  • The older DateTime class has the same methods but changes itself, which is easy to get wrong.

Differences and user input

$a->diff($b) returns a DateInterval describing the gap:

  • ->days is the total number of days.
  • ->y, ->m and ->d are the years, months and days parts, ideal for ages.
  • ->invert is 1 if $b is before $a.

To read a date typed by a user in a specific format, use createFromFormat:

PHP
$d = DateTimeImmutable::createFromFormat('d/m/Y', '25/12/2024');

It returns false if the text doesn't match, so always check the result. To save a date in MySQL, format it as Y-m-d or Y-m-d H:i:s.

PHP Dates Syntax

Date essentials

date('Y-m-d H:i:s');
date('l j F Y', $timestamp);
date_default_timezone_set('Europe/London');
$d = new DateTimeImmutable('2024-07-05');
$d->format('d/m/Y');
$d->modify('+7 days');
$a->diff($b)->days;
DateTimeImmutable::createFromFormat('d/m/Y', $input);
$d->setTimezone(new DateTimeZone('Asia/Tokyo'));

PHP Dates Summary

  • date(format) formats the current time; Y, m, d, H, i and friends build the format.
  • Set the time zone with date_default_timezone_set or php.ini.
  • DateTimeImmutable's modify, add and sub return new objects; the original never changes.
  • diff() returns a DateInterval with total days and y, m, d parts.
  • createFromFormat reads user input in a known format and returns false if it doesn't match.

PHP Dates Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Formatting an order date

PHP
<?php
date_default_timezone_set('Europe/London');

$placed = new DateTimeImmutable('2024-07-05 14:30');
$dispatch = $placed->modify('+1 weekday');
$delivery = $placed->modify('+3 weekdays');

echo "Ordered:    " . $placed->format('l j F Y, g:i A') . "\n";
echo "Dispatched: " . $dispatch->format('D j M') . "\n";
echo "Arrives by: " . $delivery->format('l j F') . "\n";
echo "For MySQL:  " . $placed->format('Y-m-d H:i:s') . "\n";
Output
Ordered:    Friday 5 July 2024, 2:30 PM
Dispatched: Mon 8 Jul
Arrives by: Wednesday 10 July
For MySQL:  2024-07-05 14:30:00
Line-by-line explanation
  1. Line 4Create a specific date and time from text.
  2. Lines 5 to 6"weekday" skips Saturdays and Sundays, so Friday plus one weekday is Monday.
  3. Line 8g:i A gives a 12-hour time with AM or PM.
  4. Line 11Y-m-d H:i:s is the format MySQL DATETIME columns expect.
Example 2

Ages, countdowns and "days ago"

PHP
<?php
$today = new DateTimeImmutable('2024-07-05');

$born = new DateTimeImmutable('2008-09-14');
$age = $born->diff($today);
echo "Age: {$age->y} years, {$age->m} months\n";

$launch = new DateTimeImmutable('2024-12-01');
$left = $today->diff($launch);
echo "Days until launch: {$left->days}\n";

$posted = new DateTimeImmutable('2024-06-28');
$ago = $posted->diff($today)->days;
echo "Posted " . ($ago === 0 ? "today" : "$ago days ago") . "\n";

echo $launch > $today ? "Launch is in the future\n" : "Already launched\n";
Output
Age: 15 years, 9 months
Days until launch: 149
Posted 7 days ago
Launch is in the future
Line-by-line explanation
  1. Line 2A fixed "today" keeps the output repeatable. In real code, use new DateTimeImmutable() for now.
  2. Lines 5 to 6diff gives a DateInterval; y and m are the whole years and months.
  3. Line 10days is the total number of days between the two dates.
  4. Line 16DateTime objects compare directly with > and <.
Example 3

Reading a date from a form

PHP
<?php
function parseUkDate(string $input): ?DateTimeImmutable {
    $date = DateTimeImmutable::createFromFormat('!d/m/Y', trim($input));
    $errors = DateTimeImmutable::getLastErrors();
    if ($date === false || ($errors && $errors['warning_count'] > 0)) {
        return null;
    }
    return $date;
}

foreach (['25/12/2024', '31/02/2024', 'tomorrow'] as $input) {
    $date = parseUkDate($input);
    echo $input . ": " . ($date ? $date->format('Y-m-d') : 'invalid date') . "\n";
}
Output
25/12/2024: 2024-12-25
31/02/2024: invalid date
tomorrow: invalid date
Line-by-line explanation
  1. Line 2The return type ?DateTimeImmutable means "a date, or null".
  2. Line 3The ! sets the time to midnight, and d/m/Y describes the expected layout.
  3. Lines 4 to 5PHP would quietly turn 31 February into 2 March and record a warning, so a warning counts as invalid too.

Common PHP Dates Mistakes

Forgetting the time zone

Don't
echo date('H:i');       // server's default zone, maybe UTC
Do
date_default_timezone_set('Europe/London');
echo date('H:i');

Ignoring modify's return value

Don't
$d = new DateTimeImmutable('2024-07-05');
$d->modify('+1 day');           // result thrown away
echo $d->format('Y-m-d');       // still 2024-07-05
Do
$d = $d->modify('+1 day');

m and i mixed up

Don't
echo date('H:m');               // m is the month!
Do
echo date('H:i');               // i is minutes

PHP Dates in Real Projects

Dates in PHP websites

Blogs show "published 3 days ago" with diff, shops calculate delivery dates that skip weekends, booking systems count nights between check-in and check-out, and subscription sites find accounts due for renewal. Laravel's Carbon library extends DateTimeImmutable with friendly extras like diffForHumans(), so everything here carries straight over.

PHP Dates FAQ

How do I get the current date in PHP?

date('Y-m-d') returns today's date as text, such as 2026-10-11. For calculations, create an object with new DateTimeImmutable(), or new DateTimeImmutable('now', new DateTimeZone('Europe/London')) for a specific zone, and format it with ->format('d/m/Y').

How do I convert a date from one format to another?

Parse it with the format it arrives in, then format it the way you need: DateTimeImmutable::createFromFormat('d/m/Y', '05/07/2024')->format('Y-m-d') gives 2024-07-05. Check that parsing didn't return false before calling format.

Is strtotime safe for dates typed by users?

It's risky for ambiguous formats. strtotime("01/02/2024") treats slashes as the US month/day order and gives 2 January, while "01-02-2024" with dashes is read as day-month-year and gives 1 February. For user input, use createFromFormat with the exact format you asked for.

Why is my time an hour out?

Usually because of the time zone: the server's default zone differs from yours, or a daylight saving change wasn't taken into account. Set date.timezone in php.ini or call date_default_timezone_set() early, store times in UTC, and convert to the visitor's zone with setTimezone() only for display.

How do I compare two dates in PHP?

Compare DateTimeImmutable objects directly with <, > or ==, which compare the actual moments. Strings in Y-m-d format also compare correctly as text, but formats like d/m/Y don't, so convert them to objects first.

PHP Dates Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Complete the code so it prints the current four-digit year.

echo date('');
Predict the outputeasy

What does this print?

$d = new DateTimeImmutable('2024-03-09');
echo $d->format('d/m/Y');
Predict the outputmedium

What does this print?

$a = new DateTimeImmutable('2024-07-01');
$b = $a->modify('+1 month');
echo $a->format('n') . " " . $b->format('n');
Multiple choicemedium

How do you get the total number of days between $a and $b?

Multiple choicehard

Why use DateTimeImmutable rather than DateTime?

PHP Dates Quiz

5 questions to check what you learned.

  1. What does date('l') show?

    • The month
    • A lowercase L
    • The year
    • The full weekday name, such as Friday
  2. What is a Unix timestamp?

    • A date string
    • The number of seconds since 1 January 1970 UTC
    • The current time zone
    • A DateTime object
  3. What does createFromFormat return when the input doesn't match?

    • null
    • An empty date
    • false
    • Today's date
  4. Which format should you use to save a date in a MySQL DATE column?

    • Y-m-d
    • d/m/Y
    • l j F
    • m-d-Y
  5. How should a site with users in many countries store times?

    • In each user's local time
    • As text like "2:30 PM"
    • Without any time zone
    • In UTC, converting to the user's zone when displaying