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

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