Solved

Impossible arithmetic

Posted on 2016-10-16
8
49 Views
Last Modified: 2016-10-17
See this code:
<?php
// email_reminders.php
// overnight process to remind subscribers
include "db_connect.php";
$qry = "SELECT * from subscribers where subscribe_type = 'YR' or subscribe_type = '90'";
$res = mysqli_query($link, $qry);
$ns = mysqli_num_rows($res);
$thirty = 30 * 86400;
$fifteen = 15 * 86400;
$ten = 864000;
$five = 5 * 86400;
$two = 2 * 86400;
$one = 86400;
echo "mo = " . date("n") . ", day = " . date('j') . ", year = " . date("Y") . "<br>";
$today_secs = mktime (0, 0, 0, date("n"), date("j"), date("Y"));
if ($ns != 0) {
	for ($i = 0; $i < $ns; $i++) {
		$s = mysqli_fetch_array($res,MYSQLI_ASSOC);
		$exdt = $s['exp_date'];
		echo "exp date = " . $exdt . "<br>";
		echo "mo exp date = " . substr($exdt,5,2) . "<br>";
		echo "day exp date = " . substr($exdt,8,2) . "<br>";
		echo "yr exp date = " . substr($exdt,0,4) . "<br>";
		$expsec = mktime (0, 0, 0, substr($exdt,5,2), substr($exdt,8,2), substr($exdt,0,4));
		$remsec = $expsec - $today_secs;
		echo "remsecs for " , $s['uid'] . " = " . $remsec . "<br>";
		echo "thirty = " . $thirty . "<br>";
		$msg = "";
		switch($remsec) {
			case ($thirty):
				$msg = "30 days";
				break;
			case ($fifteen):
				$msg = "15 days";
				break;
			case ($ten):
				$msg = "100 days";
				break;
			case ($five):
				$msg = "30 days";
				break;
			case ($two):
				$msg = "2 days";
				break;
			case ($one):
				$msg = "1 day";
				break;
			default:
				break;
		}
		if ($msg != "") {
			// send email
			$qryu = "SELECT * from users where uid = " . $s['uid'];
			$resu = mysqli_query($link, $qryu);
			$u = mysqli_fetch_array($resu,MYSQLI_ASSOC);
			$link = "<a href='http://backflowtestreport.com/subscribe.php?uid=" . $s['uid'] . "'>Subscribe</a>";
			$headers  = 'MIME-Version: 1.0' . "\r\n";
			$headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n";
			$to = $u['email'];
			$subj = "Backflow Test Report Subscription Expiration";
			$body = "Your subscription to the Backflow Test Report Web app will expire in " . $msg . "<br><br>";
			$body = $body . "To renew, click this link " . $link . "<br><br>";
			$body = $body . "Thank you for your interest.";
			echo "to: " . $to . "<br>";
			echo "body = " . $body . "<br>";
	//echo "subject = " . $subj . "<br>";
			//$mr = mail($to, $subj, $body, $headers);
		}
	}
}
?>

Open in new window


This code, run today 10/16/2016, produces this:

mo = 10, day = 16, year = 2016
exp date = 2016-11-14
mo exp date = 11
day exp date = 14
yr exp date = 2016
remsecs for 1 = 2509200
thirty = 2592000
exp date = 2016-11-15
mo exp date = 11
day exp date = 15
yr exp date = 2016
remsecs for 2 = 2595600
thirty = 2592000

This is impossible.

The remsecs for 1 is 2509200, which in days is 29.04166666666667, which is wrong.

The actual difference is EXACTLY 30 days.

What am I doing wrong?
0
Comment
Question by:Richard Korts
  • 4
  • 3
8 Comments
 
LVL 22

Expert Comment

by:Kim Walker
ID: 41846059
What is the default time zone for the PHP server?
0
 
LVL 22

Expert Comment

by:Kim Walker
ID: 41846066
Never mind that. All calculations should be relative to the same time zone regardless of which time zone that is. I'm still looking.
0
 
LVL 108

Expert Comment

by:Ray Paseur
ID: 41846085
As a general rule, it's not a good idea to write your own date/time calculations.  The "home-grown" computations tend to fail over leap year (a rare, super-annual occurrence), and the boundaries of daylight-savings time (a common, twice-a-year occurrence).  I haven't got time to plow this field again right now, but my guess is that home-grown computations made at this time of year might incorrectly assume that all days have 86,400 seconds.  When the computations span the boundaries of daylight savings time, that assumption is untrue, leading to results that look a lot like what you're finding here.

Try this search.  It looks like we are less than a month away from the boundary.  Might be worth checking.
https://www.google.com/?q=Daylight+savings+time

This looks suspicious.  The difference here is 3,600, or exactly the number of seconds in an hour.  Like the hour adjustment for daylight savings time.
remsecs for 2 = 2595600
thirty = 2592000

It might also be a typo?  This looks incongruous:
remsecs for 1 = 2509200 /*** maybe this should be 2592000 ? ***/
thirty = 2592000

The principles for handling date/time values in PHP are described in these two articles.

Procedural
https://www.experts-exchange.com/articles/201/Handling-Date-and-Time-in-PHP-and-MySQL-Procedural-Version.html

Object-oriented
https://www.experts-exchange.com/articles/20920/Handling-Time-and-Date-in-PHP-and-MySQL-OOP-Version.html
0
 
