Page 1 of 1

Not a Number (NAN)

Posted: Thu Jul 07, 2011 12:54 pm
by cl2606
Hello all...I'm relatively new to PHP but not to programming. I'm running into a weird issue. I'm writing a type of accounting web site for my work. It keeps track of payments made by each employee for a staple fund. Payment is due every two weeks and I have a MySQL db that keeps the date a person paid, amount paid, how much their next payment should be and the date that payment is due. All relatively simple.

I have a screen where I ask for what pay period a user wants to view and then the PHP code determines each persons balance up to that pay period. I got it to work, but as I try to run it multiple times, the $num_payperiods ends up going to NAN. It isn't because of the dates I'm giving because if I shut down the browser (Chrome) and stop Apache and MySql sessions and start them back up and retype in the same date it will work. But then as I go back to enter a different date I eventually get NAN. It will go to NAN after entering 2 to 3 dates. Here is the code:

Code: Select all

function calculate_balance($due_date) {
  // calculate the balance due for each person based on the due date given
  $payment_history = get_last_payment_all();
  
  // hold the balance for each person
  $balance = array();
  
  foreach ($payment_history as $history) {
  
    $last_due_date = new DateTime($history['nextDateDue']);
    // determine the span (number of days) between due date given 
    // and last due date on record.
    $span = $last_due_date->diff($due_date);
    
    
    if ($last_due_date == $due_date) {
    // if the two dates are equal the balance is the amount in the database.
       $balance[] = $history['amount_due'];
    } else if ($span->format('%R') == '-') {    
    // if the last due date on record is greater than the due date given
    // the person is at least paid up to this date so balance = 0.            
      $balance[] = 0.0;
    } else { 
                 
        echo "<b>".$history['lastName']."</b><br />";
        echo "last_due_date".$last_due_date->format('m/d/Y')."<br />";
        echo "due_date".$due_date->format('m/d/Y')."<br />";
              
        $num_days = $span->format('%d');
        echo "num_days = ".$num_days."<br />";
        $num_months = $span->format('%m');
        echo "num_months = ".$num_months."<br />";
        
        $days = ($num_months * 30) + $num_days;
        echo "days = ".$days."<br />";          
        
        //$len = strlen($days);
        $number_days = intval($days);
        
        echo "num days = ".$number_days."<br />";
    
        // determine the number of pay periods 
        // (1 pay period = 2 weeks = 14 days)
        $twoWeeks = 14;
        $num_payperiod = $number_days / $twoWeeks;
        
        echo "Num payperiod = ".$num_payperiod."<br />" ;
        
        // temp balance from last date paid to the due date given
        //$rate = floatval($history['biWeeklyRate']);
        $temp_bal = $history['biWeeklyRate'] * $num_payperiod;
        echo "temp_bal = ".$temp_bal."<br />";
        
        // total balance for this person is their previous balance
        // plus the temp_bal
        $balance[] = $history['amount_due'] + $temp_bal;
        
    }  // if ($history['nextDateDue' > $due_date)
    
  } // foreach ($payment...)
  
  return $balance; 
  
}  // function calculate_balance($due_date)
I've put extraneous echos in to help me debug. If I put in 7/1/2011 for a date it will work fine. If I then enter say 6/17/2011 it may or may not work. If it doesn't I shut everything down, start it back up and then enter 6/17/2011 I get the correct values.

Any idea of what is going on?

Thanks
cl2606

Re: Not a Number (NAN)

Posted: Thu Jul 07, 2011 4:20 pm
by social_experiment
Can you paste the code of the format function?

Code: Select all

<?php
//
 $num_days = $span->format('%d');
?>

Re: Not a Number (NAN)

Posted: Thu Jul 07, 2011 5:40 pm
by cl2606
I'm not sure if this is what you are asking for but here is the first time $span and format are used:

Code: Select all

$span = $last_due_date->diff($due_date);
What is odd is that $num_days, $num_months, $days are all correct values every time. When it gets to $num_payperiods is when it goes south.

Thanks

Re: Not a Number (NAN)

Posted: Thu Jul 07, 2011 6:28 pm
by cl2606
Okay I got a fix...but I don't like it since it doesn't make sense of why I have to do it.

After I calculate $num_payperiods

Code: Select all

$num_payperiod = $number_days / $twoWeeks;
I added the following code:

