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

374 lines 13.2 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 getTimestamp($timezone = 'UTC')
157 {
158 $timezone = self::getValidatedTimeZone($timezone);
159 $dateTime = new \DateTime(gmdate('Y-m-d H:i:s'), new \DateTimeZone('UTC')); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
160 $dateTime->setTimezone(new \DateTimeZone($timezone));
161 $date = $dateTime->format('Y-m-d H:i:s');
162 return strtotime($date);
163 }
164
165 public static function formatToLocale($dateTime, $for = 'date')
166 {
167 // $for can be date | date_time | time
168 $dateFormat = get_option('date_format');
169
170 if ($for == 'date_time') {
171 $dateFormat .= ' ' . get_option('time_format');
172 } elseif ($for == 'time') {
173 $dateFormat = get_option('time_format');
174 }
175
176 return date_i18n($dateFormat, strtotime($dateTime)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
177 }
178
179 public static function getAvailableDateFormats()
180 {
181 $dateFormats = apply_filters('fluent_booking/available_date_formats', [
182 'm/d/Y' => 'm/d/Y - (Ex: 05/20/2024)', // USA
183 'd/m/Y' => 'd/m/Y - (Ex: 20/05/2024)', // Canada, UK
184 'd.m.Y' => 'd.m.Y - (Ex: 20.05.2024)', // Germany
185 'n/j/y' => 'n/j/y - (Ex: 5/20/24)',
186 'm/d/y' => 'm/d/y - (Ex: 05/20/24)',
187 'M/d/Y' => 'M/d/Y - (Ex: May/20/2024)',
188 'y/m/d' => 'y/m/d - (Ex: 24/05/20)',
189 'Y-m-d' => 'Y-m-d - (Ex: 2024-05-20)',
190 'd-M-y' => 'd-M-y - (Ex: 20-May-24)',
191 'F j, Y' => 'F j, Y - (Ex: May 20, 2024)'
192 ]);
193
194 $formatted = [];
195 foreach ($dateFormats as $format => $label) {
196 $formatted[] = [
197 'label' => $label,
198 'value' => $format,
199 ];
200 }
201 return $formatted;
202 }
203
204 public static function getFormattedEventTime($eventTime)
205 {
206 $timeZone = self::guessTimeZone();
207
208 $convertedTime = self::convertToTimeZone($eventTime, 'UTC', $timeZone);
209
210 $eventTime = self::formatToLocale($convertedTime, 'time');
211
212 $eventDate = self::formatToLocale($convertedTime, 'date');
213
214 return $eventTime . ', ' . $eventDate . ' (' . $timeZone . ')';
215 }
216
217 public static function convertPhpDateToDayJSFormay($phpFormat)
218 {
219 // Mapping PHP date format characters to Day.js format characters
220 $replacements = [
221 // Day
222 'd' => 'DD', // Day of the month, 2 digits with leading zeros
223 'D' => 'ddd', // A textual representation of a day, three letters
224 'j' => 'D', // Day of the month without leading zeros
225 'l' => 'dddd', // A full textual representation of the day of the week
226 'N' => 'E', // ISO-8601 numeric representation of the day of the week
227 'S' => 'o', // English ordinal suffix for the day of the month, 2 characters
228 'w' => 'd', // Numeric representation of the day of the week
229 'z' => 'DDD', // The day of the year (starting from 0)
230
231 // Week
232 'W' => 'W', // ISO-8601 week number of year, weeks starting on Monday
233
234 // Month
235 'F' => 'MMMM', // A full textual representation of a month
236 'm' => 'MM', // Numeric representation of a month, with leading zeros
237 'M' => 'MMM', // A short textual representation of a month, three letters
238 'n' => 'M', // Numeric representation of a month, without leading zeros
239 't' => '', // Not supported in Day.js (Number of days in the given month)
240
241 // Year
242 'L' => '', // Not supported in Day.js (Whether it's a leap year)
243 'o' => 'GGGG', // ISO-8601 week-numbering year
244 'Y' => 'YYYY', // A full numeric representation of a year, 4 digits
245 'y' => 'YY', // A two digit representation of a year
246
247 // Time
248 'a' => 'a', // Lowercase Ante meridiem and Post meridiem
249 'A' => 'A', // Uppercase Ante meridiem and Post meridiem
250 'B' => '', // Not supported in Day.js (Swatch Internet time)
251 'g' => 'h', // 12-hour format of an hour without leading zeros
252 'G' => 'H', // 24-hour format of an hour without leading zeros
253 'h' => 'hh', // 12-hour format of an hour with leading zeros
254 'H' => 'HH', // 24-hour format of an hour with leading zeros
255 'i' => 'mm', // Minutes with leading zeros
256 's' => 'ss', // Seconds with leading zeros
257 'u' => 'SSS', // Milliseconds (Day.js uses SSS for fractional seconds)
258 'v' => 'SSS', // Milliseconds (Day.js uses SSS for fractional seconds)
259
260 // Timezone
261 'e' => '', // Not supported in Day.js (Timezone identifier)
262 'I' => '', // Not supported in Day.js (Whether or not the date is in daylight saving time)
263 'O' => 'ZZ', // Difference to Greenwich time (GMT) in hours
264 'P' => 'Z', // Difference to Greenwich time (GMT) with colon between hours and minutes
265 'T' => '', // Not supported in Day.js (Timezone abbreviation)
266 'Z' => '', // Not supported in Day.js (Timezone offset in seconds)
267
268 // Full Date/Time
269 'c' => 'YYYY-MM-DDTHH:mm:ssZ', // ISO 8601 date
270 'r' => 'ddd, DD MMM YYYY HH:mm:ss ZZ', // RFC 2822 formatted date
271 'U' => 'X', // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
272 ];
273
274 // Replace each PHP date format character with Day.js equivalent
275 $dayjsFormat = "";
276
277 for ($i = 0; $i < strlen($phpFormat); $i++) {
278 $char = $phpFormat[$i];
279
280 // Check if the character is escaped
281 if ($char === "\\") {
282 // Add the next character to the result as is, without mapping
283 $i++;
284 if ($i < strlen($phpFormat)) {
285 $dayjsFormat .= "\\" . $phpFormat[$i];
286 }
287 continue;
288 }
289
290 // Add the mapped character or the character itself if not found in the mapping
291 $dayjsFormat .= $replacements[$char] ?? $char;
292 }
293
294 return $dayjsFormat;
295 }
296
297 public static function getDateFormatter($isDayJs = false)
298 {
299 $format = get_option('date_format');
300 if ($isDayJs) {
301 return self::convertPhpDateToDayJSFormay($format);
302 }
303
304 return $format;
305 }
306
307 public static function getTimeFormatter($isDayJs = false)
308 {
309 $format = get_option('time_format');
310 if ($isDayJs) {
311 return self::convertPhpDateToDayJSFormay($format);
312 }
313
314 return $format;
315 }
316
317 public static function getTodayDate($timezone = 'UTC', $format = 'Y-m-d')
318 {
319 $timezone = self::getValidatedTimeZone($timezone);
320 $dateTime = new \DateTime('now', new \DateTimeZone($timezone));
321 return $dateTime->format($format);
322 }
323
324 public static function getDayDifference($dateTime, $fromTimeZone, $toTimeZone, $refDate = 'now')
325 {
326 $fromTimeZone = self::getValidatedTimeZone($fromTimeZone);
327 $toTimeZone = self::getValidatedTimeZone($toTimeZone);
328
329 if ($refDate != 'now') {
330 $dateTime = gmdate('Y-m-d H:i', strtotime($refDate . ' ' . gmdate('H:i', strtotime($dateTime)))); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
331 }
332
333 $currentDate = new \DateTime($refDate, new \DateTimeZone($fromTimeZone));
334
335 $originalDateTime = new \DateTime($dateTime, new \DateTimeZone($fromTimeZone));
336
337 $originalDateTime->setTimezone(new \DateTimeZone($toTimeZone));
338
339 return $originalDateTime->format('z') - $currentDate->format('z');
340 }
341
342 public static function getDaylightSavingTime($timezone)
343 {
344 $timezone = self::getValidatedTimeZone($timezone);
345 $timezone = new \DateTimeZone($timezone);
346 $dateTime = new \DateTime('2024-01-01', $timezone);
347
348 $currentOffset = $timezone->getOffset($dateTime);
349
350 $sixMonthsAgo = clone $dateTime;
351 $sixMonthsAgo->modify('+6 month');
352 $offsetBefore = $timezone->getOffset($sixMonthsAgo);
353
354 $dstDifference = $currentOffset - $offsetBefore;
355
356 return abs($dstDifference) / 60;
357 }
358
359 public static function isDaylightSavingActive($dateTime, $timezone)
360 {
361 if (!$dateTime || !$timezone) {
362 return false;
363 }
364
365 $timezone = self::getValidatedTimeZone($timezone);
366
367 $dateTimeObject = new \DateTime($dateTime, new \DateTimeZone($timezone));
368
369 $isDstActive = $dateTimeObject->format('I');
370
371 return $isDstActive;
372 }
373 }
374