Calculate difference between dates (no time) with strtotime - use of date_default_timezone_set() gives different results

Asked

Viewed 336 times

0

Example scenario

I had a problem with a simple day count between 2 dates.

One resulted me in 5 and the other 6, and was using the same dates with the same function, only in different files.

The function:

$dt_inicial = "2019-11-01"
$dt_final   = "2019-11-07"
$dias       = (int) floor((strtotime($dt_final) - strtotime($dt_inicial))/(60*60*24));

And yet, if I’m not going crazy, the current time of execution influences the outcome.

The dates were: 2019-11-01 and 2019-11-07.

Then, looking at what was wrong, I realized that (apart from the fact of having the same function in 2 different places) what was giving the problem, was in one of the files be set date_default_timezone_set('America/Sao_Paulo') and the other not, thus assuming the pattern.


Doubts

  • I should add the "hours" (00:00:00 and 23:59:59) on the dates to avoid the problem?
  • When I use the strtotime with a date, no hours, it takes the current time?
  • What is the correct use of date_default_timezone_set() in such cases?
  • Using this formula for day difference calculation is correct?
  • There are more exact ways to calculate difference between dates?
  • 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.

  • 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

  • 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 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 and 2019-11-07. In a file there was the date_default_timezone_set() and the next, I had date_default_timezone_set('America/Sao_Paulo'). The test I took yesterday was around 4:50.

  • @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.

  • 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

Show 1 more comment

1 answer

2


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.

  • You reminded me of a detail... the return is 5 and 6, because I add +1 to consider another full day. Like the strtotime fills with 00:00:00, when the difference is taken "lost" the date 2 days. I will correct the question.

  • So, why did you give me the difference around 5:00? I can’t understand... if it was the turn of the day, then it would make sense... or I’m missing something!?

  • @Rbz I don’t know, he used the current time or some value different from what you went through? Is there any other detail in the calculation that wasn’t shown? If you run with the code I passed, with DateTime, is the same problem? See also the value of the default Timezone in the script that does not change to America/Sao_paulo (run date_default_timezone_get() in it and see what it is)

  • I will wait 16 hours to take the test! rs

  • @Strange, the time that rotates should not influence, because you use only the date and so he always arrow the time to midnight. Maybe someone has set up the PHP config, run echo (new DateTime('2019-11-07', new DateTimeZone('America/Sao_Paulo')))->format(DateTime::ATOM); and see the result. If at the end you are -02:00, PHP still thinks it has daylight time, if it is -03:00 the config has been corrected.

  • So... the dates are fixed... it will give 00:00:00 to everything... should not be fixed however?

  • @Rbz If the dates are fixed, then that’s what I’ve answered (depending on the Timezone can give 5 or 6). And it shouldn’t depend on the time you run... My guess is that there might be some other place changing the Timezone, I don’t know - I’d put one echo date_default_timezone_get() just to see what each script is using, to make sure...

Show 3 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.