Code: Select all

if (is_nan($num_payperiod)) {
            echo ("num_payperiod = <b>NAN</b><br />");
            $num_payperiod = 0;
            $num_payperiod = $days / $twoWeeks;
        }
It will catch the NAN and recompute $num_payperiod and it works.

Still if someone could shed some light on this I'd appreciate it. I don't like not knowing why I have to it this way.

It has to do with $last_due_date being less than $due_date. If the server is fresh and the browser is fresh it will work the first time...it is repeated calls that it goes hay wire.

Thanks

cl2606

Re: Not a Number (NAN)

Posted: Thu Jul 07, 2011 9:19 pm
by McInfo
Try this.

Code: Select all

function debug ($value = null, $name = '') {
    static $dump = '';
    if (func_num_args() > 0) {
        if ($name != '') {
            $dump .= "\$$name = ";
        }
        $dump .= var_export($value, true) . ";\n";
    } else {
        $temp = $dump;
        $dump = '';
        return $temp;
    }
}

function calculate_balance ($due_date) {
    // Clears the debug cache
    debug();

    // Adds an entry to the debug cache
    debug($due_date, 'due_date');

    // calculate the balance due for each person based on the due date given
    $payment_history = get_last_payment_all();
    debug($payment_history, 'payment_history');

    // hold the balance for each person
    $balance = array();

    $save_debug = false;

    foreach ($payment_history as $h => $history) :
        debug($h, 'h');
        debug($history, 'history');

        $last_due_date = new DateTime($history['nextDateDue']);
        debug($last_due_date, 'last_due_date');

        // determine the span (number of days) between due date given
        // and last due date on record.
        $span = $last_due_date->diff($due_date);
        debug($span, 'span');

        if ($last_due_date == $due_date) :
            debug(0, 'if');
            // if the two dates are equal the balance is the amount in the database.
            $balance[] = $history['amount_due'];

        elseif ($span->format('%R') == '-') :
            debug(1, 'if');
            // if the last due date on record is greater than the due date given
            // the person is at least paid up to this date so balance = 0.
            $balance[] = 0.0;

        else :
            debug(2, 'if');

            $num_days = $span->format('%d');
            debug($num_days, 'num_days');

            $num_months = $span->format('%m');
            debug($num_months, 'num_months');

            $days = ($num_months * 30) + $num_days;
            debug($days, 'days');

            $number_days = intval($days);
            debug($number_days, 'number_days');

            // determine the number of pay periods
            // (1 pay period = 2 weeks = 14 days)
            $twoWeeks = 14;
            $num_payperiod = $number_days / $twoWeeks;
            debug($num_payperiod, 'num_payperiod');
            if (is_nan($num_payperiod)) {
                $save_debug = true;
            }

            // temp balance from last date paid to the due date given
            //$rate = floatval($history['biWeeklyRate']);
            $temp_bal = $history['biWeeklyRate'] * $num_payperiod;
            debug($temp_bal, 'temp_bal');

            // total balance for this person is their previous balance
            // plus the temp_bal
            $balance[] = $history['amount_due'] + $temp_bal;

        endif;

    endforeach;

    debug($balance, 'balance');

    if ($save_debug) {
        // Writes the debug data to a log file
        file_put_contents(sprintf('debug_%s.log', microtime(true)), debug());
    }

    return $balance;
    
} // function calculate_balance
Whenever $num_payperiod is NAN, a log file will be created.

Re: Not a Number (NAN)

Posted: Fri Jul 08, 2011 2:43 pm
by cl2606
Here is a log file:

