Jump to content

Adapting my Calendar to work across more than 1 Month.


fileparts
Go to solution Solved by Barand,

Recommended Posts

I am currently working on a Calendar where it will be populated from Arrays that will be populated from a database.

I currently have the Calendar look for a start date that is within the bookedStart Array and then set the variable booked as true until it finds an end date that is within the bookedEnd Array and then set the variable to false.

 

The issue with this method is that it will only show what dates are booked, between a start and end date, if the start and end date are within the same month. How would I adapt my Calendar to allow it to work across months, for example:

 

Here is the code I am working with:

 

My Main Variables:

	//Labels
	$dayLabels 			= array("Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday");
	$dayMiniLabels		= array("Mon","Tue","Wed","Thu","Fri","Sat","Sun");
	$monthLables 		= array("January","February","March","April","May","June","July","August","September","October","November","December");
	
	//max values
	$maxDays				= 7;
	$maxMonths 			= 12;
	
	//stats
	$forceMonth 			= $_GET['m'];
	$forceYear 			= $_GET['y'];
	
	$todayDate			= date("d-m-Y");
	$todayDate			= date("d-m-Y", strtotime($todayDate));
	$explodeToday		= explode("-", $todayDate);
	
	$currentDay			= $explodeToday[0];
	
	if(isset($forceMonth)) {
		$currentMonth	= $forceMonth;
	} else {
		$currentMonth	= $explodeToday[1];
	};
	
	if(isset($forceYear)) {
		$currentYear		= $forceYear;
	} else {
		$currentYear		= $explodeToday[2];
	};
	
	$currentDate			= strtotime("01-$currentMonth-$currentYear");
	$prevMonth 			= sprintf("%02d", $currentMonth - 1);
	$nextMonth 			= sprintf("%02d", $currentMonth + 1);
	$prevYear 			= sprintf("%02d", $currentYear - 1);
	$nextYear 			= sprintf("%02d", $currentYear + 1);
	
	$daysInMonth 		= cal_days_in_month(CAL_GREGORIAN, $currentMonth, $currentYear);
	$firstDayofMonth	= date("D", $currentDate);
	$firstDayofMonth	= array_search($firstDayofMonth, $dayMiniLabels);
	$firstDayofMonth	= $firstDayofMonth;
	
	//database values
	$bookedStart			= array();
	$bookedEnd			= array();
	$bookedUser			= array();
	
	if($getBookings = $con->prepare("SELECT userID,bookingStart,bookingEnd FROM bookings WHERE machineID=?")) {
		$getBookings->bind_param("i", $_GET['id']);
		if($getBookings->execute()) {
			$getBookings->bind_result($bookingUserID,$bookingStart,$bookingEnd);
			while($getBookings->fetch()) {
				array_push($bookedStart, 	$bookingStart);
				array_push($bookedEnd, 	$bookingEnd);
				array_push($bookedUser, 	$bookingUserID);
			};
		};
	};
	$getBookings->close();

	//counters
	$daysIntoMonth		= 0;
	$dayCounter 		= 0;
	$startMonth			= 0;

Here is the display code, "where the issue is":

	<table class="full grid dayLabels">
		<tr>
		<?php
			foreach($dayLabels as $day) {
				echo '<td class="day"><p>' .$day. '</p></td>';
			};
		?>
		</tr>
	</table>
	<table id="calendar" class="full grid calendar">
		<?php
			while($daysIntoMonth < $daysInMonth) {
				//days into month
				$daysIntoMonth++;
				$temp_intoMonth 			= sprintf("%02d", $daysIntoMonth);
				$daysIntoMonth 		= $temp_intoMonth;
				
				//days into week
				$dayCounter++;
				$temp_dayCounter	= sprintf("%02d", $dayCounter);
				$dayCounter 			= $temp_dayCounter;
				
				//current calendar date
				$calDate 					= date('d-m-Y', strtotime($daysIntoMonth. '-' .$currentMonth. '-' .$currentYear));
				$calTime 					= strtotime($calDate);
				$todaysNumber			= date('w', $timeCal);
				
				if($dayCounter == 1) {
					echo '<tr>';
				};
				
				if($firstDayofMonth != 7) {
					while($startMonth < $firstDayofMonth) {
						echo '<td class="padding"></td>';
						$startMonth++;
						$dayCounter++;
						$temp_dayCounter = sprintf("%02d", $dayCounter);
						$dayCounter = $temp_dayCounter;
					};
				};
				
				if($startKey = in_array($calDate, $bookedStart, true)) {
					$booked = true;
					echo '
						<td class="booked">
							<p class="date">' .$daysIntoMonth. '</p>
						</td>
					';
				} else if(in_array($calDate, $bookedEnd, true)) {
					$booked = false;
					echo '
						<td class="booked">
							<p class="date">' .$daysIntoMonth. '</p>
						</td>
					';
				} else if($booked == true) {
					echo '
						<td class="booked">
							<p class="date">' .$daysIntoMonth. '</p>
						</td>
					';
				} else {
					echo '
						<td>
							<p class="date">' .$daysIntoMonth. '</p>
						</td>
					';
				};

				if($dayCounter == $maxDays) {
					echo '</tr>';
					$dayCounter = 0;
				};
			};
		?>
	</table>

