PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 2.5.0
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v2.5.0
2.5.0 2.4.0 2.3.0 2.2.5 2.2.0 2.1.2 2.1.1 trunk 1.10.0 1.10.01 1.10.02 1.5.0 1.5.01 1.5.02 1.5.1 1.5.10 1.5.20 1.5.21 1.5.22 1.5.23 1.5.24 1.5.25 1.6.0 1.7.0 1.7.1 All 34 releases
← All changes | app/Services/TimeSlotService.php +23 -12 1.7.0 → 2.5.0 View file →
@@ -106,9 +106,9 @@
106 106 continue;
107 107 }
108 108
109 109 if ($isLastDay && strtotime($slot['end']) > $maxBookingTime) {
110 - continue;
110 + break;
111 111 }
112 112
113 113 $isSlotAvailable = $this->isSlotAvailable($slot, $currentBookedSlots, $bufferTime, $hostId);
114 114
@@ -180,12 +180,12 @@
180 180 $duration = $this->calendarSlot->getDuration($duration);
181 181
182 182 list($scheduleTimezone, $dstTime) = $this->getTimezoneInfo();
183 183
184 - $fromStartTime = $this->maybeDayLightSavingTime($fromTime, $dstTime, $scheduleTimezone);
185 184 $toEndTime = $this->maybeDayLightSavingTime($toTime, $dstTime, $scheduleTimezone);
186 185
187 - $fromTime = gmdate('Y-m-d 00:00:00', strtotime($fromStartTime)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
186 + // Start from the requested time, not the DST-shifted one: the shift can cross into the next UTC day and drop that day's bookings.
187 + $fromTime = gmdate('Y-m-d 00:00:00', $fromTimeStamp); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
188 188 $toTime = gmdate('Y-m-d 23:59:59', strtotime($toEndTime)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
189 189
190 190 $slots = $this->getDates($fromTime, $toTime, $duration, true);
191 191
@@ -202,8 +202,15 @@
202 202 }
203 203
204 204 protected function isSlotExists($availableSlots, $fromTimeStamp, $toTimeStamp)
205 205 {
206 + // Slot generation only checks notice on today and the horizon on the last day.
207 + if ($fromTimeStamp < strtotime($this->calendarSlot->getMinBookableDateTime()) ||
208 + $toTimeStamp > $this->getMaxBookingTimestamp(null, gmdate('Y-m-d H:i:s', $toTimeStamp), 'UTC') // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
209 + ) {
210 + return false;
211 + }
212 +
206 213 $left = 0;
207 214 $right = count($availableSlots) - 1;
208 215
209 216 while ($left <= $right) {
@@ -235,9 +242,9 @@
235 242 $endDate = gmdate('Y-m-t 23:59:59', strtotime($startDate)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
236 243 }
237 244
238 245 $currentDate = strtotime($startDate);
239 - $endDate = strtotime($endDate);
246 + $endDate = strtotime($endDate) + 1; // add 1s in case end is 23:59:59
240 247 $oneDay = 24 * 60 * 60;
241 248
242 249 $dateArray = [];
243 250
@@ -269,16 +276,20 @@
269 276
270 277 $hostIds = $this->calendarSlot->getHostIds($this->hostId);
271 278 $status = ['pending', 'reserved', 'approved', 'scheduled', 'completed'];
272 279
280 + // Single indexed start_time range: widen the lower bound by max booking duration to catch overlaps.
281 + $maxDurationMinutes = (int) apply_filters('fluent_booking/max_booking_duration_minutes', DAY_IN_SECONDS / MINUTE_IN_SECONDS, $this->calendarSlot);
282 +
283 + $rangeLowerBound = gmdate('Y-m-d H:i:s', strtotime($dateRange[0]) - $maxDurationMinutes * MINUTE_IN_SECONDS); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
284 +
273 285 $bookings = Booking::with(['calendar_event'])
274 286 ->whereHas('hosts', function ($query) use ($hostIds) {
275 287 $query->whereIn('user_id', $hostIds);
276 288 })
277 - ->where(function ($query) use ($dateRange) {
278 - $query->whereBetween('start_time', $dateRange)
279 - ->orWhereBetween('end_time', $dateRange);
280 - })
289 + ->where('start_time', '>=', $rangeLowerBound)
290 + ->where('start_time', '<=', $dateRange[1])
291 + ->where('end_time', '>=', $dateRange[0])
281 292 ->orderBy('start_time', 'ASC')
282 293 ->whereIn('status', $status)
283 294 ->get()
284 295 ->groupBy('group_id');
@@ -569,14 +580,14 @@
569 580
570 581 $event = $this->calendarSlot;
571 582 $duration = $event->getDuration($duration);
572 583
573 - $startDate = $this->adjustStartDate($startDate, $timeZone);
584 + $adjustedDate = $this->adjustStartDate($startDate, $timeZone);
574 585
575 586 $isDisplaySpots = $event->is_display_spots;
576 587 $isMultiGuest = $event->isMultiGuestEvent();
577 588 $isMultiBooking = $event->isAdditionalGuestEnabled();
578 - $endDate = $event->getMaxBookableDateTime($startDate, $timeZone);
589 + $endDate = $event->getMaxBookableDateTime($adjustedDate, $timeZone);
579 590 $startDate = $event->getMinBookableDateTime($startDate, $timeZone);
580 591
581 592 $maxBooking = false;
582 593 if ($isMultiGuest && ($isDisplaySpots || $isMultiBooking)) {
@@ -647,9 +658,9 @@
647 658 }
648 659 }
649 660
650 661 $convertedSpots = array_map(function ($spots) {
651 - return array_values($spots);
662 + return array_values(ksort($spots) ? $spots : $spots);
652 663 }, $convertedSpots);
653 664
654 665 return $convertedSpots;
655 666 }
@@ -1029,9 +1040,9 @@
1029 1040 }
1030 1041
1031 1042 protected function getMaxBookingTimestamp($fromDate, $toDate, $timeZone)
1032 1043 {
1033 - $maxBookingTime = $this->calendarSlot->getMaxBookableDateTime($fromDate, $timeZone, 'Y-m-d H:i:s');
1044 + $maxBookingTime = $this->calendarSlot->getMaxBookableDateTime($toDate, $timeZone, 'Y-m-d H:i:s');
1034 1045
1035 1046 return strtotime($maxBookingTime);
1036 1047 }
1037 1048