[text]$due_date = DateTime::__set_state(array(
'date' => '2011-06-17 00:00:00',
'timezone_type' => 3,
'timezone' => 'Europe/Berlin',
));
$payment_history = PDOStatement::__set_state(array(
'queryString' => 'SELECT DISTINCT
person.firstName, person.lastName, person.shift,
person.biWeeklyRate,
MAX(account.next_due) as nextDateDue, account.amount_due
FROM account INNER JOIN person
ON account.personID = person.personID
GROUP BY
person.lastName, person.firstName
ORDER BY
person.shift, person.lastName, person.firstName',
));
$h = 0;
$history = array (
'firstName' => 'Tom',
0 => 'Tom',
'lastName' => 'N',
1 => 'Niedert',
'shift' => 'A',
2 => 'A',
'biWeeklyRate' => '3.00',
3 => '3.00',
'nextDateDue' => '2011-07-01',
4 => '2011-07-01',
'amount_due' => '7.00',
5 => '7.00',
);
$last_due_date = DateTime::__set_state(array(
'date' => '2011-07-01 00:00:00',
'timezone_type' => 3,
'timezone' => 'Europe/Berlin',
));
$span = DateInterval::__set_state(array(
'y' => 0,
'm' => 0,
'd' => 14,
'h' => 0,
'i' => 0,
's' => 0,
'invert' => 1,
'days' => 6015,
));
$if = 1;
$h = 1;
$history = array (
'firstName' => 'Jason',
0 => 'Jason',
'lastName' => 'W',
1 => 'W',
'shift' => 'A',
2 => 'A',
'biWeeklyRate' => '7.00',
3 => '7.00',
'nextDateDue' => '2011-04-22',
4 => '2011-04-22',
'amount_due' => '5.00',
5 => '5.00',
);
$last_due_date = DateTime::__set_state(array(
'date' => '2011-04-22 00:00:00',
'timezone_type' => 3,
'timezone' => 'Europe/Berlin',
));
$span = DateInterval::__set_state(array(
'y' => 0,
'm' => 1,
'd' => 26,
'h' => 0,
'i' => 0,
's' => 0,
'invert' => 0,
'days' => 6015,
));
$if = 2;
$num_days = '26';
$num_months = '1';
$days = 56;
$number_days = 56;
$num_payperiod = 4;
$temp_bal = 28;
$h = 2;
$history = array (
'firstName' => 'Curt',
0 => 'Curt',
'lastName' => 'L',
1 => 'L',
'shift' => 'B',
2 => 'B',
'biWeeklyRate' => '7.00',
3 => '7.00',
'nextDateDue' => '2011-07-15',
4 => '2011-07-15',
'amount_due' => '7.00',
5 => '7.00',
);
$last_due_date = DateTime::__set_state(array(
'date' => '2011-07-15 00:00:00',
'timezone_type' => 3,
'timezone' => 'Europe/Berlin',
));
$span = DateInterval::__set_state(array(
'y' => 0,
'm' => 0,
'd' => 28,
'h' => 0,
'i' => 0,
's' => 0,
'invert' => 1,
'days' => 6015,
));
$if = 1;
$h = 3;
$history = array (
'firstName' => 'Lee',
0 => 'Lee',
'lastName' => 'C',
1 => 'C',
'shift' => 'C',
2 => 'C',
'biWeeklyRate' => '7.00',
3 => '7.00',
'nextDateDue' => '2011-04-08',
4 => '2011-04-08',
'amount_due' => '13.00',
5 => '13.00',
);
$last_due_date = DateTime::__set_state(array(
'date' => '2011-04-08 00:00:00',
'timezone_type' => 3,
'timezone' => 'Europe/Berlin',
));
$span = DateInterval::__set_state(array(
'y' => 0,
'm' => 2,
'd' => 9,
'h' => 0,
'i' => 0,
's' => 0,
'invert' => 0,
'days' => 6015,
));
$if = 2;
$num_days = '9';
$num_months = '2';
$days = 69;
$number_days = 69;
$num_payperiod = NAN;
$temp_bal = NAN;
$h = 4;
$history = array (
'firstName' => 'Fergus',
0 => 'Fergus',
'lastName' => 'K',
1 => 'K',
'shift' => 'C',
2 => 'C',
'biWeeklyRate' => '7.00',
3 => '7.00',
'nextDateDue' => '2011-08-01',
4 => '2011-08-01',
'amount_due' => '5.00',
5 => '5.00',
);
$last_due_date = DateTime::__set_state(array(
'date' => '2011-08-01 00:00:00',
'timezone_type' => 3,
'timezone' => 'Europe/Berlin',
));
$span = DateInterval::__set_state(array(
'y' => 0,
'm' => 1,
'd' => 15,
'h' => 0,
'i' => 0,
's' => 0,
'invert' => 1,
'days' => 6015,
));
$if = 1;
$balance = array (
0 => NAN,
1 => 33,
2 => 0,
3 => NAN,
4 => 0,
);
[/text]

What is odd is the very first record $h=0, it doesn't go into the code where $num_payperiod is calculated but $balance[0] = NAN.