tl;dr - if a booking runs over a month, the start date isn't shown on the ending month, but the end date is, because of how I have it check between dates, it won't highlight the days before the end date.

 

Note: This is on an internal server, so it doesn't have to worry about SQL Injection.

 

Thanks in advance,

fp

Link to comment
Share on other sites

you need to use dates with a YYYY-MM-DD format, so that you can do greater-than/less-than comparisons.

 

your dates should be stored in the database using a DATE data type, which will give them that format. this will let you retrieve only the rows that have a start to end date range that matches the year-month you are trying to display.

 

next, just store the retrieved data directly in one php array, with the id, start date, and end date.

 

as you loop over the days in a month to display the calendar, you will form the current date with a YYYY-MM-DD format. for each day, you will then loop over the array of data and retrieve the id(s) where the current date is between the start date and the end date.

 

edit: an alternate method would be to expand the start date to end date into individual dates within the range and for those dates that correspond to the year-month being displayed (dates before and after the current year-month would not need to be stored), store the id as an array element for each day, using the date as the main array key. this would result in an array that looks like -

 

$array['2015-06-01'][] = some_id; // some_id is booked on the 1st and 2nd of this month

$array['2015-06-01'][] = someother_id; // someother_id is booked on the 1st of this month

$array['2015-06-02'][] = some_id;

 

as you loop over the days in the month, just loop over any sub-array of id's, if present, that have a main array key matching that current date.

Edited by mac_gyver
Link to comment
Share on other sites

you need to use dates with a YYYY-MM-DD format, so that you can do greater-than/less-than comparisons.

 

your dates should be stored in the database using a DATE data type, which will give them that format. this will let you retrieve only the rows that have a start to end date range that matches the year-month you are trying to display.

 

next, just store the retrieved data directly in one php array, with the id, start date, and end date.

 

as you loop over the days in a month to display the calendar, you will form the current date with a YYYY-MM-DD format. for each day, you will then loop over the array of data and retrieve the id(s) where the current date is between the start date and the end date.

 

edit: an alternate method would be to expand the start date to end date into individual dates within the range and for those dates that correspond to the year-month being displayed (dates before and after the current year-month would not need to be stored), store the id as an array element for each day, using the date as the main array key. this would result in an array that looks like -

 

$array['2015-06-01'][] = some_id; // some_id is booked on the 1st and 2nd of this month

$array['2015-06-01'][] = someother_id; // someother_id is booked on the 1st of this month

$array['2015-06-02'][] = some_id;

 

as you loop over the days in the month, just loop over any sub-array of id's, if present, that have a main array key matching that current date.

 

Thanks for the response, certainly an interesting idea.

 

How about a way to check to see if a bookedStart date and bookedEnd date have the same position in their respective arrays and then checking if bookedEnd date's month is greater than bookedStart date's month, then - somehow, the dates between these two dates become highlighted? Possibly simplar than your answer, although I wouldn't know where to start with coding it.

Link to comment
Share on other sites

  • Solution

Here is how I would create an array of booked dates for a month

$monthStart = date('Y-m-01');
$monthEnd   = date('Y-m-t');

$bookings = array();
// CREATE DATE PERIOD FOR THE MONTH
$s = new DateTime($monthStart);
$e = new DateTime("$monthEnd + 1 days");
$oneday = new DateInterval('P1D');
$dp = new DatePeriod($s, $oneday, $e);

// INITIALIZE BOOKINGS ARRAY FOR MONTH
foreach ($dp as $d) {
    $bookings[$d->format('Y-m-d')] = '';   
}

// FIND BOOKING WHICH ARE ALL OR PART IN THIS MONTH
$sql = "SELECT userID
         , bookingStart
         , bookingEnd 
        FROM bookings 
        WHERE machineID = ?
          AND bookingStart < ?
          AND bookingEnd > ? ";
$getBookings = $con->prepare($sql);
$getBookings->bind_param('iss', $_GET['id'], $monthEnd, $monthStart);
$getBookings->execute();
$getBookings->bind_result($uid, $bstart, $bend);

