PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 1.5.20
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v1.5.20
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 1.7.2 All 33 releases
fluent-booking / app / Services / DateTimeHelper.php

DateTimeHelper.php in Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution 1.5.20, at app/Services/DateTimeHelper.php

350 lines 12.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBooking\App\Services;
4
5 class DateTimeHelper
6 {
7 public static function getTimeZones($grouped = false)
8 {
9 $tz_identifiers = timezone_identifiers_list();
10
11 if (!$grouped) {
12 return $tz_identifiers;
13 }
14
15 $lists = [];
16
17 $topLevels = [];
18 foreach ($tz_identifiers as $tz) {
19 $parts = explode('/', $tz);
20 if (count($parts) > 1) {
21 $lists[$parts[0]][] = $tz;
22 } else {
23 $topLevels[$tz][] = $tz;
24 }
25 }
26
27 return array_merge($topLevels, $lists);
28 }
29
30 public static function getFlatGroupedTimeZones()
31 {
32 $tz_identifiers = timezone_identifiers_list();
33
34 $lists = [];
35 $topLevels = [];
36 foreach ($tz_identifiers as $tz) {
37 $parts = explode('/', $tz);
38 if (count($parts) > 1) {
39 $lists[] = [
40 'label' => $tz,
41 'value' => $tz,
42 'group' => $parts[0]
43 ];
44 } else {
45 $topLevels[] = [
46 'label' => $tz,
47 'value' => $tz,
48 'group' => $tz
49 ];
50 }
51 }
52
53 return $topLevels + $lists;
54 }
55
56 public static function getTimeZone()
57 {
58 $timeZone = wp_timezone_string();
59 if (!$timeZone || !in_array($timeZone, \DateTimeZone::listIdentifiers())) {
60 $timeZone = 'UTC';
61 }
62
63 return $timeZone;
64 }
65
66 public static function getValidatedTimeZone($requestedTimeZone)
67 {
68 static $cached = [];
69 if (isset($cached[$requestedTimeZone])) {
70 return $cached[$requestedTimeZone];
71 }
72
73 if (!in_array($requestedTimeZone, \DateTimeZone::listIdentifiers())) {
74 $requestedTimeZone = apply_filters('fluent_booking/fallback_timezone', self::getTimeZone(), $requestedTimeZone);
75 }
76
77 $cached[$requestedTimeZone] = $requestedTimeZone;
78 return $requestedTimeZone;
79 }
80
81 public static function convertToUtc($dateTime, $timezone, $format = 'Y-m-d H:i:s')
82 {
83
84 $timezone = self::getValidatedTimeZone($timezone);
85
86 $dateTime = new \DateTime($dateTime, new \DateTimeZone($timezone));
87 $dateTime->setTimezone(new \DateTimeZone('UTC'));
88 return $dateTime->format($format);
89 }
90
91 public static function convertFromUtc($dateTime, $timezone, $format = 'Y-m-d H:i:s')
92 {
93 $dateTime = new \DateTime($dateTime, new \DateTimeZone('UTC'));
94
95 if ($timezone != 'UTC') {
96 $timezone = self::getValidatedTimeZone($timezone);
97 $dateTime->setTimezone(new \DateTimeZone($timezone));
98 }
99
100 return $dateTime->format($format);
101 }
102
103 public static function convertToTimeZone($dateTime, $fromTimeZone, $toTimeZone, $format = 'Y-m-d H:i:s', $date = null)
104 {
105 if ($fromTimeZone == $toTimeZone) {
106 return gmdate($format, strtotime($dateTime)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
107 }
108
109 if ($date) {
110 $dateTime = gmdate('Y-m-d H:i', strtotime($date . ' ' . gmdate('H:i', strtotime($dateTime)))); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
111 }
112
113 $fromTimeZone = self::getValidatedTimeZone($fromTimeZone);
114 $toTimeZone = self::getValidatedTimeZone($toTimeZone);
115
116 $dateTime = new \DateTime($dateTime, new \DateTimeZone($fromTimeZone));
117 $dateTime->setTimezone(new \DateTimeZone($toTimeZone));
118 return $dateTime->format($format);
119 }
120
121 public static function getDateWithoutDST($timezone)
122 {
123 if (!self::isDaylightSavingActive('2024-01-03', $timezone)) {
124 return '2024-01-03';
125 }
126 return '2024-06-03';
127 }
128
129 public static function convertToIso($dateTime, $fromTimeZone = 'UTC')
130 {
131 $fromTimeZone = self::getValidatedTimeZone($fromTimeZone);
132
133 $dateTime = new \DateTime($dateTime, new \DateTimeZone($fromTimeZone));
134 return $dateTime->format('Y-m-d\TH:i:s\Z');
135 }
136
137 public static function convertFromIso($dateTime, $toTimeZone = 'UTC')
138 {
139 $toTimeZone = self::getValidatedTimeZone($toTimeZone);
140 $dateTime = new \DateTime($dateTime, new \DateTimeZone('UTC'));
141 $dateTime->setTimezone(new \DateTimeZone($toTimeZone));
142 return $dateTime->format('Y-m-d H:i:s');
143 }
144
145 public static function getTimestamp($timezone = 'UTC')
146 {
147 $timezone = self::getValidatedTimeZone($timezone);
148 $dateTime = new \DateTime(gmdate('Y-m-d H:i:s'), new \DateTimeZone('UTC')); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
149 $dateTime->setTimezone(new \DateTimeZone($timezone));
150 $date = $dateTime->format('Y-m-d H:i:s');
151 return strtotime($date);
152 }
153
154 public static function formatToLocale($dateTime, $for = 'date')
155 {
156 // $for can be date | date_time | time
157 $dateFormat = get_option('date_format');
158
159 if ($for == 'date_time') {
160 $dateFormat .= ' ' . get_option('time_format');
161 } elseif ($for == 'time') {
162 $dateFormat = get_option('time_format');
163 }
164
165 return date_i18n($dateFormat, strtotime($dateTime)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
166 }
167
168 public static function getAvailableDateFormats()
169 {
170 $dateFormats = apply_filters('fluent_booking/available_date_formats', [
171 'm/d/Y' => 'm/d/Y - (Ex: 05/20/2024)', // USA
172 'd/m/Y' => 'd/m/Y - (Ex: 20/05/2024)', // Canada, UK
173 'd.m.Y' => 'd.m.Y - (Ex: 20.05.2024)', // Germany
174 'n/j/y' => 'n/j/y - (Ex: 5/20/24)',
175 'm/d/y' => 'm/d/y - (Ex: 05/20/24)',
176 'M/d/Y' => 'M/d/Y - (Ex: May/20/2024)',
177 'y/m/d' => 'y/m/d - (Ex: 24/05/20)',
178 'Y-m-d' => 'Y-m-d - (Ex: 2024-05-20)',
179 'd-M-y' => 'd-M-y - (Ex: 20-May-24)',
180 'F j, Y' => 'F j, Y - (Ex: May 20, 2024)'
181 ]);
182
183 $formatted = [];
184 foreach ($dateFormats as $format => $label) {
185 $formatted[] = [
186 'label' => $label,
187 'value' => $format,
188 ];
189 }
190 return $formatted;
191 }
192
193 public static function convertPhpDateToDayJSFormay($phpFormat)
194 {
195 // Mapping PHP date format characters to Day.js format characters
196 $replacements = [
197 // Day
198 'd' => 'DD', // Day of the month, 2 digits with leading zeros
199 'D' => 'ddd', // A textual representation of a day, three letters
200 'j' => 'D', // Day of the month without leading zeros
201 'l' => 'dddd', // A full textual representation of the day of the week
202 'N' => 'E', // ISO-8601 numeric representation of the day of the week
203 'S' => 'o', // English ordinal suffix for the day of the month, 2 characters
204 'w' => 'd', // Numeric representation of the day of the week
205 'z' => 'DDD', // The day of the year (starting from 0)
206
207 // Week
208 'W' => 'W', // ISO-8601 week number of year, weeks starting on Monday
209
210 // Month
211 'F' => 'MMMM', // A full textual representation of a month
212 'm' => 'MM', // Numeric representation of a month, with leading zeros
213 'M' => 'MMM', // A short textual representation of a month, three letters
214 'n' => 'M', // Numeric representation of a month, without leading zeros
215 't' => '', // Not supported in Day.js (Number of days in the given month)
216
217 // Year
218 'L' => '', // Not supported in Day.js (Whether it's a leap year)
219 'o' => 'GGGG', // ISO-8601 week-numbering year
220 'Y' => 'YYYY', // A full numeric representation of a year, 4 digits
221 'y' => 'YY', // A two digit representation of a year
222
223 // Time
224 'a' => 'a', // Lowercase Ante meridiem and Post meridiem
225 'A' => 'A', // Uppercase Ante meridiem and Post meridiem
226 'B' => '', // Not supported in Day.js (Swatch Internet time)
227 'g' => 'h', // 12-hour format of an hour without leading zeros
228 'G' => 'H', // 24-hour format of an hour without leading zeros
229 'h' => 'hh', // 12-hour format of an hour with leading zeros
230 'H' => 'HH', // 24-hour format of an hour with leading zeros
231 'i' => 'mm', // Minutes with leading zeros
232 's' => 'ss', // Seconds with leading zeros
233 'u' => 'SSS', // Milliseconds (Day.js uses SSS for fractional seconds)
234 'v' => 'SSS', // Milliseconds (Day.js uses SSS for fractional seconds)
235
236 // Timezone
237 'e' => '', // Not supported in Day.js (Timezone identifier)
238 'I' => '', // Not supported in Day.js (Whether or not the date is in daylight saving time)
239 'O' => 'ZZ', // Difference to Greenwich time (GMT) in hours
240 'P' => 'Z', // Difference to Greenwich time (GMT) with colon between hours and minutes
241 'T' => '', // Not supported in Day.js (Timezone abbreviation)
242 'Z' => '', // Not supported in Day.js (Timezone offset in seconds)
243
244 // Full Date/Time
245 'c' => 'YYYY-MM-DDTHH:mm:ssZ', // ISO 8601 date
246 'r' => 'ddd, DD MMM YYYY HH:mm:ss ZZ', // RFC 2822 formatted date
247 'U' => 'X', // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
248 ];
249
250 // Replace each PHP date format character with Day.js equivalent
251 $dayjsFormat = "";
252
253 for ($i = 0; $i < strlen($phpFormat); $i++) {
254 $char = $phpFormat[$i];
255
256 // Check if the character is escaped
257 if ($char === "\\") {
258 // Add the next character to the result as is, without mapping
259 $i++;
260 if ($i < strlen($phpFormat)) {
261 $dayjsFormat .= "\\" . $phpFormat[$i];
262 }
263 continue;
264 }
265
266 // Add the mapped character or the character itself if not found in the mapping
267 $dayjsFormat .= $replacements[$char] ?? $char;
268 }
269
270 return $dayjsFormat;
271 }
272
273 public static function getDateFormatter($isDayJs = false)
274 {
275 $format = get_option('date_format');
276 if ($isDayJs) {
277 return self::convertPhpDateToDayJSFormay($format);
278 }
279
280 return $format;
281 }
282
283 public static function getTimeFormatter($isDayJs = false)
284 {
285 $format = get_option('time_format');
286 if ($isDayJs) {
287 return self::convertPhpDateToDayJSFormay($format);
288 }
289
290 return $format;
291 }
292
293 public static function getTodayDate($timezone = 'UTC', $format = 'Y-m-d')
294 {
295 $timezone = self::getValidatedTimeZone($timezone);
296 $dateTime = new \DateTime('now', new \DateTimeZone($timezone));
297 return $dateTime->format($format);
298 }
299
300 public static function getDayDifference($dateTime, $fromTimeZone, $toTimeZone, $refDate = 'now')
301 {
302 $fromTimeZone = self::getValidatedTimeZone($fromTimeZone);
303 $toTimeZone = self::getValidatedTimeZone($toTimeZone);
304
305 if ($refDate != 'now') {
306 $dateTime = gmdate('Y-m-d H:i', strtotime($refDate . ' ' . gmdate('H:i', strtotime($dateTime)))); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
307 }
308
309 $currentDate = new \DateTime($refDate, new \DateTimeZone($fromTimeZone));
310
311 $originalDateTime = new \DateTime($dateTime, new \DateTimeZone($fromTimeZone));
312
313 $originalDateTime->setTimezone(new \DateTimeZone($toTimeZone));
314
315 return $originalDateTime->format('z') - $currentDate->format('z');
316 }
317
318 public static function getDaylightSavingTime($timezone)
319 {
320 $timezone = self::getValidatedTimeZone($timezone);
321 $timezone = new \DateTimeZone($timezone);
322 $dateTime = new \DateTime('2024-01-01', $timezone);
323
324 $currentOffset = $timezone->getOffset($dateTime);
325
326 $sixMonthsAgo = clone $dateTime;
327 $sixMonthsAgo->modify('+6 month');
328 $offsetBefore = $timezone->getOffset($sixMonthsAgo);
329
330 $dstDifference = $currentOffset - $offsetBefore;
331
332 return abs($dstDifference) / 60;
333 }
334
335 public static function isDaylightSavingActive($dateTime, $timezone)
336 {
337 if (!$dateTime || !$timezone) {
338 return false;
339 }
340
341 $timezone = self::getValidatedTimeZone($timezone);
342
343 $dateTimeObject = new \DateTime($dateTime, new \DateTimeZone($timezone));
344
345 $isDstActive = $dateTimeObject->format('I');
346
347 return $isDstActive;
348 }
349 }
350