PluginProbe
Import Social Events / 1.8.5
Import Social Events v1.8.5
1.9.1 1.9.0 trunk 1.0.0 1.0.1 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.2.0 1.3.0 1.4.0 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.6.0 1.6.1 1.6.10 1.6.11 1.6.12 All 61 releases
import-facebook-events / includes / lib / icalcreator / src / Util / DateTimeFactory.php

DateTimeFactory.php in Import Social Events 1.8.5, at includes/lib/icalcreator/src/Util/DateTimeFactory.php

665 lines 21.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * iCalcreator, the PHP class package managing iCal (rfc2445/rfc5445) calendar information.
4 *
5 * copyright (c) 2007-2021 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
6 * Link https://kigkonsult.se
7 * Package iCalcreator
8 * Version 2.30
9 * License Subject matter of licence is the software iCalcreator.
10 * The above copyright, link, package and version notices,
11 * this licence notice and the invariant [rfc5545] PRODID result use
12 * as implemented and invoked in iCalcreator shall be included in
13 * all copies or substantial portions of the iCalcreator.
14 *
15 * iCalcreator is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License as published
17 * by the Free Software Foundation, either version 3 of the License,
18 * or (at your option) any later version.
19 *
20 * iCalcreator is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU Lesser General Public License for more details.
24 *
25 * You should have received a copy of the GNU Lesser General Public License
26 * along with iCalcreator. If not, see <https://www.gnu.org/licenses/>.
27 *
28 * This file is a part of iCalcreator.
29 */
30
31 namespace Kigkonsult\Icalcreator\Util;
32
33 use DateTime;
34 use DateTimeInterface;
35 use Exception;
36 use InvalidArgumentException;
37 use Kigkonsult\Icalcreator\Vcalendar;
38
39 use function ctype_digit;
40 use function date_default_timezone_get;
41 use function in_array;
42 use function is_null;
43 use function is_string;
44 use function sprintf;
45 use function strcasecmp;
46 use function strlen;
47 use function strrpos;
48 use function strtotime;
49 use function substr;
50 use function trim;
51 use function var_export;
52
53 /**
54 * iCalcreator DateTime support class
55 *
56 * @see https://en.wikipedia.org/wiki/Iso8601
57 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
58 * @since 2.29.21 - 2020-01-31
59 */
60 class DateTimeFactory
61 {
62
63 /**
64 * @var array
65 * @static
66 */
67 public static $DEFAULTVALUEDATETIME = [ Vcalendar::VALUE => Vcalendar::DATE_TIME ];
68
69 /**
70 * @var string
71 * @static
72 */
73 public static $Ymd = 'Ymd';
74 public static $YmdTHis = 'Ymd\THis';
75 public static $YmdHis = 'YmdHis';
76 public static $YMDHISe = 'Y-m-d H:i:s e';
77
78 /**
79 * @var string
80 * @access private
81 * @static
82 */
83 private static $ERR1 = 'Invalid date : %s';
84 private static $ERR3 = 'Can\'t update date with timezone : %s';
85 private static $ERR4 = 'Invalid date \'%s\' - \'%s\'';
86
87 /**
88 * Return new DateTime object instance
89 *
90 * @param string $dateTimeString default 'now'
91 * @param string $timeZoneString
92 * @return DateTime
93 * @throws InvalidArgumentException
94 * @throws Exception
95 * @static
96 * @since 2.29.21 - 2020-01-31
97 */
98 public static function factory( $dateTimeString = null, $timeZoneString = null )
99 {
100 static $AT = '@';
101 if( is_null( $dateTimeString )) {
102 $dateTimeString = 'now';
103 }
104 if(( $AT == substr( $dateTimeString, 0, 1 )) &&
105 ctype_digit( substr( $dateTimeString, 1 ))) {
106 try {
107 $dateTime = new DateTime( $dateTimeString );
108 $dateTime->setTimezone( DateTimeZoneFactory::factory( Vcalendar::UTC ));
109 if( ! empty( $timeZoneString ) &&
110 ! DateTimeZoneFactory::isUTCtimeZone( $timeZoneString ) &&
111 ( false === $dateTime->setTimezone(
112 DateTimeZoneFactory::factory( $timeZoneString )
113 )
114 )) {
115 throw new InvalidArgumentException(
116 sprintf( self::$ERR3, $timeZoneString )
117 );
118 }
119 return $dateTime;
120 }
121 catch( InvalidArgumentException $e ) {
122 throw $e;
123 }
124 catch( Exception $e ) {
125 throw $e;
126 }
127 } // end if
128 return self::assertDateTimeString( $dateTimeString, $timeZoneString );
129 }
130
131 /**
132 * Assert DateTime String
133 *
134 * @param string $dateTimeString
135 * @param string $timeZoneString
136 * @return DateTime
137 * @throws InvalidArgumentException
138 * @static
139 * @since 2.27.8 - 2019-01-12
140 */
141 public static function assertDateTimeString(
142 $dateTimeString,
143 $timeZoneString = null
144 ) {
145 try {
146 $tz = empty( $timeZoneString )
147 ? null
148 : DateTimeZoneFactory::factory( $timeZoneString );
149 $dateTime = new DateTime( $dateTimeString, $tz );
150 }
151 catch( Exception $e ) {
152 throw new InvalidArgumentException(
153 sprintf( self::$ERR1, $dateTimeString ),
154 null,
155 $e );
156 }
157 return $dateTime;
158 }
159
160 /**
161 * Return DateTime if DateTimeInterface else string
162 *
163 * @param string|DateTimeInterface $value
164 * @return string|DateTime
165 * @throws Exception
166 * @static
167 * @since 2.29.16 2020-01-24
168 */
169 public static function cnvrtDateTimeInterface( $value )
170 {
171 if( $value instanceof DateTimeInterface ) {
172 try {
173 $dtTmp = new DateTime( null, $value->getTimezone());
174 $dtTmp->setTimestamp( $value->getTimestamp() );
175 }
176 catch( Exception $e ) {
177 throw $e;
178 }
179 return $dtTmp;
180 } // end if
181 return $value;
182 }
183
184 /**
185 * Return internal date (format) with parameters based on input date
186 *
187 * @param string|DateTimeInterface $value
188 * @param array $params
189 * @param bool $forceUTC
190 * @return array
191 * @throws Exception
192 * @throws InvalidArgumentException
193 * @static
194 * @since 2.29.16 2020-01-24
195 */
196 public static function setDate( $value, $params = [], $forceUTC = false )
197 {
198 $value = self::cnvrtDateTimeInterface( $value );
199 $output = [ Util::$LCparams => $params ];
200 $isValueDate = ParameterFactory::isParamsValueSet( $output, Vcalendar::DATE );
201 $paramTZid = ParameterFactory::getParamTzid( $output );
202 $isLocalTime = isset( $params[Util::$ISLOCALTIME] );
203 if( ! empty( $paramTZid )) {
204 if( DateTimeZoneFactory::hasOffset( $paramTZid )) {
205 $paramTZid =
206 DateTimeZoneFactory::getTimeZoneNameFromOffset( $paramTZid );
207 }
208 else {
209 DateTimeZoneFactory::assertDateTimeZone( $paramTZid );
210 }
211 } // end if
212 switch( true ) {
213 case ( $value instanceof DateTime ) :
214 $dateTime = self::conformDateTime(
215 $value,
216 $isValueDate,
217 $forceUTC,
218 $paramTZid
219 );
220 break;
221 case ( self::isStringAndDate( $value )) :
222 // string ex. "2006-08-03 10:12:18 [[[+/-]1234[56]] / timezone]"
223 $dateTime = self::conformStringDate(
224 $value, $isValueDate, $forceUTC, $isLocalTime, $paramTZid
225 );
226 if( $isLocalTime && $forceUTC ) {
227 $isLocalTime = false;
228 }
229 break;
230 default :
231 throw new InvalidArgumentException(
232 sprintf(
233 self::$ERR4,
234 var_export( $value, true ),
235 var_export( $params, true )
236 )
237 );
238 } // end switch
239 $output[Util::$LCvalue] = $dateTime;
240 self::conformDateTimeParams(
241 $output[Util::$LCparams],
242 $isValueDate,
243 $isLocalTime,
244 ( $forceUTC ? Vcalendar::UTC : $paramTZid )
245 );
246 return $output;
247 }
248
249 /**
250 * Return conformed DateTime
251 *
252 * @param DateTime $input
253 * @param bool $isValueDate
254 * @param bool $forceUTC
255 * @param string $paramTZid
256 * @return DateTime
257 * @static
258 * @since 2.29.1 - 2019-06-26
259 */
260 public static function conformDateTime(
261 DateTime $input,
262 $isValueDate,
263 $forceUTC,
264 & $paramTZid
265 ) {
266 switch( true ) {
267 case ( ! $isValueDate && $forceUTC ) :
268 $dateTime = self::setDateTimeTimeZone( $input, Vcalendar::UTC );
269 break;
270 case ( ! $forceUTC && ! empty( $paramTZid )) :
271 $dateTime = self::setDateTimeTimeZone( $input, $paramTZid );
272 break;
273 case ( self::dateTimeHasOffset( $input )) :
274 $dateTime = self::setDateTimeTimeZone(
275 $input,
276 $input->getTimezone()->getName()
277 );
278 break;
279 default :
280 $dateTime = $input;
281 break;
282 } // end switch
283 if( empty( $paramTZid )) {
284 $paramTZid = $dateTime->getTimezone()->getName();
285 }
286 return $dateTime;
287 }
288
289 /**
290 * Return conformed DateTime from string date
291 *
292 * @param string $input
293 * @param bool $isValueDate
294 * @param bool $forceUTC
295 * @param bool $isLocalTime
296 * @param string $paramTZid
297 * @return DateTime
298 * @throws Exception
299 * @throws InvalidArgumentException
300 * @static
301 * @since 2.29.1 - 2019-06-26
302 */
303 public static function conformStringDate(
304 $input,
305 $isValueDate,
306 $forceUTC,
307 & $isLocalTime,
308 & $paramTZid
309 ) {
310 list( $dateStr, $timezonePart ) = self::splitIntoDateStrAndTimezone( $input );
311 $isLocalTime = ( empty( $timezonePart ) && empty( $paramTZid ));
312 $dateTime = self::getDateTimeWithTimezoneFromString(
313 $dateStr,
314 $isLocalTime ? null : $timezonePart,
315 $isLocalTime ? Vcalendar::UTC : $paramTZid,
316 $forceUTC
317 );
318 if( ! $isValueDate && $forceUTC ) {
319 $dateTime = self::setDateTimeTimeZone( $dateTime, Vcalendar::UTC );
320 }
321 if( empty( $paramTZid ) && ! $isLocalTime ) {
322 $paramTZid = $dateTime->getTimezone()->getName();
323 }
324 return $dateTime;
325 }
326
327 /**
328 * Conform date parameters
329 *
330 * @param array $params
331 * @param bool $isValueDate
332 * @param bool $isLocalTime
333 * @param string $paramTZid
334 * @static
335 * @since 2.29.1 - 2019-06-27
336 */
337 public static function conformDateTimeParams(
338 array & $params,
339 $isValueDate,
340 $isLocalTime,
341 $paramTZid
342 ) {
343 ParameterFactory::ifExistRemove( // remove default
344 $params,
345 Vcalendar::VALUE,
346 Vcalendar::DATE_TIME
347 );
348 switch( true ) {
349 case ( $isValueDate ) :
350 ParameterFactory::ifExistRemove( $params, Vcalendar::TZID );
351 ParameterFactory::ifExistRemove( $params, Util::$ISLOCALTIME );
352 break;
353 case ( $isLocalTime ) :
354 ParameterFactory::ifExistRemove( $params, Vcalendar::TZID );
355 $params[Util::$ISLOCALTIME] = true;
356 break;
357 case ( ! empty( $paramTZid ) &&
358 ! DateTimeZoneFactory::isUTCtimeZone( $paramTZid )) :
359 $params[Vcalendar::TZID] = $paramTZid;
360 break;
361 default :
362 ParameterFactory::ifExistRemove( $params, Vcalendar::TZID );
363 break;
364 } // end switch
365 }
366
367 /**
368 * Return array [<datePart>, <timezonePart>] from (split) string
369 *
370 * @param string $string
371 * @return array [<datePart>, <timezonePart>]
372 * @static
373 * @since 2.27.14 - 2019-03-08
374 */
375 public static function splitIntoDateStrAndTimezone( $string )
376 {
377 $string = trim((string) $string );
378 if(( DateTimeZoneFactory::$UTCARR[0] == substr( $string, -1 )) &&
379 ( ctype_digit( substr( $string, -3, 2 )))) { // nnZ
380 return [ substr( $string, 0, -1 ), DateTimeZoneFactory::$UTCARR[1] ]; // UTC
381 }
382 $strLen = strlen( $string );
383 if( self::isDateTimeStrInIcal( $string )) {
384 $icalDateTimeString = substr( $string, 0, 15 );
385 if(( DateTimeZoneFactory::$UTCARR[0] ==
386 substr( $string, 15, 1 )) && ( 16 == $strLen )) {
387 return [ $icalDateTimeString, Vcalendar::UTC ]; // 'Z'
388 }
389 if( 15 == $strLen ) {
390 return [ $string, null ];
391 }
392 }
393 elseif( ctype_digit( $string ) && ( 9 > $strLen )) { // ex. YYYYmmdd
394 return [ $string, null ];
395 }
396 if( DateTimeZoneFactory::hasOffset( $string )) {
397 $tz = DateTimeZoneFactory::getOffset( $string );
398 $string2 = trim( substr( $string, 0, 0 - strlen( $tz )));
399 if( Vcalendar::GMT == substr( $string2, -3 )) {
400 $string2 = trim( substr( $string2, 0, -3 ));
401 }
402 $tz = DateTimeZoneFactory::getTimeZoneNameFromOffset( $tz );
403 return [ $string2, $tz ];
404 } // end if
405 if( false !== strrpos( $string, Util::$SP1 )) {
406 $tz = StringFactory::afterLast( Util::$SP1, $string );
407 $string2 = StringFactory::beforeLast( Util::$SP1, $string );
408 if( DateTimeZoneFactory::isUTCtimeZone( $tz )) {
409 $tz = Vcalendar::UTC;
410 }
411 $found = true;
412 try {
413 DateTimeZoneFactory::assertDateTimeZone( $tz );
414 }
415 catch( InvalidArgumentException $e ) {
416 $found = false;
417 }
418 if( $found ) {
419 return [ $string2, $tz ];
420 }
421 } // end if
422 return [ $string, null ];
423 }
424
425 /**
426 * Return DateTime with the right timezone set
427 *
428 * @param string $dateStr
429 * @param string $timezonePart
430 * @param string $paramTZid
431 * @param bool $forceUTC
432 * @return DateTime
433 * @throws Exception
434 * @throws InvalidArgumentException
435 * @static
436 * @since 2.27.8 - 2019-01-14
437 */
438 public static function getDateTimeWithTimezoneFromString(
439 $dateStr,
440 $timezonePart = null,
441 $paramTZid = null,
442 $forceUTC = false
443 ) {
444 $tz2 = null;
445 switch( true ) {
446 case ( empty( $timezonePart ) && ! empty( $paramTZid )) :
447 $tz = $paramTZid;
448 break;
449 case ( empty( $timezonePart )) :
450 $tz = date_default_timezone_get(); // local time
451 break;
452 case ( ! empty( $paramTZid )) :
453 $tz = $timezonePart;
454 if( ! $forceUTC ) {
455 $tz2 = $paramTZid;
456 }
457 break;
458 default :
459 $tz = $timezonePart;
460 break;
461 } // end switch
462 $dateTime = self::getDateTimeFromDateString( $dateStr, $tz );
463 if( ! empty( $tz2 )) {
464 $dateTime = self::setDateTimeTimeZone( $dateTime, $tz2 );
465 }
466 return $dateTime;
467 }
468
469 /**
470 * Return string formatted DateTime, if offset then set timezone UTC
471 *
472 * @param DateTimeInterface $dateTime
473 * @param bool $isDATE
474 * @param bool $isLocalTime
475 * @return string
476 * @throws Exception
477 * @throws InvalidArgumentException
478 * @static
479 * @since 2.29.21 - 2020-01-31
480 * @usedby RexdateFactory::getPeriod()/prepInputRdate() + <dateProp>::get<dateProp>()
481 */
482 public static function dateTime2Str(
483 $dateTime,
484 $isDATE = false,
485 $isLocalTime = false
486 ) {
487 $dateTime = self::cnvrtDateTimeInterface( $dateTime );
488 if( self::dateTimeHasOffset( $dateTime )) {
489 $dateTime = self::setDateTimeTimeZone(
490 $dateTime,
491 $dateTime->getTimezone()->getName()
492 );
493 }
494 $fmt = $isDATE ? self::$Ymd : self::$YmdTHis;
495 $output = $dateTime->format( $fmt );
496 if( ! $isDATE && ! $isLocalTime &&
497 DateTimeZoneFactory::isUTCtimeZone( $dateTime->getTimezone()->getName())) {
498 $output .= DateTimeZoneFactory::$UTCARR[0];
499 }
500 return $output;
501 }
502
503 /**
504 * Return bool true if datetime har offset timezone
505 *
506 * @param DateTime $datetime
507 * @return bool
508 * @static
509 * @since 2.27.19 - 2019-04-09
510 */
511 public static function dateTimeHasOffset( DateTime $datetime )
512 {
513 return DateTimeZoneFactory::hasOffset( $datetime->getTimezone()->getName());
514 }
515
516 /*
517 * Return bool true if date(times) are in sequence
518 *
519 * @param DateTime $first
520 * @param DateTime $second
521 * @param string $propName
522 * @return bool
523 * @static
524 * @throws InvalidArgumentException
525 * @since 2.27.14 - 2019-02-03
526 */
527 public static function assertDatesAreInSequence(
528 DateTime $first,
529 DateTime $second,
530 $propName
531 ) {
532 static $ERR = '%s, dates are not in (asc) order (%s < _%s_)';
533 if( $first->getTimestamp() > $second->getTimestamp()) {
534 throw new InvalidArgumentException(
535 sprintf(
536 $ERR,
537 $propName,
538 $first->format( self::$YmdTHis ),
539 $second->format( self::$YmdTHis )
540 )
541 );
542 }
543 }
544
545 /*
546 * Return DateTime from string date, opt. with other timezone
547 *
548 * @param string $dateString
549 * @param string $tz
550 * @return DateTime
551 * @throws Exception
552 * @throws InvalidArgumentException
553 * @access private
554 * @static
555 * @since 2.27.8 - 2019-01-12
556 */
557 private static function getDateTimeFromDateString( $dateString, $tz = null )
558 {
559 $tz = trim( $tz );
560 switch( true ) {
561 case ( empty( $tz )) :
562 break;
563 case ( DateTimeZoneFactory::isUTCtimeZone( $tz )) :
564 $tz = Vcalendar::UTC;
565 break;
566 case ( DateTimeZoneFactory::hasOffset( $tz )) :
567 $tz = DateTimeZoneFactory::getTimeZoneNameFromOffset( $tz );
568 break;
569 } // end switch
570 try {
571 $dateTime = self::factory( $dateString, $tz );
572 }
573 catch( InvalidArgumentException $ie ) {
574 throw $ie;
575 }
576 catch( Exception $e ) {
577 throw $e;
578 }
579 return $dateTime;
580 }
581
582 /*
583 * Return DateTime modified from (ext) timezone
584 *
585 * @param DateTimeInterface $dateTime
586 * @param string $tz
587 * @return DateTime
588 * @throws Exception
589 * @throws InvalidArgumentException
590 * @static
591 * @since 2.27.14 - 2019-02-04
592 */
593 public static function setDateTimeTimeZone( DateTimeInterface $dateTime, $tz )
594 {
595 $dateTime = self::cnvrtDateTimeInterface( $dateTime );
596 if( empty( $tz )) {
597 return $dateTime;
598 }
599 if( DateTimeZoneFactory::hasOffset( $tz )) {
600 $tz = DateTimeZoneFactory::getTimeZoneNameFromOffset( $tz );
601 }
602 $currTz = $dateTime->getTimezone()->getName();
603 if( DateTimeZoneFactory::isUTCtimeZone( $currTz ) &&
604 DateTimeZoneFactory::isUTCtimeZone( $tz )) {
605 return $dateTime;
606 }
607 if( 0 == strcasecmp( $currTz, $tz )) { // same
608 return $dateTime;
609 }
610 try {
611 $tzt = DateTimeZoneFactory::factory( $tz );
612 }
613 catch( Exception $e ) {
614 throw new InvalidArgumentException(
615 sprintf( self::$ERR4, $dateTime->format( self::$YMDHISe ), $tz ),
616 null,
617 $e
618 );
619 }
620 if( false === $dateTime->setTimezone( $tzt )) {
621 throw new InvalidArgumentException(
622 sprintf( self::$ERR4, $dateTime->format( self::$YMDHISe ), $tz )
623 );
624 }
625 return $dateTime;
626 }
627
628 /*
629 * Return bool true if string contains a valid date
630 *
631 * @param mixed $str
632 * @return bool
633 * @static
634 * @since 2.27.14 - 2019-02-17
635 */
636 public static function isStringAndDate( $string )
637 {
638 if( ! is_string( $string )) {
639 return false;
640 }
641 $string = trim( $string );
642 return (( 8 <= strlen( $string )) &&
643 ( false !== strtotime ( $string )));
644 }
645
646 /*
647 * Return bool true if dateStr starts with format YYYYmmdd[T/t]HHmmss
648 *
649 * @param string $dateStr
650 * @return bool
651 * @access private
652 * @static
653 * @since 2.27.8 - 2019-01-12
654 */
655 private static function isDateTimeStrInIcal( $dateStr )
656 {
657 static $Tarr = ['T','t'];
658 return ( is_string( $dateStr) &&
659 ctype_digit( substr( $dateStr, 0, 8 )) &&
660 in_array( substr( $dateStr, 8, 1 ), $Tarr ) &&
661 ctype_digit( substr( $dateStr, 9, 6 )));
662 }
663 }
664
665