while ($getBookings->fetch()) {
    $s = new DateTime(max($bstart, $monthStart));    // FIND THE START AND END
    $e = new DateTime(min($bend, $monthEnd));        // DATES WITHIN THE MONTH
    $e->modify('+1 day');
    $dp = new DatePeriod($s, $oneday, $e);           // DATE PERIOD FOR THE BOOKING
    foreach ($dp as $d) {
        // STORE userid IN THE DAYS BOOKED
        $bookings[$d->format('Y-m-d')] = $uid;
    }
}

which gives an array, indexed by date, showing the user who has booked the machine

 

 

Array
(
    [2015-06-01] => 2
    [2015-06-02] => 2
    [2015-06-03] => 2
    [2015-06-04] => 2
    [2015-06-05] => 2
    [2015-06-06] => 2
    [2015-06-07] => 2
    [2015-06-08] => 2
    [2015-06-09] => 
    [2015-06-10] => 3
    [2015-06-11] => 3
    [2015-06-12] => 3
    [2015-06-13] => 
    [2015-06-14] => 
    [2015-06-15] => 4
    [2015-06-16] => 4
    [2015-06-17] => 4
    [2015-06-18] => 4
    [2015-06-19] => 4
    [2015-06-20] => 4
    [2015-06-21] => 4
    [2015-06-22] => 4
    [2015-06-23] => 4
    [2015-06-24] => 
    [2015-06-25] => 
    [2015-06-26] => 
    [2015-06-27] => 
    [2015-06-28] => 5
    [2015-06-29] => 5
    [2015-06-30] => 5
)

 

 

from this data

+-------------+--------+-----------+--------------+------------+
| bookings_id | userID | machineID | bookingStart | bookingEnd |
+-------------+--------+-----------+--------------+------------+
|           1 |      1 |         1 | 2015-05-20   | 2015-05-31 | previous month
|           2 |      2 |         1 | 2015-05-28   | 2015-06-08 | starts in prev month
|           3 |      3 |         1 | 2015-06-10   | 2015-06-12 |
|           4 |      4 |         1 | 2015-06-15   | 2015-06-23 |
|           5 |      5 |         1 | 2015-06-28   | 2015-07-10 | ends in next month
|           6 |      6 |         1 | 2015-07-11   | 2015-07-20 | next month
+-------------+--------+-----------+--------------+------------+
Link to comment
Share on other sites

 

Here is how I would create an array of booked dates for a month

$monthStart = date('Y-m-01');
$monthEnd   = date('Y-m-t');

$bookings = array();
// CREATE DATE PERIOD FOR THE MONTH
$s = new DateTime($monthStart);
$e = new DateTime("$monthEnd + 1 days");
$oneday = new DateInterval('P1D');
$dp = new DatePeriod($s, $oneday, $e);

// INITIALIZE BOOKINGS ARRAY FOR MONTH
foreach ($dp as $d) {
    $bookings[$d->format('Y-m-d')] = '';   
}

// FIND BOOKING WHICH ARE ALL OR PART IN THIS MONTH
$sql = "SELECT userID
         , bookingStart
         , bookingEnd 
        FROM bookings 
        WHERE machineID = ?
          AND bookingStart < ?
          AND bookingEnd > ? ";
$getBookings = $con->prepare($sql);
$getBookings->bind_param('iss', $_GET['id'], $monthEnd, $monthStart);
$getBookings->execute();
$getBookings->bind_result($uid, $bstart, $bend);

while ($getBookings->fetch()) {
    $s = new DateTime(max($bstart, $monthStart));    // FIND THE START AND END
    $e = new DateTime(min($bend, $monthEnd));        // DATES WITHIN THE MONTH
    $e->modify('+1 day');
    $dp = new DatePeriod($s, $oneday, $e);           // DATE PERIOD FOR THE BOOKING
    foreach ($dp as $d) {
        // STORE userid IN THE DAYS BOOKED
        $bookings[$d->format('Y-m-d')] = $uid;
    }
}

which gives an array, indexed by date, showing the user who has booked the machine

 

 

Array
(
    [2015-06-01] => 2
    [2015-06-02] => 2
    [2015-06-03] => 2
    [2015-06-04] => 2
    [2015-06-05] => 2
    [2015-06-06] => 2
    [2015-06-07] => 2
    [2015-06-08] => 2
    [2015-06-09] => 
    [2015-06-10] => 3
    [2015-06-11] => 3
    [2015-06-12] => 3
    [2015-06-13] => 
    [2015-06-14] => 
    [2015-06-15] => 4
    [2015-06-16] => 4
    [2015-06-17] => 4
    [2015-06-18] => 4
    [2015-06-19] => 4
    [2015-06-20] => 4
    [2015-06-21] => 4
    [2015-06-22] => 4
    [2015-06-23] => 4
    [2015-06-24] => 
    [2015-06-25] => 
    [2015-06-26] => 
    [2015-06-27] => 
    [2015-06-28] => 5
    [2015-06-29] => 5
    [2015-06-30] => 5
)

 

 

