PHP Date and Time Explained — Formatting, Timestamps and Timezones
Every meaningful app on the internet deals with time. Blog posts need publish dates. E-commerce orders need timestamps. Event platforms count down to zero. Booking systems block unavailable slots. If your PHP app can't handle dates correctly, you're building on sand — and your users will feel it the moment something shows '1970-01-01' where their birthday should be.
PHP's date and time tools solve a very specific problem: computers store time as a single big integer (seconds since 1970), but humans need to read '3rd April 2025 at 2:45 PM'. PHP bridges that gap. It also handles the genuinely tricky stuff — like the fact that noon in London and noon in New York happen at completely different moments — through its timezone system.
By the end of this article you'll know how to display formatted dates, do date arithmetic (add 7 days to an order date, for example), convert between timezones, and use the modern DateTime class that professionals use in production. You'll also know the two classic mistakes that trip up almost every beginner — and exactly how to avoid them.
The Unix Timestamp — PHP's Secret Clock
Before you can format a date, you need to understand what PHP is actually storing under the hood. PHP represents every moment in time as a single integer: the number of seconds that have passed since midnight on January 1st, 1970, UTC. This starting point is called the Unix Epoch, and it's the same standard used by Linux, Python, JavaScript, and virtually every programming language on Earth.
Why 1970? That's when Unix was being developed and the designers needed a fixed starting point. It's arbitrary, but it's universal — which matters when servers in different countries need to agree on when something happened.
The function time() gives you the current Unix timestamp right now. It's just a number — something like 1712000000. On its own that's useless to a human. But it's incredibly useful to a computer because you can do maths on it: add 86400 (the number of seconds in a day) and you get tomorrow. Subtract 604800 and you get a week ago. That simplicity is the whole point.
<?php // time() returns the current Unix timestamp — an integer counting // seconds elapsed since 00:00:00 UTC on 1 January 1970. $currentTimestamp = time(); echo "Current Unix timestamp: " . $currentTimestamp . "\n"; // Because it's just a number, maths works perfectly on it. $secondsInOneDay = 86400; // 60 seconds × 60 minutes × 24 hours $tomorrowTimestamp = $currentTimestamp + $secondsInOneDay; $yesterdayTimestamp = $currentTimestamp - $secondsInOneDay; echo "Tomorrow's timestamp: " . $tomorrowTimestamp . "\n"; echo "Yesterday's timestamp: " . $yesterdayTimestamp . "\n"; // The difference between two timestamps gives you elapsed seconds. $orderPlacedAt = 1711900000; // a past order's timestamp $orderShippedAt = 1711986400; // when it was shipped $secondsBetween = $orderShippedAt - $orderPlacedAt; $hoursBetween = $secondsBetween / 3600; // 3600 seconds in an hour echo "Hours between order and shipment: " . $hoursBetween . "\n";
Tomorrow's timestamp: 1712132223
Yesterday's timestamp: 1711959423
Hours between order and shipment: 24
Formatting Dates the Way Humans Actually Read Them — date()
The date() function is PHP's translator between that raw Unix integer and a readable string. It takes two arguments: a format string that describes what you want the output to look like, and optionally a timestamp to format (if you omit it, it uses time() — right now).
The format string is a sequence of special single-character codes. Each letter means something specific: Y means the four-digit year, m means the two-digit month, d means the two-digit day. You put them together with whatever separators you like — dashes, slashes, spaces, words — and PHP fills in the real values.
The trick is knowing which letters do what. PHP has dozens of format characters. You don't need to memorise all of them — you just need the common ones and you can always look the rest up. The key insight is that uppercase and lowercase matter: Y gives you 2025 but y gives you just 25. D gives you Mon but l (lowercase L) gives you Monday. Small typos in the format string cause frustrating bugs, so slow down when you write it.
<?php // date(format, timestamp) — timestamp is optional, defaults to now. // Let's build up from simple to complex. $rightNow = time(); // grab current timestamp once, use it everywhere // --- Basic date components --- $fourDigitYear = date('Y', $rightNow); // e.g. 2025 $twoDigitYear = date('y', $rightNow); // e.g. 25 $monthNumber = date('m', $rightNow); // e.g. 04 (zero-padded) $monthName = date('F', $rightNow); // e.g. April $shortMonthName = date('M', $rightNow); // e.g. Apr $dayOfMonth = date('d', $rightNow); // e.g. 02 (zero-padded) $dayOfWeekFull = date('l', $rightNow); // e.g. Wednesday (lowercase L) $dayOfWeekShort = date('D', $rightNow); // e.g. Wed // --- Time components --- $hours24 = date('H', $rightNow); // 00–23 $hours12 = date('h', $rightNow); // 01–12 $minutes = date('i', $rightNow); // 00–59 $seconds = date('s', $rightNow); // 00–59 $amOrPm = date('A', $rightNow); // AM or PM // --- Useful combinations --- // ISO 8601 format — common in APIs and databases $isoFormat = date('Y-m-d', $rightNow); echo "ISO date: " . $isoFormat . "\n"; // Human-friendly format $friendlyFormat = date('l, F jS Y', $rightNow); // 'j' = day without leading zero, 'S' = ordinal suffix (st, nd, rd, th) echo "Friendly date: " . $friendlyFormat . "\n"; // Time in 12-hour format $timeFormat = date('h:i A', $rightNow); echo "Current time: " . $timeFormat . "\n"; // Full datetime — perfect for logging $fullDatetime = date('Y-m-d H:i:s', $rightNow); echo "Full datetime: " . $fullDatetime . "\n"; // Formatting a specific past timestamp (a product launch date) $productLaunchTimestamp = mktime(9, 0, 0, 3, 15, 2024); // 9:00 AM, March 15 2024 $launchDisplay = date('D, d M Y \a\t H:i', $productLaunchTimestamp); // Backslashes escape literal letters so PHP doesn't treat them as format codes echo "Product launch: " . $launchDisplay . "\n";
Friendly date: Wednesday, April 2nd 2025
Current time: 10:45 AM
Full datetime: 2025-04-02 10:45:33
Product launch: Fri, 15 Mar 2024 at 09:00
Building Custom Timestamps with mktime() and strtotime()
So far we've formatted the current time. But what about formatting a specific date — like a user's birthday, an event two weeks from now, or an order placed last Tuesday? You need a way to create a timestamp from known values.
PHP gives you two tools for this. mktime() is the precise, programmatic approach: you hand it exact hour, minute, second, month, day and year values and it returns the corresponding Unix timestamp. It's ideal when you already have the date broken into parts — from a form submission or a database row, for example.
strtotime() is the English-language wizard. You pass it a human-readable string like 'next Monday', 'last Friday', '+2 weeks', '15 March 2024', or 'tomorrow' and it figures out the timestamp. It's remarkably clever and it makes relative date calculations readable. Behind the scenes both functions return the same thing — a Unix timestamp — so you can pass their output straight into date().
<?php // ── mktime(hour, minute, second, month, day, year) ────────────────── // Build a timestamp for a specific moment you know in full. $userBirthdayTimestamp = mktime( 0, // hour (midnight) 0, // minute 0, // second 8, // month (August) 22, // day 1995 // year ); echo "Birthday: " . date('F jS, Y', $userBirthdayTimestamp) . "\n"; // Calculate how many years ago that was $yearsAgo = date('Y') - date('Y', $userBirthdayTimestamp); echo "That was " . $yearsAgo . " years ago.\n\n"; // ── strtotime() — plain English date parsing ───────────────────────── // Converts a natural-language date string into a Unix timestamp. $nextMondayTimestamp = strtotime('next Monday'); $twoWeeksFromNow = strtotime('+2 weeks'); $startOfLastMonth = strtotime('first day of last month'); $specificDateTimestamp = strtotime('25 December 2025 08:00:00'); echo "Next Monday: " . date('D, d M Y', $nextMondayTimestamp) . "\n"; echo "Two weeks from now: " . date('D, d M Y', $twoWeeksFromNow) . "\n"; echo "First of last month: " . date('D, d M Y', $startOfLastMonth) . "\n"; echo "Christmas 2025 8am: " . date('D, d M Y H:i', $specificDateTimestamp) . "\n\n"; // ── Real-world example: subscription expiry ────────────────────────── // A user signs up today, gets a 30-day free trial. $signUpTimestamp = time(); $trialEndTimestamp = strtotime('+30 days', $signUpTimestamp); // strtotime() accepts an optional base timestamp as a second argument. $signUpDate = date('Y-m-d', $signUpTimestamp); $trialEndDate = date('Y-m-d', $trialEndTimestamp); echo "Account created: " . $signUpDate . "\n"; echo "Trial expires: " . $trialEndDate . "\n"; // Check if the trial is still active if (time() < $trialEndTimestamp) { echo "Status: Trial is ACTIVE.\n"; } else { echo "Status: Trial has EXPIRED.\n"; }
That was 30 years ago.
Next Monday: Mon, 07 Apr 2025
Two weeks from now: Wed, 16 Apr 2025
First of last month: Sat, 01 Mar 2025
Christmas 2025 8am: Thu, 25 Dec 2025 08:00
Account created: 2025-04-02
Trial expires: 2025-05-02
Status: Trial is ACTIVE.
Timezones and the Modern DateTime Class — The Professional Way
Here's where most tutorials let beginners down: they teach date() and time() and call it done. But timezone handling is where real apps live or die. A booking made at '9:00 AM' by a user in Tokyo means something completely different to a server in New York.
PHP's date_default_timezone_set() function sets a global timezone for your script — always call it at the top of your file, or better yet, set it in php.ini with date.timezone = 'Europe/London'. If you don't, PHP will warn you and fall back to UTC, which causes incorrect local times.
For anything beyond simple formatting, use the DateTime class. It's object-oriented, it handles timezones explicitly, and it lets you do date arithmetic cleanly with DateInterval. Think of DateTime as a smart calendar object you can ask questions: 'what's the date 45 days from now?', 'how many days between these two dates?'. It's cleaner, safer, and it's what you'll see in every professional PHP codebase.
<?php // ── Step 1: Always declare your timezone ───────────────────────────── // Do this at the top of every PHP file that works with dates. date_default_timezone_set('America/New_York'); // ── Step 2: Create DateTime objects ────────────────────────────────── // Current moment in the server's default timezone $now = new DateTime(); echo "Current time (NY): " . $now->format('Y-m-d H:i:s T') . "\n"; // A specific past date $eventDate = new DateTime('2025-07-04 18:00:00'); echo "Event date: " . $eventDate->format('l, F jS Y \a\t g:i A') . "\n"; // DateTime in a DIFFERENT timezone using DateTimeZone $tokyoTimezone = new DateTimeZone('Asia/Tokyo'); $tokyoNow = new DateTime('now', $tokyoTimezone); echo "Same moment in Tokyo: " . $tokyoNow->format('Y-m-d H:i:s T') . "\n\n"; // ── Step 3: Date arithmetic with DateInterval ───────────────────────── // DateInterval uses the ISO 8601 duration format: // P = Period, Y = years, M = months, D = days, T = time separator, // H = hours, M = minutes, S = seconds. $invoiceDueDate = new DateTime('2025-04-02'); // today $invoiceDueDate->modify('+30 days'); // add 30 days in place echo "Invoice due: " . $invoiceDueDate->format('Y-m-d') . "\n"; // Or use add() with a DateInterval object for more control $subscriptionStart = new DateTime('2025-01-15'); $oneYear = new DateInterval('P1Y'); // Period of 1 Year $subscriptionEnd = clone $subscriptionStart; // clone so we don't mutate the original $subscriptionEnd->add($oneYear); echo "Subscription start: " . $subscriptionStart->format('Y-m-d') . "\n"; echo "Subscription end: " . $subscriptionEnd->format('Y-m-d') . "\n\n"; // ── Step 4: Difference between two dates ───────────────────────────── $projectStart = new DateTime('2025-01-01'); $projectDeadline = new DateTime('2025-04-02'); // diff() returns a DateInterval showing the gap between two DateTimes $durationInterval = $projectStart->diff($projectDeadline); echo "Project duration: "; echo $durationInterval->m . " months and " . $durationInterval->d . " days\n"; echo "Total days: " . $durationInterval->days . " days\n"; // ── Step 5: Comparing two DateTime objects ──────────────────────────── $deadline = new DateTime('2025-03-31'); $today = new DateTime('2025-04-02'); if ($today > $deadline) { echo "\nDeadline has passed.\n"; } else { echo "\nDeadline is still ahead.\n"; }
Same moment in Tokyo: 2025-04-02 23:45:33 JST
Event date: Friday, July 4th 2025 at 6:00 PM
Invoice due: 2025-05-02
Subscription start: 2025-01-15
Subscription end: 2026-01-15
Project duration: 3 months and 1 days
Total days: 91 days
Deadline has passed.
| Feature | date() / time() Functions | DateTime Class |
|---|---|---|
| Syntax style | Procedural (functions) | Object-oriented (methods) |
| Timezone awareness | Global setting only via date_default_timezone_set() | Per-object via DateTimeZone — explicit and safe |
| Date arithmetic | Manual maths on seconds — error-prone | add(), sub(), modify() — readable and reliable |
| Comparing dates | Compare raw integer timestamps | Use > < == directly on objects |
| DST handling | Can break — e.g. '+1 day' in seconds crosses a DST boundary | Handles DST transitions automatically |
| Best for | Quick formatting in simple scripts | Production apps, APIs, scheduling systems |
| Parse English strings | strtotime() as companion function | new DateTime('next Monday') built-in |
| Immutable option | Not available | DateTimeImmutable — returns new object, never mutates |
🎯 Key Takeaways
- PHP stores all moments in time as a Unix timestamp — an integer counting seconds since 1 Jan 1970. Everything else is formatting that number for humans.
- date('Y-m-d H:i:s') is your go-to format for databases and logs. date('l, F jS Y') is for human-facing output. Backslash-escape any literal letters in the format string.
- strtotime() understands plain English like '+30 days' and 'next Monday' — but always pass a base timestamp as the second argument when calculating from a known date, not just 'now'.
- Use the DateTime class in real projects — it handles timezones per object, makes date arithmetic readable, lets you compare dates with > and <, and never silently breaks across Daylight Saving Time boundaries.
⚠ Common Mistakes to Avoid
- ✕Mistake 1: Forgetting to set a timezone — PHP shows a warning ('It is not safe to rely on the system's timezone settings') and silently falls back to UTC. Users see times hours off from their local time. Fix: add
date_default_timezone_set('America/New_York')at the top of every script that uses dates, or setdate.timezone = 'Europe/London'in your php.ini file once for the whole server. - ✕Mistake 2: Mutating a DateTime object you meant to keep — when you call
$date->add(new DateInterval('P1M'))it changes$dateitself. If you needed the original date for comparison later, it's gone. Fix: either useclone $datebefore adding, or better yet switch toDateTimeImmutable— it returns a new object on every operation and never touches the original. - ✕Mistake 3: Using strtotime() without checking for failure — if strtotime() can't parse the string you gave it (e.g. a garbled date from user input like '32-15-2025'), it returns
false, not a timestamp. Passingfalsetodate()gives you the date for timestamp 0 — January 1st 1970 — which is a very confusing bug to track down. Fix: always checkif ($timestamp === false)before using the result.
Interview Questions on This Topic
- QWhat is a Unix timestamp, and why does PHP's time() function return the number of seconds since January 1st 1970 specifically?
- QWhat's the difference between using date() with strtotime() versus using the DateTime class — and when would you choose one over the other in a production application?
- QIf a user in London books a meeting for '3:00 PM' on your platform and the server is in New York, how would you store and display that time correctly across both timezones using PHP?
Frequently Asked Questions
How do I get the current date and time in PHP?
Use echo date('Y-m-d H:i:s'); to get the current datetime as a formatted string like '2025-04-02 10:45:33'. If you need an object you can manipulate, use $now = new DateTime(); and then $now->format('Y-m-d H:i:s'). Always set your timezone first with date_default_timezone_set('Your/Timezone') or the output may show UTC instead of local time.
What is the difference between date() and DateTime in PHP?
date() is a plain function — quick, procedural, great for simple formatting. DateTime is a class that gives you an object with methods for arithmetic (add, subtract, diff), timezone control per instance, and direct comparison using > and <. For anything beyond basic display, DateTime is safer and more readable. In team projects and production code, DateTime (or DateTimeImmutable) is the standard choice.
Why does PHP show a wrong date or a date in 1970?
A date showing as January 1st 1970 almost always means a timestamp of 0 is being passed to date(). This happens when strtotime() fails to parse your date string and returns false — PHP then treats false as 0. Check that your date string is in a format strtotime() understands, and always validate: if ($ts = strtotime($input)) { ... } before using the result.
Written and reviewed by senior developers with real-world experience across enterprise, startup and open-source projects. Every article on TheCodeForge is written to be clear, accurate and genuinely useful — not just SEO filler.