THE DEFINITIVE GUIDE TO PHP TIME HANDLING
As a PHP programmer, sooner or later you will have to work with time and time related entities like dates, timestamps and time zones.
When dealing with time, things can get a bit complicated: what is exactly a Unix Timestamp and why should you use it? How can you properly handle different time zones? Should you work in UTC or in local time?
Without knowing the fundamental concepts related to time measurement it’s very easy to make mistakes. This three-parts guide starts from the beginning, clearly explaining how UTC was born and how it actually works, and then moving into the practical PHP programming techniques and functions and explaining how to use them properly.
After reading this guide you will master PHP time handling once and for all.
In this first part of the guide we will cover the basic theory behind time measurement, we will see what is UTC and we will learn how to use it in PHP.
HOW MANY TIMES ARE THERE, REALLY?
When we think about how time passes, we usually take for granted that there is no ambiguity about which time we are referring to. Time is time, right?
Sure, we know time-space is relative, but that doesn’t really matter in real life unless we work in a physics lab or in a space agency.
But what about web programming?
The good news is that it’s not necessary to take relativity into account 🙂
There are, however, some facts that should be considered when working with time and time related functions.
Think of one of the most simple operations: printing the current time. It sounds easy, and usually it is (you just need to use date()). But what happens if the server running your script is in a different time zone than you? To be sure your code works properly, you should at least know how PHP handles time zone changes.
For more complex time related operations it is necessary to take into account many more factors, otherwise you application may achieve wrong results and not work properly.
The first thing you should know is that there are a few different ways we can measure how time passes on Earth.
To understand why, we need to jump back to the year 1967, when the International System of Units defined the second, the base unit of time, taking as reference the caesium-133 vibration frequency. That definition gave the second a precise and universal length, the one we still use today.
In 1971, the International Atomic Time, also known as TAI, was created as a standard timekeeping system based on the second as defined in 1967. The TAI is basically a clock that “ticks” precisely every second, marking the passage of hours, days and years.
Now it’s where things start to get complicated. A day has 86400 seconds (24 hours of 60 minutes of 60 seconds), but while in 1967 the day’s length was exactly 86400 seconds, now it is a few milliseconds longer than that. The reason is that the Earth’s rotation is slowing down, mainly because of the Moon’s tidal effects, and the slower the Earth rotates the longer the days last.
The International Atomic Time keeps ticking precisely every second, but the day’s length is gradually increasing. This means that the TAI clock is slowly going ahead of the Earth solar time. In fact, the TAI has already gone many seconds ahead since it was created.
Therefore, the TAI cannot be used as a standard civil time because it’s not in sync with the Earth’s astronomical time (or “solar” time) any more, and it will always be more out of sync.
If the International Atomic Time cannot be used for civil timekeeping, what can be used instead?
There are other time systems that are based on the Earth’s astronomical position. These systems give the astronomical datetime (or “solar” datetime), which is perfectly coordinated with the Earth’s motion changes.
The most important of these systems is the Universal Time, or UT1. As the Earth’s rotation slows down, the Universal Time stays perfectly in sync with it.
Now, since days last longer than 86400 seconds, the UT1 has no choice but to “stretch” the seconds to fill all the day’s length. This let it stay in sync with the Earth’s rotation, but the inevitable side effect is that the seconds counted by the Universal Time are not precisely all the same length.
Unfortunately such a clock cannot be used for civil timekeeping either, because we absolutely need a clock where every second has a fixed length as defined by the International System of Units.
A clock suited for civil timekeeping needs to use the standard, fixed-length seconds as base unit, but also needs to stay in sync with the Earth’s astronomical position. This is the reason why UTC, or Coordinated Universal Time, was created.
UTC: Coordinated Universal Time
UTC is the world standard civil clock.
Like the International Atomic Time, the UTC too is based on the International Standard of Units’ definition of second. This means that every UTC’s second has a fixed and precise length. So far so good.
Of course, just like the TAI (International Atomic Time), the UTC too should slowly go out of sync with the Earth’s astronomical time… but unlike the TAI, the UTC has a trick for fixing this problem: it uses leap seconds.
In a similar way leap years are used to restore the sync between the legal years and the astronomical years, leap seconds in the UTC system are used to restore the synchronization between the UTC datetime and the Universal Time (UT1) datetime.
(Remember that the UT1 represents the Earth astronomical, or solar, datetime).
The UTC slowly gets out of sync with the UT1 (exactly like the TAI does), because the UT1 gradually slows down to match the Earth’s rotation slowdown. When the UTC drifts more than half a second from the UT1, then the UTC inserts an additional second to reduce the drift and to stay in sync.
These additional seconds are called leap seconds, and are always added at 23:59:59 of December 31st or of June 30th. It may seem odd, but in these cases the official UTC times goes from 23:59:59 to 23:59:60, and only the next second it goes to the 00:00:00 of the next day.
The UTC clock, therefore, can occasionally have days 86401 seconds long. This has happened the last time on December 31st 2016 at 23:59:60.
In the following video you can see how that happened from the official NIST website:
It’s interesting to compare the tree clocks we have seen: TAI, UT1 and UTC:
International Atomic TimeTAI
- Fixed-length seconds and days
- Not in sync with solar time
- All days have 86400 seconds
Coordinated Universal TimeUTC
- Fixed-length seconds
- Doesn’t drift more than half a second from solar time
- Occasionally, days can have 86401 seconds
- Seconds vary in length to match Earth’s rotation
- Perfectly in sync with solar time
- All days have 86400 seconds (but seconds haven’t the same length)
It is also interesting to see a practical example. At the end of the year 2016, the International Atomic Time was already 36 seconds ahead of the UTC (and of the Earth’s solar time). After the leap second was added at December 31st 23:50:60, the TAI drift increased to 37 seconds:
|2017-01-01 00:00:34||2016-12-31 23:59:58|
|2017-01-01 00:00:35||2016-12-31 23:59:59|
|2017-01-01 00:00:36||2016-12-31 23:59:60|
|2017-01-01 00:00:37||2017-01-01 00:00:00|
|2017-01-01 00:00:38||2017-01-01 00:00:01|
This concludes the theory. Now it’s time to see how UTC can be used in PHP.
Programming languages need to access the UTC clock in order to work with time and dates. Many operating systems and libraries include a representation of UTC that can be used directly by programmers.
One common UTC representation is called Unix Time, or Unix Timestamp. It is an integer number that counts the seconds elapsed since the UTC datetime 1970-01-01 00:00:00 (a date known as “The Epoch”).
PHP uses Unix Time too. The current Unix Time (or Unix Timestamp) can be retrieved simply by calling the time() function.
As I’m writing, the UTC datetime is 2018-04-02 16:30:26. Executing time() now gives me the Unix Time 1522686626. That means that there have been 1522686626 / 86400 = a little more than 17623 days from the UTC datetime 1970-01-01 00:00:00.
You can try yourself with the following code:
<?php /* Set the time zone to UTC in order to print the UTC datetime */ date_default_timezone_set('UTC'); /* We print the current UTC datetime */ echo 'Current UTC datetime is ' . date('Y-m-d H:i:s') . '<br>'; /* We print the current Unix Time */ echo 'Current Unix Time is ' . strval(time()) . '<br>'; /* Note that the Unix Time does not depend on which time zone we are in */ date_default_timezone_set('Arctic/Longyearbyen'); echo 'Current Arctic datetime is ' . date('Y-m-d H:i:s') . '<br>'; echo 'Current Unix Time is ' . strval(time()) . '<br>';
A very important fact about Unix Time is that it doesn’t consider UTC leap seconds. In other words, the Unix Time number does not increment when a leap second is added in the UTC clock.
Unix Time is usually synchronized with UTC: in fact, just like UTC, Unix Time too increments precisely every second. However, when a new leap second is inserted, the Unix Time stays the same “ignoring” the leap second.
It’s easy to understand what happens if we look again at the previous table and we add the Unix Time value next to the TAI and UTC ones:
|2017-01-01 00:00:34||2016-12-31 23:59:58||1483228798|
|2017-01-01 00:00:35||2016-12-31 23:59:59||1483228799|
|2017-01-01 00:00:36||2016-12-31 23:59:60||1483228800|
|2017-01-01 00:00:37||2017-01-01 00:00:00||1483228800|
|2017-01-01 00:00:38||2017-01-01 00:00:01||1483228801|
As you can see, all these three time measurement systems behave differently when a leap second occurs: TAI moves on regularly, UTC adds an extra second to the current day and Unix Time simply ignores it.
This Unix Time behaviour leads to a problem: how can you discern between 23:59:60 of December 31st 2016 and 00:00:00 of January 1st 2017?
Short answer: you can’t. PHP, like other languages, will always translate that Unix Time to the 00:00:00 datetime. This means that it’s not actually possible to represent a leap second time (i.e., a 23:59:60 time) using Unix Time.
Calculations of time differences can be affected too. A common way to find a time interval is by calculating the difference between two Unix Times (the one relative to the end of the time interval minus the one relative to the start): the result is the time interval in seconds.
However, if a leap second is found inside that interval then the calculation is wrong by one second.
Look at the previous table. The difference between 2017-01-01 00:00:01 Unix Time and the 2016-12-31 23:59:58 Unix Time is 3, however the time interval is actually 4 seconds.
In time critical web applications this can cause serious problems. Moreover, leap seconds are added on average every one and a half years, so it’s a situation that can occur quite often.
Now you should be wondering: if leap years are handled without problems by UTC and Unix Time as well, why aren’t leap seconds handled the same way?
The reason is that leap years follow a mathematical rule and can be found using an algorithm. Leap seconds, on the other side, are added when needed and cannot be scheduled with too much advance. Therefore is not possible to implement an algorithm that calculates when they will occur.
In the second part of this guide we will see how it is possible to retrieve informations about leap seconds and solve some of these problems.
Continue to part 2.
If you have any questions feel free to ask in the comments below or on my Facebook page.
If this guide has been helpful to you, please spend a second of your time and share it using the buttons below… thanks!