PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 1.7.0
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v1.7.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 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.7.0, at app/Services/DateTimeHelper.php

450 lines 17.5 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 guessTimeZone()
67 {
68 $timeZone = self::getTimeZone();
69
70 if (isset($_COOKIE['fluent_booking_user_timezone'])) {
71 $timeZone = $_COOKIE['fluent_booking_user_timezone'];
72 }
73
74 return $timeZone;
75 }
76
77 public static function getValidatedTimeZone($requestedTimeZone)
78 {
79 static $cached = [];
80 if (isset($cached[$requestedTimeZone])) {
81 return $cached[$requestedTimeZone];
82 }
83
84 if (!in_array($requestedTimeZone, \DateTimeZone::listIdentifiers())) {
85 $requestedTimeZone = apply_filters('fluent_booking/fallback_timezone', self::getTimeZone(), $requestedTimeZone);
86 }
87
88 $cached[$requestedTimeZone] = $requestedTimeZone;
89 return $requestedTimeZone;
90 }
91
92 public static function convertToUtc($dateTime, $timezone, $format = 'Y-m-d H:i:s')
93 {
94
95 $timezone = self::getValidatedTimeZone($timezone);
96
97 $dateTime = new \DateTime($dateTime, new \DateTimeZone($timezone));
98 $dateTime->setTimezone(new \DateTimeZone('UTC'));
99 return $dateTime->format($format);
100 }
101
102 public static function convertFromUtc($dateTime, $timezone, $format = 'Y-m-d H:i:s')
103 {
104 $dateTime = new \DateTime($dateTime, new \DateTimeZone('UTC'));
105
106 if ($timezone != 'UTC') {
107 $timezone = self::getValidatedTimeZone($timezone);
108 $dateTime->setTimezone(new \DateTimeZone($timezone));
109 }
110
111 return $dateTime->format($format);
112 }
113
114 public static function convertToTimeZone($dateTime, $fromTimeZone, $toTimeZone, $format = 'Y-m-d H:i:s', $date = null)
115 {
116 if ($fromTimeZone == $toTimeZone) {
117 return gmdate($format, strtotime($dateTime)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
118 }
119
120 if ($date) {
121 $dateTime = gmdate('Y-m-d H:i', strtotime($date . ' ' . gmdate('H:i', strtotime($dateTime)))); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
122 }
123
124 $fromTimeZone = self::getValidatedTimeZone($fromTimeZone);
125 $toTimeZone = self::getValidatedTimeZone($toTimeZone);
126
127 $dateTime = new \DateTime($dateTime, new \DateTimeZone($fromTimeZone));
128 $dateTime->setTimezone(new \DateTimeZone($toTimeZone));
129 return $dateTime->format($format);
130 }
131
132 public static function getDateWithoutDST($timezone)
133 {
134 if (!self::isDaylightSavingActive('2024-01-03', $timezone)) {
135 return '2024-01-03';
136 }
137 return '2024-06-03';
138 }
139
140 public static function convertToIso($dateTime, $fromTimeZone = 'UTC')
141 {
142 $fromTimeZone = self::getValidatedTimeZone($fromTimeZone);
143
144 $dateTime = new \DateTime($dateTime, new \DateTimeZone($fromTimeZone));
145 return $dateTime->format('Y-m-d\TH:i:s\Z');
146 }
147
148 public static function convertFromIso($dateTime, $toTimeZone = 'UTC')
149 {
150 $toTimeZone = self::getValidatedTimeZone($toTimeZone);
151 $dateTime = new \DateTime($dateTime, new \DateTimeZone('UTC'));
152 $dateTime->setTimezone(new \DateTimeZone($toTimeZone));
153 return $dateTime->format('Y-m-d H:i:s');
154 }
155
156 public static function getIsoDurationInMinutes($duration)
157 {
158 if (!$duration) {
159 return 0;
160 }
161
162 $duration = new \DateInterval($duration);
163
164 return ($duration->d * 24 * 60) + ($duration->h * 60) + ($duration->i) + ($duration->s / 60);
165 }
166
167 public static function getTimestamp($timezone = 'UTC')
168 {
169 $timezone = self::getValidatedTimeZone($timezone);
170 $dateTime = new \DateTime(gmdate('Y-m-d H:i:s'), new \DateTimeZone('UTC')); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
171 $dateTime->setTimezone(new \DateTimeZone($timezone));
172 $date = $dateTime->format('Y-m-d H:i:s');
173 return strtotime($date);
174 }
175
176 public static function formatToLocale($dateTime, $for = 'date')
177 {
178 // $for can be date | date_time | time
179 $dateFormat = get_option('date_format');
180
181 if ($for == 'date_time') {
182 $dateFormat .= ' ' . get_option('time_format');
183 } elseif ($for == 'time') {
184 $dateFormat = get_option('time_format');
185 }
186
187 return date_i18n($dateFormat, strtotime($dateTime)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
188 }
189
190 public static function getAvailableDateFormats()
191 {
192 $dateFormats = apply_filters('fluent_booking/available_date_formats', [
193 'm/d/Y' => 'm/d/Y - (Ex: 05/20/2024)', // USA
194 'd/m/Y' => 'd/m/Y - (Ex: 20/05/2024)', // Canada, UK
195 'd.m.Y' => 'd.m.Y - (Ex: 20.05.2024)', // Germany
196 'n/j/y' => 'n/j/y - (Ex: 5/20/24)',
197 'm/d/y' => 'm/d/y - (Ex: 05/20/24)',
198 'M/d/Y' => 'M/d/Y - (Ex: May/20/2024)',
199 'y/m/d' => 'y/m/d - (Ex: 24/05/20)',
200 'Y-m-d' => 'Y-m-d - (Ex: 2024-05-20)',
201 'd-M-y' => 'd-M-y - (Ex: 20-May-24)',
202 'F j, Y' => 'F j, Y - (Ex: May 20, 2024)'
203 ]);
204
205 $formatted = [];
206 foreach ($dateFormats as $format => $label) {
207 $formatted[] = [
208 'label' => $label,
209 'value' => $format,
210 ];
211 }
212 return $formatted;
213 }
214
215 public static function getFormattedEventTime($eventTime)
216 {
217 $timeZone = self::guessTimeZone();
218
219 $convertedTime = self::convertToTimeZone($eventTime, 'UTC', $timeZone);
220
221 $eventTime = self::formatToLocale($convertedTime, 'time');
222
223 $eventDate = self::formatToLocale($convertedTime, 'date');
224
225 return $eventTime . ', ' . $eventDate . ' (' . $timeZone . ')';
226 }
227
228 public static function convertPhpDateToDayJSFormay($phpFormat)
229 {
230 // Mapping PHP date format characters to Day.js format characters
231 $replacements = [
232 // Day
233 'd' => 'DD', // Day of the month, 2 digits with leading zeros
234 'D' => 'ddd', // A textual representation of a day, three letters
235 'j' => 'D', // Day of the month without leading zeros
236 'l' => 'dddd', // A full textual representation of the day of the week
237 'N' => 'E', // ISO-8601 numeric representation of the day of the week
238 'S' => 'o', // English ordinal suffix for the day of the month, 2 characters
239 'w' => 'd', // Numeric representation of the day of the week
240 'z' => 'DDD', // The day of the year (starting from 0)
241
242 // Week
243 'W' => 'W', // ISO-8601 week number of year, weeks starting on Monday
244
245 // Month
246 'F' => 'MMMM', // A full textual representation of a month
247 'm' => 'MM', // Numeric representation of a month, with leading zeros
248 'M' => 'MMM', // A short textual representation of a month, three letters
249 'n' => 'M', // Numeric representation of a month, without leading zeros
250 't' => '', // Not supported in Day.js (Number of days in the given month)
251
252 // Year
253 'L' => '', // Not supported in Day.js (Whether it's a leap year)
254 'o' => 'GGGG', // ISO-8601 week-numbering year
255 'Y' => 'YYYY', // A full numeric representation of a year, 4 digits
256 'y' => 'YY', // A two digit representation of a year
257
258 // Time
259 'a' => 'a', // Lowercase Ante meridiem and Post meridiem
260 'A' => 'A', // Uppercase Ante meridiem and Post meridiem
261 'B' => '', // Not supported in Day.js (Swatch Internet time)
262 'g' => 'h', // 12-hour format of an hour without leading zeros
263 'G' => 'H', // 24-hour format of an hour without leading zeros
264 'h' => 'hh', // 12-hour format of an hour with leading zeros
265 'H' => 'HH', // 24-hour format of an hour with leading zeros
266 'i' => 'mm', // Minutes with leading zeros
267 's' => 'ss', // Seconds with leading zeros
268 'u' => 'SSS', // Milliseconds (Day.js uses SSS for fractional seconds)
269 'v' => 'SSS', // Milliseconds (Day.js uses SSS for fractional seconds)
270
271 // Timezone
272 'e' => '', // Not supported in Day.js (Timezone identifier)
273 'I' => '', // Not supported in Day.js (Whether or not the date is in daylight saving time)
274 'O' => 'ZZ', // Difference to Greenwich time (GMT) in hours
275 'P' => 'Z', // Difference to Greenwich time (GMT) with colon between hours and minutes
276 'T' => '', // Not supported in Day.js (Timezone abbreviation)
277 'Z' => '', // Not supported in Day.js (Timezone offset in seconds)
278
279 // Full Date/Time
280 'c' => 'YYYY-MM-DDTHH:mm:ssZ', // ISO 8601 date
281 'r' => 'ddd, DD MMM YYYY HH:mm:ss ZZ', // RFC 2822 formatted date
282 'U' => 'X', // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
283 ];
284
285 // Replace each PHP date format character with Day.js equivalent
286 $dayjsFormat = "";
287
288 for ($i = 0; $i < strlen($phpFormat); $i++) {
289 $char = $phpFormat[$i];
290
291 // Special handling for G\hi pattern
292 if ($char === 'G' && $i + 2 < strlen($phpFormat) &&
293 $phpFormat[$i + 1] === '\\' && $phpFormat[$i + 2] === 'h') {
294 $dayjsFormat .= 'H[h]';
295 $i += 2;
296 continue;
297 }
298
299 // Check if the character is escaped
300 if ($char === "\\") {
301 // Add the next character to the result as is, without mapping
302 $i++;
303 if ($i < strlen($phpFormat)) {
304 $dayjsFormat .= "\\" . $phpFormat[$i];
305 }
306 continue;
307 }
308
309 // Add the mapped character or the character itself if not found in the mapping
310 $dayjsFormat .= $replacements[$char] ?? $char;
311 }
312
313 return $dayjsFormat;
314 }
315
316 public static function getDateFormatter($isDayJs = false)
317 {
318 $format = get_option('date_format');
319 if ($isDayJs) {
320 return self::convertPhpDateToDayJSFormay($format);
321 }
322
323 return $format;
324 }
325
326 public static function getTimeFormatter($isDayJs = false)
327 {
328 $format = get_option('time_format');
329 if ($isDayJs) {
330 return self::convertPhpDateToDayJSFormay($format);
331 }
332
333 return $format;
334 }
335
336 public static function getTodayDate($timezone = 'UTC', $format = 'Y-m-d')
337 {
338 $timezone = self::getValidatedTimeZone($timezone);
339 $dateTime = new \DateTime('now', new \DateTimeZone($timezone));
340 return $dateTime->format($format);
341 }
342
343 public static function getDayDifference($dateTime, $fromTimeZone, $toTimeZone, $refDate = 'now')
344 {
345 $fromTimeZone = self::getValidatedTimeZone($fromTimeZone);
346 $toTimeZone = self::getValidatedTimeZone($toTimeZone);
347
348 if ($refDate != 'now') {
349 $dateTime = gmdate('Y-m-d H:i', strtotime($refDate . ' ' . gmdate('H:i', strtotime($dateTime)))); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
350 }
351
352 $currentDate = new \DateTime($refDate, new \DateTimeZone($fromTimeZone));
353
354 $originalDateTime = new \DateTime($dateTime, new \DateTimeZone($fromTimeZone));
355
356 $originalDateTime->setTimezone(new \DateTimeZone($toTimeZone));
357
358 return $originalDateTime->format('z') - $currentDate->format('z');
359 }
360
361 public static function getDaylightSavingTime($timezone)
362 {
363 $timezone = self::getValidatedTimeZone($timezone);
364 $timezone = new \DateTimeZone($timezone);
365 $dateTime = new \DateTime('2024-01-01', $timezone);
366
367 $currentOffset = $timezone->getOffset($dateTime);
368
369 $sixMonthsAgo = clone $dateTime;
370 $sixMonthsAgo->modify('+6 month');
371 $offsetBefore = $timezone->getOffset($sixMonthsAgo);
372
373 $dstDifference = $currentOffset - $offsetBefore;
374
375 return abs($dstDifference) / 60;
376 }
377
378 public static function isDaylightSavingActive($dateTime, $timezone)
379 {
380 if (!$dateTime || !$timezone) {
381 return false;
382 }
383
384 $timezone = self::getValidatedTimeZone($timezone);
385
386 $dateTimeObject = new \DateTime($dateTime, new \DateTimeZone($timezone));
387
388 $isDstActive = $dateTimeObject->format('I');
389
390 if ($timezone == 'Europe/Dublin') {
391 return !$isDstActive;
392 }
393
394 return $isDstActive;
395 }
396
397 public static function getI18nDateTimeConfig()
398 {
399 return apply_filters('fluent_booking/i18n_date_time_config', [
400 'weekdays' => array(
401 'sunday' => _x('Sunday', 'calendar day full', 'fluent-booking'),
402 'monday' => _x('Monday', 'calendar day full', 'fluent-booking'),
403 'tuesday' => _x('Tuesday', 'calendar day full', 'fluent-booking'),
404 'wednesday' => _x('Wednesday', 'calendar day full', 'fluent-booking'),
405 'thursday' => _x('Thursday', 'calendar day full', 'fluent-booking'),
406 'friday' => _x('Friday', 'calendar day full', 'fluent-booking'),
407 'saturday' => _x('Saturday', 'calendar day full', 'fluent-booking'),
408 ),
409 'months' => array(
410 'January' => _x('January', 'calendar month name full', 'fluent-booking'),
411 'February' => _x('February', 'calendar month name full', 'fluent-booking'),
412 'March' => _x('March', 'calendar month name full', 'fluent-booking'),
413 'April' => _x('April', 'calendar month name full', 'fluent-booking'),
414 'May' => _x('May', 'calendar month name full', 'fluent-booking'),
415 'June' => _x('June', 'calendar month name full', 'fluent-booking'),
416 'July' => _x('July', 'calendar month name full', 'fluent-booking'),
417 'August' => _x('August', 'calendar month name full', 'fluent-booking'),
418 'September' => _x('September', 'calendar month name full', 'fluent-booking'),
419 'October' => _x('October', 'calendar month name full', 'fluent-booking'),
420 'November' => _x('November', 'calendar month name full', 'fluent-booking'),
421 'December' => _x('December', 'calendar month name full', 'fluent-booking')
422 ),
423 'weekdaysShort' => array(
424 'sun' => _x('Sun', 'calendar day short', 'fluent-booking'),
425 'mon' => _x('Mon', 'calendar day short', 'fluent-booking'),
426 'tue' => _x('Tue', 'calendar day short', 'fluent-booking'),
427 'wed' => _x('Wed', 'calendar day short', 'fluent-booking'),
428 'thu' => _x('Thu', 'calendar day short', 'fluent-booking'),
429 'fri' => _x('Fri', 'calendar day short', 'fluent-booking'),
430 'sat' => _x('Sat', 'calendar day short', 'fluent-booking')
431 ),
432 'monthsShort' => array(
433 'jan' => _x('Jan', 'calendar month name short', 'fluent-booking'),
434 'feb' => _x('Feb', 'calendar month name short', 'fluent-booking'),
435 'mar' => _x('Mar', 'calendar month name short', 'fluent-booking'),
436 'apr' => _x('Apr', 'calendar month name short', 'fluent-booking'),
437 'may' => _x('May', 'calendar month name short', 'fluent-booking'),
438 'jun' => _x('Jun', 'calendar month name short', 'fluent-booking'),
439 'jul' => _x('Jul', 'calendar month name short', 'fluent-booking'),
440 'aug' => _x('Aug', 'calendar month name short', 'fluent-booking'),
441 'sep' => _x('Sep', 'calendar month name short', 'fluent-booking'),
442 'oct' => _x('Oct', 'calendar month name short', 'fluent-booking'),
443 'nov' => _x('Nov', 'calendar month name short', 'fluent-booking'),
444 'dec' => _x('Dec', 'calendar month name short', 'fluent-booking')
445 ),
446 'numericSystem' => _x('0_1_2_3_4_5_6_7_8_9', 'calendar numeric system - Sequence must need to maintained', 'fluent-booking'),
447 ]);
448 }
449 }
450