I tested this on Linux PHP 5.5.5, with Europe/London set as the timezone in php.ini . In fact, I set the clock to four hours to do this too. The minimum code I used to play was:
$d = new DateTime('tomorrow'); echo $d->format('c e');
Output (correct):
2013-10-27T00:00:00+01:00 Europe/London
I am going to find an error in PHP or an error in timezone data. To find out what, we will see what some other program is doing today at midnight in London. Epoch Converter tells me that this should have a Unix timestamp from 1382828400. To double check this timestamp, I ran into PHP:
$d = new DateTime('27-10-2013'); echo $d->format('U');
He also returned 1382828400. So, let's see what it should show ...
TZ=Europe/London date --date="@1382828400" +%c
The output was:
Sun 27 Oct 2013 12:00:00 AM BST
Correctly! Therefore tzdata is fine. So let's look at PHP.
I ran your sample code along with the date command and got the following output:
1 hours, 29 minutes and 53 seconds Sat Oct 26 21:30:07 UTC 2013 Sat Oct 26 22:30:07 BST 2013
This, of course, is correct.
I think that at that moment we eliminated errors in both tzdata and PHP, and you need to look at configuration problems and programmer expectations.
Firstly, as I noted earlier, Europe / London is not UTC, which does not have the concept of daylight saving time and, therefore, it does not change twice a year. Since this does not cause such problems, it is recommended to use servers in UTC no matter what time zone their users are in, and it is best to use programs for internal use of UTC, and then convert to / from local time zones for display and user input.
My best guess is that your server running PHP is actually configured to use UTC rather than Europe / London as its default time zone. This is the only configuration in which I could reproduce your problem. The results of this test were as follows:
date.timezone = UTC 2 hours, 24 minutes and 36 seconds Sat Oct 26 21:35:24 UTC 2013 Sat Oct 26 22:35:24 BST 2013
Forward, you should work in UTC (and with Unix timestamps), where it is practically possible, and convert to local time at the beginning of processing user input and display it as late as possible. A rare case like this when daylight saving time is nearing completion may be an exception, but you should be especially careful that every new DateTime object you create has the correct time zone when you create it, and also keep in mind that they will have such problems.
See also the huge and informative best practices for daylight saving time and time zones.
Finally, to “fix” your code, do the following:
$tz = new DateTimeZone('Europe/London'); $now = new DateTime('now', $tz); $midnight = new DateTime('tomorrow', $tz); $timeToMidnight = $now->diff($midnight); echo $timeToMidnight->format('%h hours, %i minutes and %s seconds');