kevin3954
kevin3954

Reputation: 95

PHP DateTime with Daylight Savings time

So I am building an website app that calculates an amount due for a bill any user has. I managed to get all the calculations working great right up until the bills come due next month. Something went wrong with the calculations of how many paychecks a user gets before a bill is due. After a few var_dumps I realized that when adding the days to my loop I am coming up with an extra 3600 seconds (1 hour that is gained during daylight savings time on March 12th). So here is a peak at the code that calculates everything. Just for the people looking at this down the road months after this is posted. Todays current date is 2017-02-27

  //declare paychecks as counter
  $paychecks = 0;

  //the number of days a user has between paychecks
  $frequency = 14; 

  //users next payday
  $next_payday = strtotime(2017-03-10);

  //the date the next bill is due
  $due_date = strtotime(2017-03-24);

In theory, there should be 2 paychecks before the due_date. (The 2nd paycheck happens the day the bill is due)

  while ($next_payday <= $due_date) {

       $payday = new DateTime($next_payday);
       $date_array = $payday->add(new DateInterval('P'. $frequency . 'D'));
       $next_payday += strtotime($date_array->format('Y-m-d'));

       //I commented this out but this does not work either
       //$next_payday += ($frequency * 86400);

       //increase the counter
       $paychecks++;

   }

So in theory (And this works any other time other than when DST is a factor) I am trying to determine how many paychecks a user has before a bill is due. The problem is that this instance returns 1 instead of 2 because $next_payday actually gets an extra 3600 seconds added to it when the second iteration of the loop takes place. This makes $next_payday 3600 seconds higher than $due_dates value. I would assume because of DST.

So should I compare the string values (date('Y-m-d', $due_date) == date('Y-m-d', $next_payday)) instead? That would work when the due date is the same as the next payday, but will not work when the date is greater than or less than. I have noticed when converting those dates back to string format, they are the same. Or is there a better way to do this that I am missing.

Adding 3600 to $next_payday while its going through the while loop works as well, but I do not really want to do it that way. I'm sure it will mess me up in the fall when DST happens again and I lose an hour.

Thanks for any input.

Upvotes: 0

Views: 1089

Answers (1)

mickmackusa
mickmackusa

Reputation: 47894

Incrementing time using 86400 or (60*60*24) is going to warp your results whenever a DST event is encountered. Fortunately strtotime() doesn't suffer from the effects when you add days, weeks, etc.

The DateTime construct is all fine and good, but I haven't yet needed it for any of my simple datetime processes. This case is no exception.

When I tested your code, it never entered the while loop because strtotime() values were not quoted and so were converted into unexpected timestamps that set $next_payday greater than $due_date.

This code will correctly tally the number of payperiod in the date range:

//declare paychecks as counter
$paychecks = 0;

//the number of days a user has between paychecks
$frequency = 14;
// or you could use 2 and set the strtotime unit to "+$frequency weeks"

//users next payday
$next_payday = strtotime("2017-03-10");  // this date value needed quotes

//the date the next bill is due
$due_date = strtotime("2017-03-24");  // this date value needed quotes

//echo date("Y-m-d",$next_payday),"<br>",date("Y-m-d",$due_date),"<br>";

while($next_payday<=$due_date){
    ++$paychecks;  // 2017-03-10 & 2017-03-24
    $next_payday=strtotime(date("Y-m-d",$next_payday)." +$frequency days");
}

//echo "next_payday=",date("Y-m-d",$next_payday),"<br>"; // this will be beyond $due_date
echo $paychecks;  // 2

p.s. Yes, the while loop could have been turned into a less readable one-liner (which I always like to seek out).

while($next_payday<=$due_date && $next_payday=strtotime(date("Y-m-d",$next_payday)." +$frequency days")){++$paychecks;}

Upvotes: 1

Related Questions