PHP Date and Time
How PHP Represents Dates and Times In simple terms: Applications constantly work with dates and times — order dates, due dates, "posted 3 hours ago", age checks, and schedules. PHP provides functions
How PHP Represents Dates and Times
In simple terms: Applications constantly work with dates and times — order dates, due dates, "posted 3 hours ago", age checks, and schedules. PHP provides functions to get the current time, display dates in any format, convert text like "next Monday" into dates, and calculate differences.
Technically: PHP offers two approaches:
| Approach | Main Tools | Best For |
|---|---|---|
| Procedural, timestamp-based | time(), date(), mktime(), strtotime(), checkdate() | Quick formatting and simple calculations |
| Object-oriented | DateTimeImmutable, DateTime, DateInterval, DatePeriod, DateTimeZone | Calculations, time zones, and any non-trivial logic |
This lesson covers the fundamentals and the procedural functions. Date Formatting, DateTime Class, and Timezones cover the rest of this section.
Unix Timestamps
A Unix timestamp is the number of seconds that have passed since 1 January 1970, 00:00:00 UTC (the "Unix epoch"). It is a single integer that identifies a moment in time, independent of time zones.
<?php
$now = time();
var_dump(is_int($now));
echo "Seconds since 1970: " . $now . "\n";
Output (the number depends on when you run it)
bool(true)
Seconds since 1970: 1792054800
Why Timestamps Are Useful
- They are easy to compare: a larger timestamp is a later moment.
- Differences are simple arithmetic:
$end - $startgives seconds. - They are independent of the time zone; the same timestamp represents the same instant everywhere.
Their limitation: a timestamp alone does not say how a person in a particular place should see that moment. Converting to a readable date always involves a time zone.
Year 2038: On old 32-bit systems, timestamps overflowed on 19 January 2038. Modern PHP on 64-bit systems uses 64-bit integers, which covers billions of years. The
DateTimeclasses do not have this limit on any platform.
Setting the Default Time Zone
Functions such as date() convert timestamps using the default time zone. Set it explicitly so results do not depend on server configuration:
<?php
date_default_timezone_set('Asia/Kolkata');
echo date_default_timezone_get() . "\n";
Output
Asia/Kolkata
The default can also be set in php.ini with date.timezone = Asia/Kolkata. If neither is set, PHP uses UTC. Time zones are covered in detail in Timezones.
date() — Formatting a Timestamp
Syntax
date(string $format, ?int $timestamp = null): string
Without a timestamp, date() formats the current time.
Example
<?php
date_default_timezone_set('Asia/Kolkata');
$timestamp = 1792054800; // 15 October 2026, 14:30 in India
echo date('Y-m-d H:i:s', $timestamp) . "\n";
echo date('d/m/Y', $timestamp) . "\n";
echo date('l, jS F Y', $timestamp) . "\n";
echo date('h:i A', $timestamp) . "\n";
Output
2026-10-15 14:30:00
15/10/2026
Thursday, 15th October 2026
02:30 PM
Each letter in the format string stands for a part of the date (Y = four-digit year, m = month with leading zero, l = weekday name, A = AM/PM, and so on). Characters that are not format letters, such as -, /, :, and spaces, are copied as they are. The full list is in Date Formatting.
mktime() — Creating a Timestamp From Parts
Syntax
mktime(int $hour, ?int $minute = null, ?int $second = null, ?int $month = null, ?int $day = null, ?int $year = null): int|false
<?php
date_default_timezone_set('Asia/Kolkata');
$timestamp = mktime(14, 30, 0, 10, 15, 2026);
echo $timestamp . "\n";
echo date('D, d M Y H:i', $timestamp) . "\n";
echo date('Y-m-d', mktime(0, 0, 0, 13, 1, 2026)) . "\n";
echo date('Y-m-d', mktime(0, 0, 0, 3, 0, 2028)) . "\n";
Output
1792054800
Thu, 15 Oct 2026 14:30
2027-01-01
2028-02-29
- The order of arguments is hour, minute, second, month, day, year — easy to mix up.
- Out-of-range values roll over: month 13 of 2026 becomes January 2027, and day 0 of March becomes the last day of February — a common trick for finding the last day of a month (2028 is a leap year).
- The parts are interpreted in the default time zone.
strtotime() — Converting Text to a Timestamp
strtotime() understands a wide range of English date expressions, both absolute and relative.
Syntax
strtotime(string $datetime, ?int $baseTimestamp = null): int|false
Relative expressions are calculated from $baseTimestamp (default: now).
Example
<?php
date_default_timezone_set('Asia/Kolkata');
$base = mktime(14, 30, 0, 10, 15, 2026); // Thursday, 15 October 2026, 14:30
$expressions = [
'2026-12-25',
'+7 days',
'+1 week 2 days',
'next monday',
'last day of february 2028',
'first monday of january 2027',
'tomorrow 09:00',
];
foreach ($expressions as $expression) {
$result = strtotime($expression, $base);
echo str_pad($expression, 30) . date('D, d M Y H:i', $result) . "\n";
}
var_dump(strtotime('not a real date'));
Output
2026-12-25 Fri, 25 Dec 2026 00:00
+7 days Thu, 22 Oct 2026 14:30
+1 week 2 days Sat, 24 Oct 2026 14:30
next monday Mon, 19 Oct 2026 00:00
last day of february 2028 Tue, 29 Feb 2028 00:00
first monday of january 2027 Mon, 04 Jan 2027 00:00
tomorrow 09:00 Fri, 16 Oct 2026 09:00
bool(false)
Points to Notice
- Pure relative amounts (
+7 days,+1 week 2 days) keep the base time of day. - Expressions that name a date (
2026-12-25,february 2028), a weekday (next monday), ortomorrowreset the time to midnight, unless a time is given (tomorrow 09:00). last day ofandfirst monday ofselect a specific day within the named month.- Unrecognized text returns
false— always check the result before using it.
strtotime() is very convenient for fixed, program-defined expressions. For user input, parse with an explicit format instead (DateTimeImmutable::createFromFormat()), because ambiguous input like 01/02/2026 is interpreted in US order (month/day) by strtotime(), which most Indian users would not expect.
checkdate() — Validating a Date
<?php
var_dump(checkdate(2, 29, 2027));
var_dump(checkdate(2, 29, 2028));
var_dump(checkdate(4, 31, 2026));
Output
bool(false)
bool(true)
bool(false)
checkdate($month, $day, $year) confirms that a combination of numbers forms a real calendar date — useful when a form uses separate fields for day, month, and year.
Calculating With Timestamps
<?php
date_default_timezone_set('Asia/Kolkata');
$today = strtotime('2026-10-15');
$festival = strtotime('2026-12-25');
$days = intdiv($festival - $today, 86400);
echo "Days until 25 December: $days\n";
Output
Days until 25 December: 71
Subtracting timestamps gives seconds; dividing by 86400 (seconds per day) gives days. This works reliably in time zones without daylight saving time, such as India. In zones with daylight saving time, some days are 23 or 25 hours long, so dividing by 86400 can be off by one. DateTimeImmutable::diff() handles this correctly (see DateTime Class).
Measuring Elapsed Time
To measure how long code takes to run, use hrtime(), a high-resolution monotonic clock that is not affected by changes to the system clock:
<?php
$start = hrtime(true);
$sum = 0;
for ($i = 0; $i < 1_000_000; $i++) {
$sum += $i;
}
$elapsedMs = (hrtime(true) - $start) / 1_000_000;
printf("Sum: %d, took %.2f ms\n", $sum, $elapsedMs);
Output (the time varies between runs and machines)
Sum: 499999500000, took 6.42 ms
hrtime(true) returns nanoseconds as an integer. microtime(true) (seconds as a float) is also common, but it follows the system clock, which can jump.
Function Overview
| Function | Purpose |
|---|---|
time() | Current Unix timestamp |
date($format, $timestamp) | Format a timestamp in the default time zone |
gmdate($format, $timestamp) | Format a timestamp in UTC |
mktime(...) | Timestamp from date and time parts |
strtotime($text, $base) | Timestamp from an English date expression |
checkdate($m, $d, $y) | Validate a calendar date |
date_default_timezone_set() / _get() | Set or read the default time zone |
hrtime() / microtime() | High-resolution timing |
Practical Example: Order Delivery Estimate
<?php
date_default_timezone_set('Asia/Kolkata');
function estimateDelivery(int $orderTimestamp, int $businessDays): string
{
$cutoffHour = 15;
$date = $orderTimestamp;
if ((int) date('G', $date) >= $cutoffHour) {
$date = strtotime('tomorrow', $date);
}
$added = 0;
while ($added < $businessDays) {
$date = strtotime('+1 day', $date);
if ((int) date('N', $date) < 7) { // Monday (1) to Saturday (6)
$added++;
}
}
return date('l, j F', $date);
}
$orderTime = mktime(16, 10, 0, 10, 15, 2026); // Thursday 4:10 PM
echo "Ordered: " . date('D, d M Y g:i A', $orderTime) . "\n";
echo "Expected delivery: " . estimateDelivery($orderTime, 3) . "\n";
Output
Ordered: Thu, 15 Oct 2026 4:10 PM
Expected delivery: Tuesday, 20 October
Step-by-Step Explanation
- Orders placed at or after 3 PM (
G= hour without leading zero) are treated as if placed the next day, so processing starts on Friday. - The loop adds one day at a time and counts only Monday to Saturday (
N= ISO day number, where 7 is Sunday). - Starting from Friday: Saturday (1), Sunday (skipped), Monday (2), Tuesday (3) → Tuesday, 20 October.
A real system would also skip public holidays, which can be stored in an array of dates and checked in the loop.
Common Mistakes
Relying on the Server's Default Time Zone
Effect: Dates differ between development and production servers.
Fix: Set date.timezone in php.ini or call date_default_timezone_set().
Mixing Up mktime() Argument Order
Effect: Month and day are swapped, or wrong dates are created.
Fix: Remember hour, minute, second, month, day, year — or use DateTimeImmutable.
Using strtotime() on User Input
Effect: 01/02/2026 becomes 2 January (US order) instead of 1 February.
Fix: Parse user input with an explicit format using createFromFormat().
Not Checking for false
Effect: date('Y-m-d', false) silently formats the epoch: 1970-01-01.
Fix: Check strtotime() results with === false.
Dividing by 86400 in DST Time Zones
Effect: Off-by-one errors around daylight saving changes.
Fix: Use DateTimeImmutable::diff().
Best Practices
- Always set an explicit default time zone.
- Use timestamps for storage and comparison, and format them only for display.
- Use
strtotime()for fixed expressions in code, not for parsing user input. - Prefer
DateTimeImmutablefor calculations, time zones, and parsing. - Validate user-entered dates with
checkdate()orcreateFromFormat().
Official reference: https://www.php.net/manual/en/function.date.php