Solved

Impossible arithmetic

Posted on 2016-10-16
8
44 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 21

Expert Comment

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

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 21

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
Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 

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 21

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

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

I imagine that there are some, like me, who require a way of getting currency exchange rates for implementation in web project from time to time, so I thought I would share a solution that I have developed for this purpose. It turns out that Yaho…
This article discusses how to create an extensible mechanism for linked drop downs.
Learn how to match and substitute tagged data using PHP regular expressions. Demonstrated on Windows 7, but also applies to other operating systems. Demonstrated technique applies to PHP (all versions) and Firefox, but very similar techniques will w…
The viewer will learn how to create a basic form using some HTML5 and PHP for later processing. Set up your basic HTML file. Open your form tag and set the method and action attributes.: (CODE) Set up your first few inputs one for the name and …

760 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

20 Experts available now in Live!

Get 1:1 Help Now