from this data

+-------------+--------+-----------+--------------+------------+
| bookings_id | userID | machineID | bookingStart | bookingEnd |
+-------------+--------+-----------+--------------+------------+
|           1 |      1 |         1 | 2015-05-20   | 2015-05-31 | previous month
|           2 |      2 |         1 | 2015-05-28   | 2015-06-08 | starts in prev month
|           3 |      3 |         1 | 2015-06-10   | 2015-06-12 |
|           4 |      4 |         1 | 2015-06-15   | 2015-06-23 |
|           5 |      5 |         1 | 2015-06-28   | 2015-07-10 | ends in next month
|           6 |      6 |         1 | 2015-07-11   | 2015-07-20 | next month
+-------------+--------+-----------+--------------+------------+

 

Thanks for the great response. I still would need to put this in a tabular format, 7 columns etc, to make a calendar which I would be able to read from.

How would you go about outputting the data so that it can be read within a html table?

Link to comment
Share on other sites

Okay thanks @barand for the answer,

 

I've tweaked and added bits to get it to display as a calendar. Here's what I've got:

$dayLabels 			= array("Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday");
$dayMiniLabels		= array("Mon","Tue","Wed","Thu","Fri","Sat","Sun");
$monthLables 		= array("January","February","March","April","May","June","July","August","September","October","November","December");

$forceMonth = $_GET['m'];
$forceYear = $_GET['y'];

if(isset($forceMonth)) {
	if(strlen($forceMonth) == 1) {
		$forceMonth 	= sprintf("%02d", $forceMonth);
	};
	$currentMonth	= $forceMonth;
} else {
	$currentMonth	= date("m");
};

if(isset($forceYear)) {
	if(strlen($forceYear) == 2) {
		$dt 				= DateTime::createFromFormat('y', $forceYear);
		$forceYear 	= $dt->format('Y');
	};
	$currentYear		= $forceYear;
} else {
	$currentYear		= date("Y");
};

$monthStart 			= date($currentYear. '-' .$currentMonth. '-01');
$monthEnd   			= date($currentYear. '-' .$currentMonth. '-t');

$prevMonth 			= sprintf("%02d", $currentMonth - 1);
$nextMonth 			= sprintf("%02d", $currentMonth + 1);
$prevYear 			= sprintf("%02d", $currentYear - 1);
$nextYear 			= sprintf("%02d", $currentYear + 1);

$firstDayofMonth	= date("D", $currentDate);
$firstDayofMonth	= array_search($firstDayofMonth, $dayMiniLabels);
$firstDayofMonth	= $firstDayofMonth;

$bookings 			= array();
$s 						= new DateTime($monthStart);
$e 						= new DateTime("$monthEnd + 1 days");
$oneday 				= new DateInterval('P1D');
$dp 						= new DatePeriod($s, $oneday, $e);

foreach ($dp as $d) {
    $bookings[$d->format('Y-m-d')] = '';   
};

$sql = "SELECT userID
         , bookingStart
         , bookingEnd 
        FROM bookings 
        WHERE machineID = ?
          AND bookingStart < ?
          AND bookingEnd > ?";
$getBookings = $con->prepare($sql);
$getBookings->bind_param('iss', $_GET['id'], $monthEnd, $monthStart);
$getBookings->execute();
$getBookings->bind_result($uid, $bstart, $bend);

while ($getBookings->fetch()) {
    $s 					= new DateTime(max($bstart, $monthStart));
    $e 					= new DateTime(min($bend, $monthEnd));
    $e->modify('+1 day');
    $dp = new DatePeriod($s, $oneday, $e);
    foreach ($dp as $d) {
        $bookings[$d->format('Y-m-d')] = $uid;
    };
};

$dayCount 	= 0;
$startMonth 	= 0;
$calDate 		= 0;

echo '<table>';
foreach($bookings as $date) {
	$dayCount++;
	$calDate++;
	$calDate = sprintf("%02d", $calDate);
	
	if($dayCount == 1) {
		echo '<tr>';
	};
	
	if($firstDayofMonth != 7) {
		while($startMonth < $firstDayofMonth) {
			echo '<td class="padding"></td>';
			$startMonth++;
			$dayCount++;
			$temp_dayCount = sprintf("%02d", $dayCount);
			$dayCount = $temp_dayCount;
		};
	};
	
	echo '<td>' .$calDate. '</td>';
	
	if($dayCount == 7) {
		echo '</tr>';
		$dayCount = 0;
	};
};
echo '</table>';

Any changes that I should make?

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.