Dealing with dates and times is one of those things that can frustrate programmers a lot. At the same time, they are fundamental to software development, used from everything from meta and how things are ordered to time-based triggers and lots in between.
Dates and times are prone to errors too. Handle them incorrectly, and they can confuse end-users and fellow programmers alike.
This is a quick guide to dealing with dates and times specifically in the PHP programming language. It’s meant to be a reference to the most common needs you’ll have, like formatting and adjusting dates. It’s simple, but it’s likely going to cover 80% of your needs.
Table of Contents
This research is brought to you by support from Frontend Masters, CSS-Tricks’ official learning partner.
Need front-end development training?
Frontend Masters is the best place to get it. They have courses on all the most important front-end technologies. Interested in going full-stack? Here’s your best bet:
Get the current date and time
One thing to know is that the dates and times can be represented in three forms: a timestamp (i.e. epoch time), a DateTime
object, and a string.
First up, a recipe to get the current date and time:
<?php
$now = new DateTime();
var_dump($now);
// object(DateTime)#1 (3) {
// ["date"]=>
// string(26) "2021-10-13 22:25:11.790490"
// ["timezone_type"]=>
// int(3)
// ["timezone"]=>
// string(12) "Asia/Jakarta"
// }
This provides a DateTime
object that can be used to create a date and time string:
<?php
$now = new DateTime();
echo $now->format("Y-m-d"); // 2021-10-13
echo $now->format("Y-m-d h:i:s A"); // 2021-10-13 10:10:31 PM
Intuitively, you know that Y
refers to the year, m
refers to the month, d
refers to the day of the month, and so on. The full list of the parameters can be found in the PHP manual, but I’ll drop some of the most common ones here for reference.
Day of the month | ||
d | Day of the month. two digits with leading zeros | 01 – 31 |
j | Day of the month without leading zeros | 1 – 31 |
S | Includes the English suffix. | st , nd , rd , th (e.g. 1st , 2nd , 3rd , 4th ) |
Weekday | ||
D | Abbreviated textual representation of a day, in three letters | Sun – Sat |
l | A full textual representation of a weekday. | Sunday – Saturday |
Month | ||
F | A full textual representation of a month, such as January or March | January – December |
M | Abbreviated textual representation of a month, in three letters | Jan – Dec |
m | Numeric representation of a month, with leading zeros | 01 – 12 |
n | Numeric representation of a month, without leading zeros | 1 – 12 |
Year | ||
Y | A full numeric representation of a year, 4 digits | E.g. 1999 or 2003 |
y | A two digit representation of a year | E.g. 99 or 03 |
Time | ||
A | Uppercase Ante Meridiem and Post Meridiem | AM or PM |
g | 12-hour format of an hour without leading zeros | 1 – 12 |
h | 12-hour format of an hour with leading zeros | 01 – 12 |
i | Minutes with leading zeros | 00 – 59 |
s | Seconds with leading zeros | 00 – 59 |
The DateTime
object can be converted to a timestamp:
<?php
$now = new DateTime();
echo $now->getTimestamp(); // 1634139081
But we can also get the current time in timestamp without constructing a DateTime
object:
<?php
echo time(); // 1634139081
DateTime
object of a specific time
Construct a What if we want to construct a DateTime
for a particular time, like July 14th, 2011? We can pass a formatted string date to the constructor:
<?php
$date = new DateTime("2011-07-14");
var_dump($date);
// object(DateTime)#1 (3) {
// ["date"]=>
// string(26) "2011-07-14 00:00:00.000000"
// ["timezone_type"]=>
// int(3)
// ["timezone"]=>
// string(12) "Asia/Jakarta"
// }
The constructor accepts other formats as well:
<?php
$date = new DateTime("14-07-2011");
var_dump($date);
// object(DateTime)#1 (3) {
// ["date"]=>
// string(26) "2011-07-14 00:00:00.000000"
// ["timezone_type"]=>
// int(3)
// ["timezone"]=>
// string(12) "Asia/Jakarta"
// }
But be careful with an ambiguous format, like this:
<?php
$date = new DateTime("07/14/2011");
var_dump($date);
// object(DateTime)#1 (3) {
// ["date"]=>
// string(26) "2011-07-14 00:00:00.000000"
// ["timezone_type"]=>
// int(3)
// ["timezone"]=>
// string(12) "Asia/Jakarta"
// }
You might think that everyone should be familiar with an American date format. But not everyone is and it might be interpreted differently. Not PostgreSQL.
CREATE TABLE IF NOT EXISTS public.datetime_demo
(
created_at date
);
insert into datetime_demo (created_at) values ('07/12/2011');
select created_at from datetime_demo; /* 2011-12-07 */
You may have thought that would return July 12th, 2011, but it was December 7th, 2011, instead. A better way is to use an explicit format:
<?php
$date = DateTime::createFromFormat('m/d/y', "10/08/21");
var_dump($date);
//object(DateTime)#2 (3) {
// ["date"]=>
// string(26) "2021-10-08 16:00:47.000000"
// ["timezone_type"]=>
// int(3)
// ["timezone"]=>
// string(12) "Asia/Jakarta"
//}
What if we want to construct a DateTime
object from a timestamp?
<?php
$date = new DateTime();
$date->setTimestamp(1634142890);
var_dump($date);
//object(DateTime)#1 (3) {
// ["date"]=>
// string(26) "2021-10-13 23:34:50.000000"
// ["timezone_type"]=>
// int(3)
// ["timezone"]=>
// string(12) "Asia/Jakarta"
// }
We don’t have to create a DateTime
object if we want to convert a timestamp object to a formatted date string:
<?php
echo date("Y-m-d h:i A", time()); // 2021-10-14 04:10 PM
Timezones
We can create a DateTime
object that includes timezone information, like if we’re dealing with Pacific Standard Time, Eastern Daylight Time, etc.
<?php
$timezone = new DateTimeZone("America/New_York");
$date = new DateTime("2021-10-13 05:00", $timezone);
var_dump($date);
// object(DateTime)#1 (3) {
// ["date"]=>
// string(26) "2021-10-13 05:00:00.000000"
// ["timezone_type"]=>
// int(3)
// ["timezone"]=>
// string(16) "America/New_York"
// }
// Eastern Daylight Time, for example: New York
$date = new DateTime("2021-10-13 05:00 EDT");
var_dump($date);
// object(DateTime)#2 (3) {
// ["date"]=>
// string(26) "2021-10-13 05:00:00.000000"
// ["timezone_type"]=>
// int(2)
// ["timezone"]=>
// string(3) "EDT"
// }
$date = new DateTime("2021-10-13 05:00 -04:00");
var_dump($date);
// object(DateTime)#1 (3) {
// ["date"]=>
// string(26) "2021-10-13 05:00:00.000000"
// ["timezone_type"]=>
// int(1)
// ["timezone"]=>
// string(6) "-04:00"
// }
There are three ways to create a DateTime
object with timezone information. The timezone_type
accepts different values for each one.
But say we want to convert a date and time that’s displayed in New York’s timezone to display Jakarta’s timezone instead?
<?php
$newYorkTimeZone = new DateTimeZone("America/New_York");
$date = new DateTime("2021-11-11 05:00", $newYorkTimeZone);
echo $date->format("Y-m-d h:i A"); // 2021-11-11 05:00 AM
$jakartaTimeZone = new DateTimeZone("Asia/Jakarta");
$date->setTimeZone($jakartaTimeZone);
echo $date->format("Y-m-d h:i A"); // 2021-11-11 05:00 PM
When it’s 05:00 AM in New York, it’s 05:00 PM in Jakarta on the same day. Jakarta is 12 hours ahead of New York on November 11th 2021. But one month earlier, Jakarta is only 11 hours ahead of New York as shown below:
<?php
$newYorkTimeZone = new DateTimeZone("America/New_York");
$date = new DateTime("2021-10-11 05:00", $newYorkTimeZone);
echo $date->format("Y-m-d h:i A"); // 2021-10-11 05:00 AM
$jakartaTimeZone = new DateTimeZone("Asia/Jakarta");
$date->setTimeZone($jakartaTimeZone);
echo $date->format("Y-m-d h:i A"); // 2021-10-11 04:00 PM
PHP handles Daylight Saving Time for you automatically.
Localization
This is a common way to display date and time in the United States:
<?php
$now = new DateTime();
echo $now->format("m/d/Y h:i A"); // 10/14/2021 03:00 PM
But someone in France might prefer something more common to their locale. C’est horrible, they’d complain. For one, nobody puts the month before month day, except the U.S. Second, France doesn’t use AM or PM — they use the 24-hour format (e.g. 14:00 instead of 2:00 PM) like the military. This is how you make a French local happy.
<?php
$now = new DateTime();
echo $now->format("d/m/Y H:i"); // 14/10/2021 15:00
But this requires an intimate knowledge about a specific country or area. Instead, we can localize the date. To localize a date, we need to install the internationalization support for PHP. In Ubuntu, we can do this step:
$ sudo apt-get install php-intl
To display a date and time in French, we can use IntlDateFormatter
:
$locale = "fr_FR.UTF-8";
$formatter = new IntlDateFormatter($locale, IntlDateFormatter::FULL, IntlDateFormatter::SHORT, "Asia/Singapore");
$date = new DateTime("2020-10-10 00:00 UTC");
echo $formatter->format($date); // samedi 10 octobre 2020 à 08:00
You pass the French locale as the first parameter of IntlDateFormatter
.
The second parameter is the format for the date. The third parameter is the format for the time. The timezone for displaying the date and time is in the fourth parameter.
Besides IntlDateFormatter::FULL
and IntlDateFormatter::SHORT
, other popular formats are IntlDateFormatter::NONE
, IntlDateFormatter::LONG
,
and IntlDateFormatter::MEDIUM
.
If you use IntlDateFormatter::NONE
for the time or in the third parameter, it means you don’t include the time in the format:
$locale = "fr_FR.UTF-8";
$formatter = new IntlDateFormatter($locale, IntlDateFormatter::LONG, IntlDateFormatter::NONE, "Asia/Singapore");
$date = new DateTime("2020-10-10 00:00 UTC");
echo $formatter->format($date); // 10 octobre 2020
Time travel
Let’s do time travel to the past and the future. First, let’s get acquainted with DateInterval
:
<?php
$interval = new DateInterval("P4M1W2DT2H5M");
// P 4M 1W 2D T 2H 5M
//
// P = Period interval (years, months, weeks, days)
// 4M = 4 months
// 1W = 1 week
// 2D = 2 days
//
// T = Time interval (hours, minutes, seconds)
// 2H = 2 hours
// 5M = 5 minutes
The P
and T
are to separate period interval and time interval. Here’s how we can travel to the future:
<?php
$date = new DateTime("2021-10-14");
$interval = new DateInterval("P2D"); // 2 days
$futureDate = $date->add($interval);
echo $futureDate->format("Y-m-d"); // 2021-10-16
And here’s how we go back in time:
<?php
$date = new DateTime("2021-10-14 10:00");
$interval = new DateInterval("PT6H"); // 6 hours
$pastDate = $date->sub($interval);
echo $pastDate->format("Y-m-d H:i"); // 2021-10-14 04:00
If we want to time travel with the name of the weekday, we can combine the strtotime()
function and the setTimestamp()
method of a DateTime
object:
<?php
$nextTuesday = strtotime("next tuesday");
$date = new DateTime("2021-10-14");
$date->setTimestamp($nextTuesday);
echo $date->format("Y-m-d"); // 2021-10-19
See the full list of strtotime()
parameters in the PHP docs.
Recurring dates and times
It’s a common feature in calendar apps to set a reminder that repeats every so often, like every two days or every week. We can use DatePeriod
to represent a period of time:
<?php
$start = new DateTime("2021-10-01");
$end = new DateTime("2021-11-01");
$interval = new DateInterval("P1W"); // 1 week
$range = new DatePeriod($start, $interval, $end);
// Starting from October 1st 2021 (inclusive), jump every 1 week
// until November 1st 2021 (exclusive)
foreach ($range as $date) {
echo $date->format("Y-m-d") . "n";
}
// 2022-10-01
// 2022-10-08
// 2022-10-15
// 2022-10-22
// 2022-10-29
How many days ago?
You know how services like Twitter will show that someone posted X number of minutes/hours/days/etc. ago? We can do the same thing by calculating how much time has elapsed between the the current time and when that action occurred.
<?php
$date = new DateTime("2022-10-30");
$date2 = new DateTime("2022-10-25");
$date3 = new DateTime("2022-10-10");
$date4 = new DateTime("2022-03-30");
$date5 = new DateTime("2020-03-30");
function get_period_ago($endDate, $startDate) {
$dateInterval = $endDate->diff($startDate);
if ($dateInterval->invert==1) {
if ($dateInterval->y > 0) {
return $dateInterval->y . " years agon";
} if ($dateInterval->m > 0) {
return $dateInterval->m . " months agon";
} if ($dateInterval->d > 7) {
return (int)($dateInterval->d / 7) . " weeks agon";
} if ($dateInterval->d > 0) {
return $dateInterval->d . " days agon";
}
}
}
echo get_period_ago($date, $date2); // 5 days ago
echo get_period_ago($date, $date3); // 2 weeks ago
echo get_period_ago($date, $date4); // 7 months ago
echo get_period_ago($date, $date5); // 2 years ago
After getting the DateInterval
object from the diff()
method, make sure that the $startDate
variable is in the past by checking the invert
property. Then check the y
, m
, and d
properties.
The full list of DateInterval
object properties can be found here in the PHP docs.
Where do you go from here?
Now you have a little cheatsheet of common PHP recipes for when you find yourself working with dates and times. Need to get the current date and time? Maybe you need to format a date a certain way, or include the local timezone, or compare dates. All of that is right here!
There are still more methods and functions about date and time that we haven’t discussed, of course — things like calendar-related functions and whatnot. Be sure to keep the PHP Manual’s Date and Time section close by for even more use cases and examples.
Man, i’m a PHP programmer for the last 20 years, and i never knew about the DateInterval!!!
I think tha’t i got used to carbon, and i bet that they use those functions internally!
Thanks for the article!
Carbon eases date and time handling and provides additional functionality.
Sorry, just giggling a bit at this line: “You might think that everyone should be familiar with an American date format.”
I think everyone knows Americans format dates weirdly, but that doesn’t mean we’re not fuzzy on the specifics.
I wish I had this article about 15 years ago! The only thing I think is missing is ->modify(). For example ->modify(‘+ 1 Day’)
I’m surprised to see that there were no mentions about strftime not working on Windows and being deprecated on PHP 8.1.
Thanks for the feedback! I am the author of this article. I’ll try to replace the deprecated function in this article. But since now is the end of the year, we might need to wait an update in January 2022.