Here is another log:

[text]$due_date = DateTime::__set_state(array(
'date' => '2011-07-01 00:00:00',
'timezone_type' => 3,
'timezone' => 'Europe/Berlin',
));
$payment_history = PDOStatement::__set_state(array(
'queryString' => 'SELECT DISTINCT
person.firstName, person.lastName, person.shift,
person.biWeeklyRate,
MAX(account.next_due) as nextDateDue, account.amount_due
FROM account INNER JOIN person
ON account.personID = person.personID
GROUP BY
person.lastName, person.firstName
ORDER BY
person.shift, person.lastName, person.firstName',
));
$h = 0;
$history = array (
'firstName' => 'Tom',
0 => 'Tom',
'lastName' => 'N',
1 => 'N',
'shift' => 'A',
2 => 'A',
'biWeeklyRate' => '3.00',
3 => '3.00',
'nextDateDue' => '2011-07-01',
4 => '2011-07-01',
'amount_due' => '7.00',
5 => '7.00',
);
$last_due_date = DateTime::__set_state(array(
'date' => '2011-07-01 00:00:00',
'timezone_type' => 3,
'timezone' => 'Europe/Berlin',
));
$span = DateInterval::__set_state(array(
'y' => 0,
'm' => 0,
'd' => 0,
'h' => 0,
'i' => 0,
's' => 0,
'invert' => 0,
'days' => 6015,
));
$if = 0;
$h = 1;
$history = array (
'firstName' => 'Jason',
0 => 'Jason',
'lastName' => 'W',
1 => 'W',
'shift' => 'A',
2 => 'A',
'biWeeklyRate' => '7.00',
3 => '7.00',
'nextDateDue' => '2011-04-22',
4 => '2011-04-22',
'amount_due' => '5.00',
5 => '5.00',
);
$last_due_date = DateTime::__set_state(array(
'date' => '2011-04-22 00:00:00',
'timezone_type' => 3,
'timezone' => 'Europe/Berlin',
));
$span = DateInterval::__set_state(array(
'y' => 0,
'm' => 2,
'd' => 9,
'h' => 0,
'i' => 0,
's' => 0,
'invert' => 0,
'days' => 6015,
));
$if = 2;
$num_days = '9';
$num_months = '2';
$days = 69;
$number_days = 69;
$num_payperiod = NAN;
$temp_bal = NAN;
$h = 2;
$history = array (
'firstName' => 'Curt',
0 => 'Curt',
'lastName' => 'L',
1 => 'L',
'shift' => 'B',
2 => 'B',
'biWeeklyRate' => '7.00',
3 => '7.00',
'nextDateDue' => '2011-07-15',
4 => '2011-07-15',
'amount_due' => '7.00',
5 => '7.00',
);
$last_due_date = DateTime::__set_state(array(
'date' => '2011-07-15 00:00:00',
'timezone_type' => 3,
'timezone' => 'Europe/Berlin',
));
$span = DateInterval::__set_state(array(
'y' => 0,
'm' => 0,
'd' => 14,
'h' => 0,
'i' => 0,
's' => 0,
'invert' => 1,
'days' => 6015,
));
$if = 1;
$h = 3;
$history = array (
'firstName' => 'Lee',
0 => 'Lee',
'lastName' => 'C',
1 => 'C',
'shift' => 'C',
2 => 'C',
'biWeeklyRate' => '7.00',
3 => '7.00',
'nextDateDue' => '2011-04-08',
4 => '2011-04-08',
'amount_due' => '13.00',
5 => '13.00',
);
$last_due_date = DateTime::__set_state(array(
'date' => '2011-04-08 00:00:00',
'timezone_type' => 3,
'timezone' => 'Europe/Berlin',
));
$span = DateInterval::__set_state(array(
'y' => 0,
'm' => 2,
'd' => 23,
'h' => 0,
'i' => 0,
's' => 0,
'invert' => 0,
'days' => 6015,
));
$if = 2;
$num_days = '23';
$num_months = '2';
$days = 83;
$number_days = 83;
$num_payperiod = NAN;
$temp_bal = NAN;
$h = 4;
$history = array (
'firstName' => 'Fergus',
0 => 'Fergus',
'lastName' => 'K',
1 => 'K',
'shift' => 'C',
2 => 'C',
'biWeeklyRate' => '7.00',
3 => '7.00',
'nextDateDue' => '2011-08-01',
4 => '2011-08-01',
'amount_due' => '5.00',
5 => '5.00',
);
$last_due_date = DateTime::__set_state(array(
'date' => '2011-08-01 00:00:00',
'timezone_type' => 3,
'timezone' => 'Europe/Berlin',
));
$span = DateInterval::__set_state(array(
'y' => 0,
'm' => 1,
'd' => 1,
'h' => 0,
'i' => 0,
's' => 0,
'invert' => 1,
'days' => 6015,
));
$if = 1;
$balance = array (
0 => '7.00',
1 => NAN,
2 => 0,
3 => NAN,
4 => 0,
);
[/text]

