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:
| Character | Meaning | Example |
|---|---|---|
d / j | Day with / without a leading zero | 05 / 5 |
D / l | Short / full weekday | Fri / Friday |
m / n | Month number with / without zero | 07 / 7 |
M / F | Short / full month name | Jul / July |
Y | Four-digit year | 2024 |
H:i:s | 24-hour time | 14:05:09 |
g:i A | 12-hour time | 2: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:
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:
$order = new DateTimeImmutable('2024-07-05 14:30');
$delivery = $order->modify('+3 days');
echo $delivery->format('l j F'); // Monday 8 Julyformat()uses the same characters asdate().modify('+1 month'),add(new DateInterval('P2W'))andsub()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
DateTimeclass 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:
->daysis the total number of days.->y,->mand->dare the years, months and days parts, ideal for ages.->invertis 1 if$bis before$a.
To read a date typed by a user in a specific format, use createFromFormat:
$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,iand 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 totaldaysandy,m,dparts.- 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.
Formatting an order date
<?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";Ordered: Friday 5 July 2024, 2:30 PM
Dispatched: Mon 8 Jul
Arrives by: Wednesday 10 July
For MySQL: 2024-07-05 14:30:00Line-by-line explanation
- Line 4Create a specific date and time from text.
- Lines 5 to 6"weekday" skips Saturdays and Sundays, so Friday plus one weekday is Monday.
- Line 8g:i A gives a 12-hour time with AM or PM.
- Line 11Y-m-d H:i:s is the format MySQL DATETIME columns expect.
Ages, countdowns and "days ago"
<?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";Age: 15 years, 9 months
Days until launch: 149
Posted 7 days ago
Launch is in the futureLine-by-line explanation
- Line 2A fixed "today" keeps the output repeatable. In real code, use new DateTimeImmutable() for now.
- Lines 5 to 6diff gives a DateInterval; y and m are the whole years and months.
- Line 10days is the total number of days between the two dates.
- Line 16DateTime objects compare directly with > and <.
Reading a date from a form
<?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";
}25/12/2024: 2024-12-25
31/02/2024: invalid date
tomorrow: invalid dateLine-by-line explanation
- Line 2The return type ?DateTimeImmutable means "a date, or null".
- Line 3The ! sets the time to midnight, and d/m/Y describes the expected layout.
- 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
echo date('H:i'); // server's default zone, maybe UTCdate_default_timezone_set('Europe/London');
echo date('H:i');Ignoring modify's return value
$d = new DateTimeImmutable('2024-07-05');
$d->modify('+1 day'); // result thrown away
echo $d->format('Y-m-d'); // still 2024-07-05$d = $d->modify('+1 day');m and i mixed up
echo date('H:m'); // m is the month!echo date('H:i'); // i is minutesPHP 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.
Complete the code so it prints the current four-digit year.
echo date('');
What does this print?
$d = new DateTimeImmutable('2024-03-09');
echo $d->format('d/m/Y');
What does this print?
$a = new DateTimeImmutable('2024-07-01');
$b = $a->modify('+1 month');
echo $a->format('n') . " " . $b->format('n');
How do you get the total number of days between $a and $b?
Why use DateTimeImmutable rather than DateTime?
PHP Dates Quiz
5 questions to check what you learned.
What does
date('l')show?- The month
- A lowercase L
- The year
- The full weekday name, such as Friday
What is a Unix timestamp?
- A date string
- The number of seconds since 1 January 1970 UTC
- The current time zone
- A DateTime object
What does createFromFormat return when the input doesn't match?
- null
- An empty date
- false
- Today's date
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
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