PluginProbe
VikBooking Hotel Booking Engine & PMS / trunk
VikBooking Hotel Booking Engine & PMS vtrunk
1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 All 36 releases
vikbooking / admin / helpers / src / date / comparator.php

comparator.php in VikBooking Hotel Booking Engine & PMS trunk, at admin/helpers/src/date/comparator.php

171 lines 6.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikBooking
4 * @subpackage core
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2025 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 /**
15 * Helper class for dates comparison.
16 *
17 * @since 1.18.6 (J) - 1.8.6 (WP)
18 */
19 class VBODateComparator
20 {
21 /**
22 * Aligns a given date to the same weekday in a different year, applying
23 * corrections for leap-year-induced weekday shifts when necessary.
24 *
25 * Process overview:
26 *
27 * 1. Shift the original date by the difference in years to obtain the
28 * tentative equivalent date in the target year.
29 *
30 * 2. Compare the weekday of the original date with the weekday of the shifted
31 * date, then compute the minimal forward/backward number of steps needed
32 * to realign the weekday (using modular arithmetic).
33 *
34 * 3. If the date is on or after March 1st, evaluate whether a leap-year
35 * correction is required, because leap years can cause unexpected weekday
36 * shifts due to the extra day (February 29).
37 *
38 * 4. Apply the final computed weekday shift to the target date and return it.
39 *
40 * @param int|string|DateTime $date The source date to convert.
41 * @param int $year The target year to align the weekday to.
42 *
43 * @return DateTime A new DateTime object representing the input date
44 * shifted to the target year while preserving the weekday.
45 */
46 public static function alignWeekDay($date, int $year): DateTime
47 {
48 if (is_numeric($date)) {
49 // convert from timestamp
50 $date = date('Y-m-d', $date);
51 }
52
53 if (is_string($date)) {
54 // create from date
55 $date = JFactory::getDate($date);
56 }
57
58 // calculate the difference in years
59 $diffYears = $year - (int) $date->format('Y');
60
61 // shift the date for the specified year
62 $target = clone $date;
63 $target->modify(($diffYears >= 0 ? '+' : '') . $diffYears . ' years');
64
65 // fetch week days for both original and target dates
66 $originalWeekDay = (int) $date->format('N');
67 $targetWeekDay = (int) $target->format('N');
68
69 // calculate forwards and backward steps
70 $fwdSteps = ($originalWeekDay - $targetWeekDay + 7) % 7;
71 $bwdSteps = ($targetWeekDay - $originalWeekDay + 7) % 7;
72
73 // use the path that takes the lowest number of steps
74 $diffWeekDays = $fwdSteps < $bwdSteps ? ('+' . $fwdSteps) : ('-' . $bwdSteps);
75
76 // check whether we should manually fix the unexpected behavior that occurs with leap years,
77 // only for dates equal or after the 1st of March
78 if ($date->format('md') > '0229' && static::shouldFixLeapYear($date->format('Y'), $year)) {
79 // -3 steps becomes +4, +3 steps becomes -4
80 $diffWeekDays = $diffWeekDays == "-3" ? '+4' : '-4';
81 }
82
83 // shift the target date by the specified days
84 $target->modify($diffWeekDays . ' days');
85
86 return $target;
87 }
88
89 /**
90 * Calculates the minimum number of "steps" needed to align the weekday
91 * of a given date with the weekday of the same date in another year.
92 *
93 * A "step" represents a shift of days forward (positive value) or backward
94 * (negative value) within the weekly cycle (Mon–Sun).
95 * The goal is to determine whether moving forward or backward requires fewer
96 * steps to go from the weekday of the original date to the weekday of the
97 * target date in the specified year.
98 *
99 * @param DateTime $date The reference date.
100 * @param int $year The target year for calculating the weekday difference.
101 *
102 * @return int Minimum number of steps (positive for forward movement; negative for backward movement).
103 */
104 protected static function calcLowestSteps(DateTime $date, int $year): int
105 {
106 // calculate the difference in years
107 $diffYears = $year - (int) $date->format('Y');
108
109 // shift the date for the specified year
110 $target = clone $date;
111 $target->modify(($diffYears >= 0 ? '+' : '') . $diffYears . ' years');
112
113 // fetch week days for both original and target dates
114 $originalWeekDay = (int) $date->format('N');
115 $targetWeekDay = (int) $target->format('N');
116
117 // calculate forwards and backward steps
118 $fwdSteps = ($originalWeekDay - $targetWeekDay + 7) % 7;
119 $bwdSteps = ($targetWeekDay - $originalWeekDay + 7) % 7;
120
121 // use the path that takes the lowest number of steps
122 return $fwdSteps < $bwdSteps ? $fwdSteps : $bwdSteps * -1;
123 }
124
125 /**
126 * Determines whether a leap-year adjustment should be applied when comparing
127 * or aligning dates between two different years.
128 *
129 * The logic checks whether one of the two years is a leap year and the other is not.
130 * If both years are either leap years or non-leap years, no correction is needed.
131 *
132 * If exactly one year is a leap year, the function calculates the minimal weekday
133 * shift (in steps) between January 1st of the current year and January 1st of the
134 * target year.
135 *
136 * A leap-year correction is required in two specific cases:
137 * - The current year *is* a leap year and the weekday shift is exactly +3 steps.
138 * - The target year *is* a leap year and the weekday shift is exactly -3 steps.
139 *
140 * These specific offsets occur because leap years introduce an extra day (Feb 29),
141 * which causes the weekly cycle to advance or retreat by 2–3 weekday positions,
142 * depending on direction.
143 *
144 * @param int $currentYear The year from which the calculation originates.
145 * @param int $targetYear The destination year being evaluated.
146 *
147 * @return bool True if a leap-year adjustment is required, false otherwise.
148 */
149 protected static function shouldFixLeapYear(int $currentYear, int $targetYear): bool {
150 // check whether the current year and the target one are leap
151 $currentYearLeap = checkdate(2, 29, $currentYear);
152 $targetYearLeap = checkdate(2, 29, $targetYear);
153
154 // apply XOR to leap years, as we need to apply the correction only if one of them is leap
155 if (!($currentYearLeap ^ $targetYearLeap)) {
156 // correction not needed
157 return false;
158 }
159
160 // calculate the minimum steps
161 $steps = static::calcLowestSteps(new DateTime("$currentYear-01-01"), $targetYear);
162
163 // check whether the correction should be applied
164 if (($currentYearLeap && $steps === 3) || ($targetYearLeap && $steps === -3)) {
165 return true;
166 }
167
168 return false;
169 }
170 }
171