LVL 22

Accepted Solution

by:
Kim Walker earned 500 total points
ID: 41846117
Actually, remsecs for 1 should be 29 days: the 16th of October to the 14th of November is 29 days. The reason the calculation is not exactly 29 days is daylight savings time.

Take a look at this which deals in days instead of seconds and calculates relative to GMT based on whatever timezone you choose. I've chosen America/New_York for my base timezone.
<?php

// WHEN WE ARE DEBUGGING OUR CODE, WE WANT TO SEE ALL THE ERRORS!
error_reporting(E_ALL);
ini_set('display_errors','on');

date_default_timezone_set('America/New_York');

$gmt = new DateTimeZone('+0000');
$today_new = new DateTime(date("Y-n-j"),$gmt);
echo '$today_new = '.$today_new->format('r').'<br>';
$s = array('exp_date' => "2016-11-14", "uid" => "1");
$exdt_new = new DateTime(date($s['exp_date']),$gmt);
echo '$exdt_new = '.$exdt_new->format('r').'<br>';
$remsec_new = $exdt_new->diff($today_new);
echo "remdays for " . $s['uid'] . " = " . $remsec_new->d . " days<br>";

?>

Open in new window

Output:
$today_new = Mon, 17 Oct 2016 00:00:00 +0000
$exdt_new = Mon, 14 Nov 2016 00:00:00 +0000
remdays for 1 = 28 days

Open in new window

1
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 

Author Comment

by:Richard Korts
ID: 41846831
I see the problem is that a daylight savings time change occurs during that period.

Is there a way to use DateDiff in procedural (rather than object) notation? Does Date Diff take into consideration changes in DST?

I cannot understand the usage of DateDiff in object notation.

What I am trying to do is to a calculation that reminds subscribers that there subscription will expire in 30 days, 15 days, 10 days etc.

I think what I have will work if there is NOT a daylight savings time change in the middle. I think mine will work over "leap day" because I think php mktime is aware of that.
0
 
LVL 108

Expert Comment

by:Ray Paseur
ID: 41846928
If you want procedural code, this article describes how it's done.
https://www.experts-exchange.com/articles/201/Handling-Date-and-Time-in-PHP-and-MySQL-Procedural-Version.html

Here's an example...
https://iconoun.com/demo/temp_rkorts.php
<?php // demo/temp_rkorts.php
/**
 * https://www.experts-exchange.com/questions/28976753/Impossible-arithmetic.html#a41846831
 *
 * REQUIRED READING!
 * https://www.experts-exchange.com/articles/201/Handling-Date-and-Time-in-PHP-and-MySQL-Procedural-Version.html
 */
error_reporting(E_ALL);
echo '<pre>';

// GET TODAY IN ISO-8601 FORMAT
$now = date('c');

// GET A FUTURE DATE IN ISO-8601 FORMAT
$days = '15 days';
$then = date('c', strtotime("$now + $days"));

// SHOW THE WORK PRODUCTS
echo PHP_EOL . $now;
echo PHP_EOL . $then;

Open in new window

0
 
LVL 108

Expert Comment

by:Ray Paseur
ID: 41847151
For anyone coming across this question in the future, the take-away message is this: Do not write your own date calculations.  Do not use seconds when what you really want is days.  Instead use the PHP built-in functions to your greatest advantage.
1
 
LVL 22

Expert Comment

by:Kim Walker
ID: 41847496
Same code with comments. (I really should have commented the original code. Sorry.)
<?php

// WHEN WE ARE DEBUGGING OUR CODE, WE WANT TO SEE ALL THE ERRORS!
error_reporting(E_ALL);
ini_set('display_errors','on');

// set the default time zone to match the time zone of dates in database
// reason: GMT (+0000) could be a day ahead of the viewer
date_default_timezone_set('America/New_York');

// create date/time zone object for GMT (+0000)
$gmt = new DateTimeZone('+0000');

// create a date/time object for today's date in the default time zone
// but use GMT to avoid daylight savings time
$today_new = new DateTime(date("Y-n-j"),$gmt);
echo '$today_new = '.$today_new->format('r').'<br>';

// mimic data from database query
$s = array('exp_date' => "2016-11-14", "uid" => "1");

// create another date/time object for the expiration date
// from the database using GMT to avoid daylight savings time
$exdt_new = new DateTime(date($s['exp_date']),$gmt);
echo '$exdt_new = '.$exdt_new->format('r').'<br>';

// calculate difference between today and expiration date
$remsec_new = $exdt_new->diff($today_new);
// NOTE: the variable $remsec_new contains an array of values for
// years, months, days, hours, minutes, etc.

// echo the number of days from the difference array using the "d" key
echo "remdays for " . $s['uid'] . " = " . $remsec_new->d . " days<br>";

?>

Open in new window

0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article will explain how to display the first page of your Microsoft Word documents (e.g. .doc, .docx, etc...) as images in a web page programatically. I have scoured the web on a way to do this unsuccessfully. The goal is to produce something …
Things That Drive Us Nuts Have you noticed the use of the reCaptcha feature at EE and other web sites?  It wants you to read and retype something that looks like this.Insanity!  It's not EE's fault - that's just the way reCaptcha works.  But it is …
The viewer will learn how to dynamically set the form action using jQuery.
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…

861 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

24 Experts available now in Live!

Get 1:1 Help Now