Difference between dates is more complicated than it seems. In your case, it seems to be a problem related to daylight saving time.
In the Timezone America/Sao_Paulo
, in 2019, daylight saving time would have beginning on November 3, but was canceled. Only many systems have not been updated and still "believe" that daylight saving time existed in this period (see here how to update PHP in these cases).
"Luckily" the PHP I have installed here hasn’t been updated either, so I was able to do a test similar to yours.
strtotime
Let’s see how the function strtotime
behaves with the dates you indicated:
date_default_timezone_set('America/Sao_Paulo');
echo strtotime('2019-11-01'); // 1572577200
echo strtotime('2019-11-07'); // 1573092000
When the schedule is not provided, it assumes "midnight on the default Timezone". I have set Timezone to America/Sao_Paulo
(which is the equivalent of the Official Time of Brasilia), then she considers midnight in this Timezone. Just remembering that strtotime
returns the timestamp value (see here and here what is this value).
In the first case, the timestamp is 1572577200, which corresponds to November 1, 2019, at midnight, Time of Brasilia. At this time it was not yet in daylight time, so this timestamp is equivalent to 2019-11-01T03:00Z
(1 November 2019, 3 am, in UTC) - since, when it is not in daylight time, the Brasília Time is 3 hours behind the UTC.
In the second case, the timestamp is 1573092000, which corresponds to November 7, 2019, at midnight, Time of Brasilia. But remember that my PHP has not been updated and still think that on 11/03/2019 summer time began. And what happens on that day? At midnight, the clock is advanced by an hour, so every minute between 00:00 and 00:59 is skipped (in practice, that day was only 23 hours long, precisely because an hour was "skipped"). So this timestamp corresponds to 2019-11-07T02:00Z
(7 from November, at 2 am, in UTC - and no more 3 am, as in the first case - because when it is daylight saving time, the Time of Brasilia happens to be 2 hours behind the UTC, precisely by moving the clock in an hour).
Therefore the difference between the two dates is 514800 seconds, which is equivalent to 143 hours, or even 5.958333333 days. And rounding down (since you used floor
), is 5 days.
For the result to be 6 days, it would be necessary to have a difference of 144 hours, but thanks to the transition of summer time, an hour was skipped and that is where the difference of 1 hour less comes from. Therefore the calculation fails, since 60 * 60 * 24
considers that every day has 24 hours.
If I change the default Timezone to one that does not have summer time transition in the interval between dates (for example, America/Los_Angeles
), the difference becomes 144 hours and the calculation returns correctly 6 days. That’s probably why one of the scripts works and the other doesn’t, because the one that doesn’t have the call to date_default_timezone_set
is using the default which is configured in PHP, which should be different from America/Sao_Paulo
(you can check which one is using date_default_timezone_get
).
If you want to take into account only the difference in days, ignoring the time, one way is to consider that the dates are in UTC (since UTC does not suffer from daylight savings). Also, you can use a DateTime
, passing as parameter a DateTimeZone
corresponding to UTC:
$utc = new DateTimeZone('UTC');
$inicio = new DateTime('2019-11-01', $utc);
$fim = new DateTime('2019-11-07', $utc);
$diferenca = $inicio->diff($fim);
echo "A diferença é de {$diferenca->days} dias"; // A diferença é de 6 dias
The method diff
returns a DateInterval
, which will have the total amount of days in days
.
That way you don’t need to use date_default_timezone_set
, because it changes the default Timezone to all the script (and that’s not always what you want). Using a DateTimeZone
specific only where necessary, I have more control over where and when I am using that Timezone, without affecting the other parts of the script.
Obviously when there is no transition of daylight saving time between the initial and final dates, both solutions (strtotime
and DateTime
) work.
There are more exact ways to calculate difference between dates?
The "correct" way to calculate difference between two dates depends a lot on each case. Arithmetic of dates it’s okay confusing and often counter-intuitive, and depending on the criteria you use, the results may be completely different.
Maybe you just want to difference in months, disregarding the day, may want the exact difference in days, hours, minutes, seconds, etc. Each form will require a different calculation (ex: if a person was born on 02/29/2000, on 02/28/2001 he has already completed 1 year? Each API/language can return a different result when calculating the number of years between these dates - some may give zero, others may give 1).
Anyway, there is corner cases and there is no single "correct" way to calculate difference between dates. Each case is a case.
What are the values of
$dt_final
and$dt_inicial
? If one is before and the other after a summer time transition, it can give strange results even. What is the default Timezone that was in the other file? When the string only has the date without the hours,strtotime
, if I’m not mistaken, use midnight in the default Timezone that is currently set.– hkotsubo
Boy, there are some very unhappy people in this Sopt. Guys -1 because that’s the only way they think they can hit someone, justifying their inability to understand the question. Kkkkkk
– rbz
Just to make it clear that I was not the one who denied - inclusive, I want to answer the question, but I need the details I asked in the previous comment... :-)
– hkotsubo
@hkotsubo I know it wasn’t you, we know the kinds of people who do it! rs I did not put specific date because as I said below the example, I believe that the time influences, so one would try to simulate and not arrive at the result depending on how it is. The dates were:
2019-11-01
and2019-11-07
. In a file there was thedate_default_timezone_set()
and the next, I haddate_default_timezone_set('America/Sao_Paulo')
. The test I took yesterday was around 4:50.– rbz
@hkotsubo Just complementing, in test I did, yes, the
strtotime
complete with 00:00:00 the dates without hours. So I even put in question fix the times on the dates, if this would change anything, avoid problems, etc.– rbz
Well, in 2019 daylight saving time was canceled (the transition would be on November 3rd, if I’m not mistaken). So if PHP has not been updated, he still thinks that this transition existed and can give this difference even. I’ll run some tests here and answer as soon as possible
– hkotsubo