Thanks

cl2606

Re: Not a Number (NAN)

Posted: Fri Jul 08, 2011 4:15 pm
by McInfo
Is the output always the same for a given input? By input I mean both the $due_date and the database.

What version of PHP is this running on?

Re: Not a Number (NAN)

Posted: Fri Jul 08, 2011 5:06 pm
by cl2606
PHP version 5.3.5

Yes, if you mean that given a $due_date and a record in the database it will give the same result. What it is doing is if a user enters $due_date = 7/1/2011 and the database has $last_due_date = 6/1/2011 it then calculates the number of days between the two dates, calculates the number of pay periods from the number of days (a pay period is 14 days). Then it determines the next amount due based on the previous balance plus number of pay periods times the rate.

Re: Not a Number (NAN)

Posted: Fri Jul 08, 2011 7:22 pm
by McInfo
Here are some clues. Given the due date '2011-06-17', your debug log says

Code: Select all

$h = 1;
$history = array (
  'firstName' => 'Jason',
  0 => 'Jason',
  'lastName' => 'W',
  1 => 'W',
  'shift' => 'A',
  2 => 'A',
  'biWeeklyRate' => '7.00',
  3 => '7.00',
  'nextDateDue' => '2011-04-22',
  4 => '2011-04-22',
  'amount_due' => '5.00',
  5 => '5.00',
);
$last_due_date = DateTime::__set_state(array(
   'date' => '2011-04-22 00:00:00',
   'timezone_type' => 3,
   'timezone' => 'Europe/Berlin',
));
$span = DateInterval::__set_state(array(
   'y' => 0,
   'm' => 1,
   'd' => 26,
   'h' => 0,
   'i' => 0,
   's' => 0,
   'invert' => 0,
   'days' => 6015,
));
$if = 2;
$num_days = '26';
$num_months = '1';
$days = 56;
$number_days = 56;
$num_payperiod = 4;
$temp_bal = 28;
while my debug log says

Code: Select all

$h = 1;
$history = array (
  'firstName' => 'Jason',
  0 => 'Jason',
  'lastName' => 'W',
  1 => 'W',
  'shift' => 'A',
  2 => 'A',
  'biWeeklyRate' => '7.00',
  3 => '7.00',
  'nextDateDue' => '2011-04-22',
  4 => '2011-04-22',
  'amount_due' => '5.00',
  5 => '5.00',
);
$last_due_date = DateTime::__set_state(array(
   'date' => '2011-04-22 00:00:00',
   'timezone_type' => 3,
   'timezone' => 'Europe/Berlin',
));
$span = DateInterval::__set_state(array(
   'y' => 0,
   'm' => 1,
   'd' => 25,
   'h' => 0,
   'i' => 0,
   's' => 0,
   'invert' => 0,
   'days' => 56,
));
$if = 2;
$num_days = '25';
$num_months = '1';
$days = 55;
$number_days = 55;
$num_payperiod = 3.9285714285714;
$temp_bal = 27.5;
  • DateInterval::$days is unusually large and always 6015.
  • $num_days is off by one.
  • There is rounding where it is not expected on $num_payperiod and $temp_bal.
(I'm currently loading $payment_history with an array of arrays instead of the PDOStatement you are using. I set up a database and connected with PDO, but still get the same results. I should mention I'm on PHP v5.3.1 and v5.3.2.)

Re: Not a Number (NAN)

Posted: Sat Jul 09, 2011 12:46 pm
by McInfo

Re: Not a Number (NAN)

Posted: Tue Jul 12, 2011 8:10 pm
by cl2606
Thanks for all the info. I appreciate it