Jump to content

Tricky function with dates, and future dates..


jwwceo

Recommended Posts

I am writing an application where the admin can assign an event, and then can also say how often the event is repeated, for example, annually, monthly, weekly, every 3 weeks, every 2 weeks, daily, etc. They can also assign how many days in advance of the event to trigger a reminder for the event.

 

I am then using a calendar display I wrote to display events on the calendar. The calendar is displayed one month at a time, and loops through all the days in that month. For every day in the month, say "01-01-08", it then loops through all the events and looks for matching dates and displays an event which occurs on that particular day. This part is working fine.

 

The part that makes me scratch my head is the repeating events. If an even is "01-01-08" and set to repeat every 2 weeks in perpetuity, then there is this huge list of future dates which fit that criteria. How would I take a date in the future, say "12-15-2015" and see if it falls on one of those days. So I am thinking I need to write a function which will take 3 parameters:

 

1. The original date.

2. The repeating frequency

3. The current date

 

which will compare if the current date applies. Seems pretty hairy. Then I will have to modify it, and make another function that takes into consideration how many days in advance to set the notice. So that funciton would compare the current date to the "notification date", and see if that applies, and if so...set the notification.

 

I hope this makes sense. I'd be happy to clarify if it does not. I also think (maybe) I can solve this as long as the dates are not too far in the future. I could limit it to like 10 years and check the re-occurring dates, and as a long as they are less than the 10 year limit, the loop would continue finding future dates, add them to an array of future repeating dates, and then check the current date against that. For example, if an event is occuring every 2 weeks, I could use the strtotime function a bunch of times.  But I am curious about writing the function which would work for any future date.

 

I would love thoughts on this.

 

james

Link to comment
Share on other sites

Here's my go on your first function. It calculates the difference in days between the original date and the future date, and then calculates the remainder of the difference in days divided by the repeating frequency in days (modulus). If it's 0, the original event occurs on the future date.

 

<?php
/* Checks if the original event on $orig_date with repeating frequency $freq occurs on $future_date
* 
* @param string $orig_date   (date in any format recognized by strtotime())
* @param int $freq           (repeating frequency in days)
* @param string $future_date (date in any format recognized by strtotime())
* @return bool
*/
function freq_date_match($orig_date, $freq, $future_date) {
$orig_stamp = strtotime($orig_date);
$future_stamp = strtotime($future_date);
$diff_secs = $future_stamp - $orig_stamp;
$diff_days = $diff_secs / 60 / 60 / 24;
if (($diff_days % $freq) == 0) {
	return true;
} else {
	return false;
}
}

//test
if (freq_date_match('2009-06-20', 5, '2009-06-30')) {
echo 'Match!';
}
?>

 

Should be much more efficient than comparing the future date to every repeating date, based off the original date.

Link to comment
Share on other sites

ah..here's the problem...

 

not all the frequencies are in days...

 

an event that repeats monthly will sometimes be 30 days later, and sometimes 31..and rarely 28..

 

an annual event will usually be 365 days later..but not always..because of leap years and such...

 

thats why the strtotime seemed useful...in a that it can accept normal language like +1 month without knowing how many days exactly it might be.

 

James

 

Link to comment
Share on other sites

To check if a notification should be added, we can simply add an optional offset parameter to the function. Updated function:

 

<?php
/* Checks if the original event on $orig_date with repeating frequency $freq occurs on $future_date
* 
* @param string $orig_date   (date in any format recognized by strtotime())
* @param int $freq           (repeating frequency in days)
* @param string $future_date (date in any format recognized by strtotime())
* @param int $offset         (negative offset in days)
* @return bool
*/
function freq_date_match($orig_date, $freq, $future_date, $offset = 0) {
$orig_stamp = strtotime($orig_date);
$future_stamp = strtotime($future_date);
$diff_secs = $future_stamp - $orig_stamp;
$diff_days = ($diff_secs / 60 / 60 / 24) + $offset;
if (($diff_days % $freq) == 0) {
	return true;
} else {
	return false;
}
}

//test
//if 2009-06-23 occurs 2 days before one of the original event's repeats (every 5th day, beginning 2009-06-20)
if (freq_date_match('2009-06-20', 5, '2009-06-23', 2)) {
echo 'Match!';
}
?>

 

Just to be clear: Note that you can use this function for both your tasks. Just specify the number of days the notifications should appear in advance as the fourth parameter when checking for notifications.

 

Edit: Just realized that problem you're mentioning. I'll see if I can think up a solution.

Link to comment
Share on other sites

Added a frequency unit ('days', 'weeks', 'months' or 'years'):

 

<?php
/* Checks if the original event on $orig_date with repeating frequency $freq occurs on $future_date
* 
* @param string $orig_date   (date in any format recognized by strtotime())
* @param int $freq           (repeating frequency in $freq_unit)
* @param string $freq_unit   ('days', 'weeks', 'months' or 'years')
* @param string $future_date (date in any format recognized by strtotime())
* @param int $offset         (negative offset in days)
* @return bool
*/
function freq_date_match($orig_date, $freq, $freq_unit, $future_date, $offset = 0) {
if (!in_array($freq_unit, array('days', 'weeks', 'months', 'years'))) {
	die('Error: Invalid third parameter passed to function freq_date_match().');
}
$orig_stamp = strtotime($orig_date);
$future_stamp = strtotime($future_date);
switch ($freq_unit) {
	//if unit is weeks, multiply $freq with 7 and move on to calculation with days
	case 'weeks':
		$freq *= 7;
	case 'days':
		$diff_secs = $future_stamp - $orig_stamp;
		$diff_days = ($diff_secs / 60 / 60 / 24) + $offset;
		if (($diff_days % $freq) == 0) {
			return true;
		} else {
			return false;
		}
		break;
	//if unit is years, multiply $freq with 12 and move on to calculation with months
	case 'years':
		$freq *= 12;
	case 'months':
		while ($orig_stamp <= $future_stamp) {
			if ($orig_stamp == $future_stamp) {
				return true;
			}
			$orig_stamp = strtotime("+ $freq months - $offset days", $orig_stamp);
		}
		return false;
		break;
}
}

//test
//if 2009-08-20 occurs on one of the original event's repeats (every 2nd month, beginning 2009-06-20)
if (freq_date_match('2009-06-20', 2, 'months', '2009-08-20')) {
echo 'Match!';
}
?>

 

I'm using a while loop to check the repeating dates when months or years are given as frequency, returning true if the timestamps match or false if the future date is reached without a match.

 

Now I don't know if using this function for each check is more efficient than creating an array of every repeating date of each event to check against. But I hope and think it is :) Just remember that the current upper limit of a Unix timestamp is 19 Jan 2038 (corresponding to the maximum value for a 32-bit signed integer). Meaning you can't check future dates beyond that point.

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.