| 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 |
use LogicException; |
| 39 |
use function array_change_key_case; |
| 40 |
use function array_keys; |
| 41 |
use function array_unique; |
| 42 |
use function checkdate; |
| 43 |
use function count; |
| 44 |
use function ctype_alpha; |
| 45 |
use function ctype_digit; |
| 46 |
use function date; |
| 47 |
use function end; |
| 48 |
use function explode; |
| 49 |
use function get_class; |
| 50 |
use function implode; |
| 51 |
use function in_array; |
| 52 |
use function is_array; |
| 53 |
use function is_null; |
| 54 |
use function is_string; |
| 55 |
use function ksort; |
| 56 |
use function mktime; |
| 57 |
use function sprintf; |
| 58 |
use function strcasecmp; |
| 59 |
use function strlen; |
| 60 |
use function strtoupper; |
| 61 |
use function substr; |
| 62 |
use function trim; |
| 63 |
use function usort; |
| 64 |
use function var_export; |
| 65 |
|
| 66 |
/** |
| 67 |
* iCalcreator recur support class |
| 68 |
* |
| 69 |
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se> |
| 70 |
* @since 2.29.27 - 2020-09-19 |
| 71 |
*/ |
| 72 |
class RecurFactory |
| 73 |
{ |
| 74 |
/** |
| 75 |
* @const int in recur2date, years to extend startYear to create an endDate, if missing |
| 76 |
*/ |
| 77 |
const EXTENDYEAR = 2; |
| 78 |
|
| 79 |
/** |
| 80 |
* @var string iCal date/time key values ( week, tz used in test) |
| 81 |
*/ |
| 82 |
public static $LCYEAR = 'year'; |
| 83 |
public static $LCMONTH = 'month'; |
| 84 |
public static $LCWEEK = 'week'; |
| 85 |
public static $LCDAY = 'day'; |
| 86 |
public static $LCHOUR = 'hour'; |
| 87 |
public static $LCMIN = 'min'; |
| 88 |
public static $LCSEC = 'sec'; |
| 89 |
public static $LCtz = 'tz'; |
| 90 |
|
| 91 |
/** |
| 92 |
* Static values for recur BYDAY |
| 93 |
* |
| 94 |
* @var array |
| 95 |
*/ |
| 96 |
public static $DAYNAMES = [ |
| 97 |
Vcalendar::SU, |
| 98 |
Vcalendar::MO, |
| 99 |
Vcalendar::TU, |
| 100 |
Vcalendar::WE, |
| 101 |
Vcalendar::TH, |
| 102 |
Vcalendar::FR, |
| 103 |
Vcalendar::SA |
| 104 |
]; |
| 105 |
|
| 106 |
/* |
| 107 |
* @var string DateTime format keys |
| 108 |
*/ |
| 109 |
public static $YMDs = '%04d%02d%02d'; |
| 110 |
public static $HIS = '%02d%02d%02d'; |
| 111 |
|
| 112 |
/* |
| 113 |
* @var string fullRecur2date keys |
| 114 |
*/ |
| 115 |
private static $YEARCNT_UP = 'yearcnt_up'; |
| 116 |
private static $YEARCNT_DOWN = 'yearcnt_down'; |
| 117 |
private static $MONTHDAYNO_UP = 'monthdayno_up'; |
| 118 |
private static $MONTHDAYNO_DOWN = 'monthdayno_down'; |
| 119 |
private static $MONTHCNT_DOWN = 'monthcnt_down'; |
| 120 |
private static $YEARDAYNO_UP = 'yeardayno_up'; |
| 121 |
private static $YEARDAYNO_DOWN = 'yeardayno_down'; |
| 122 |
private static $WEEKNO_UP = 'weekno_up'; |
| 123 |
private static $WEEKNO_DOWN = 'weekno_down'; |
| 124 |
|
| 125 |
/** |
| 126 |
* Sort recur dates |
| 127 |
* |
| 128 |
* @param string $byDayA |
| 129 |
* @param string $byDayB |
| 130 |
* @return int |
| 131 |
*/ |
| 132 |
private static function recurBydaySort( $byDayA, $byDayB ) |
| 133 |
{ |
| 134 |
static $days = [ |
| 135 |
Vcalendar::SU => 0, |
| 136 |
Vcalendar::MO => 1, |
| 137 |
Vcalendar::TU => 2, |
| 138 |
Vcalendar::WE => 3, |
| 139 |
Vcalendar::TH => 4, |
| 140 |
Vcalendar::FR => 5, |
| 141 |
Vcalendar::SA => 6, |
| 142 |
]; |
| 143 |
return ( $days[substr( $byDayA, -2 )] < $days[substr( $byDayB, -2 )] ) |
| 144 |
? -1 |
| 145 |
: 1; |
| 146 |
} |
| 147 |
|
| 148 |
/** |
| 149 |
* Return formatted output for calendar component property data value type recur |
| 150 |
* |
| 151 |
* "The value of the UNTIL rule part MUST have the same value type as the "DTSTART" property. |
| 152 |
* Furthermore, if the "DTSTART" property is specified as a date with local time, |
| 153 |
* then the UNTIL rule part MUST also be specified as a date with local time. |
| 154 |
* If the "DTSTART" property is specified as a date |
| 155 |
* with UTC time |
| 156 |
* or |
| 157 |
* a date with local time and time zone reference, |
| 158 |
* then the UNTIL rule part MUST be specified as a date with UTC time. |
| 159 |
* In the case of the "STANDARD" and "DAYLIGHT" sub-components |
| 160 |
* the UNTIL rule part MUST always be specified as a date with UTC time. |
| 161 |
* If specified as a DATE-TIME value, then it MUST be specified in a UTC time format." |
| 162 |
* @param string $recurProperty |
| 163 |
* @param array $recurData |
| 164 |
* @param bool $allowEmpty |
| 165 |
* @return string |
| 166 |
* @throws Exception |
| 167 |
* @throws InvalidArgumentException |
| 168 |
* @since 2.29.6 2019-06-23 |
| 169 |
* @todo above |
| 170 |
*/ |
| 171 |
public static function formatRecur( $recurProperty, $recurData, $allowEmpty ) |
| 172 |
{ |
| 173 |
static $FMTFREQEQ = 'FREQ=%s'; |
| 174 |
static $FMTDEFAULTEQ = ';%s=%s'; |
| 175 |
static $FMTOTHEREQ = ';%s='; |
| 176 |
static $RECURBYDAYSORTER = null; |
| 177 |
if( is_null( $RECURBYDAYSORTER )) { |
| 178 |
$RECURBYDAYSORTER = [ get_class(), 'recurBydaySort' ]; |
| 179 |
} |
| 180 |
if( empty( $recurData )) { |
| 181 |
return null; |
| 182 |
} |
| 183 |
$output = null; |
| 184 |
if( empty( $recurData[Util::$LCvalue] )) { |
| 185 |
return ( $allowEmpty ) |
| 186 |
? StringFactory::createElement( $recurProperty ) |
| 187 |
: null; |
| 188 |
} |
| 189 |
$isValueDate = ParameterFactory::isParamsValueSet( $recurData, Vcalendar::DATE ); |
| 190 |
if( isset( $recurData[Util::$LCparams] )) { |
| 191 |
ParameterFactory::ifExistRemove( |
| 192 |
$recurData[Util::$LCparams], |
| 193 |
Vcalendar::VALUE |
| 194 |
); |
| 195 |
$attributes = ParameterFactory::createParams( $recurData[Util::$LCparams] ); |
| 196 |
} |
| 197 |
else { |
| 198 |
$attributes = null; |
| 199 |
} |
| 200 |
$content1 = $content2 = null; |
| 201 |
foreach( $recurData[Util::$LCvalue] as $ruleLabel => $ruleValue ) { |
| 202 |
$ruleLabel = strtoupper( $ruleLabel ); |
| 203 |
switch( $ruleLabel ) { |
| 204 |
case Vcalendar::FREQ : |
| 205 |
$content1 .= sprintf( $FMTFREQEQ, $ruleValue ); |
| 206 |
break; |
| 207 |
case Vcalendar::UNTIL : |
| 208 |
$content2 .= sprintf( |
| 209 |
$FMTDEFAULTEQ, |
| 210 |
Vcalendar::UNTIL, |
| 211 |
DateTimeFactory::dateTime2Str( $ruleValue, $isValueDate ) |
| 212 |
); |
| 213 |
break; |
| 214 |
case Vcalendar::COUNT : |
| 215 |
case Vcalendar::INTERVAL : |
| 216 |
case Vcalendar::WKST : |
| 217 |
$content2 .= sprintf( $FMTDEFAULTEQ, $ruleLabel, $ruleValue ); |
| 218 |
break; |
| 219 |
case Vcalendar::BYDAY : |
| 220 |
$byday = [ Util::$SP0 ]; |
| 221 |
$bx = 0; |
| 222 |
foreach( $ruleValue as $bix => $bydayPart ) { |
| 223 |
if( ! empty( $byday[$bx] ) && // new day |
| 224 |
! ctype_digit( substr( $byday[$bx], -1 ))) { |
| 225 |
$byday[++$bx] = Util::$SP0; |
| 226 |
} |
| 227 |
if( ! is_array( $bydayPart )) { // day without rel pos number |
| 228 |
$byday[$bx] .= (string) $bydayPart; |
| 229 |
} |
| 230 |
else { // day with rel pos number |
| 231 |
foreach( $bydayPart as $bix2 => $bydayPart2 ) { |
| 232 |
$byday[$bx] .= (string) $bydayPart2; |
| 233 |
} |
| 234 |
} |
| 235 |
} // end foreach( $ruleValue as $bix => $bydayPart ) |
| 236 |
if( 1 < count( $byday )) { |
| 237 |
usort( $byday, $RECURBYDAYSORTER ); |
| 238 |
} |
| 239 |
$content2 .= sprintf( |
| 240 |
$FMTDEFAULTEQ, |
| 241 |
Vcalendar::BYDAY, |
| 242 |
implode( Util::$COMMA, $byday ) |
| 243 |
); |
| 244 |
break; |
| 245 |
default : // BYSECOND/BYMINUTE/BYHOUR/BYMONTHDAY/BYYEARDAY/BYWEEKNO/BYMONTH/BYSETPOS... |
| 246 |
if( is_array( $ruleValue )) { |
| 247 |
$content2 .= sprintf( $FMTOTHEREQ, $ruleLabel ); |
| 248 |
$content2 .= implode( Util::$COMMA, $ruleValue ); |
| 249 |
} |
| 250 |
else { |
| 251 |
$content2 .= sprintf( $FMTDEFAULTEQ, $ruleLabel, $ruleValue ); |
| 252 |
} |
| 253 |
break; |
| 254 |
} // end switch( $ruleLabel ) |
| 255 |
} // end foreach( $theRule[Util::$LCvalue] )) as $ruleLabel => $ruleValue ) |
| 256 |
$output .= StringFactory::createElement( |
| 257 |
$recurProperty, |
| 258 |
$attributes, |
| 259 |
$content1 . $content2 |
| 260 |
); |
| 261 |
return $output; |
| 262 |
} |
| 263 |
|
| 264 |
/** |
| 265 |
* Return (array) parsed rexrule string |
| 266 |
* |
| 267 |
* @param string $row |
| 268 |
* @return array |
| 269 |
* @since 2.27.3 - 2018-12-28 |
| 270 |
*/ |
| 271 |
public static function parseRexrule( $row ) |
| 272 |
{ |
| 273 |
static $EQ = '='; |
| 274 |
$recur = []; |
| 275 |
$values = explode( Util::$SEMIC, $row ); |
| 276 |
foreach( $values as $value2 ) { |
| 277 |
if( empty( $value2 )) { |
| 278 |
continue; |
| 279 |
} // ;-char in end position ??? |
| 280 |
$value3 = explode( $EQ, $value2, 2 ); |
| 281 |
$ruleLabel = strtoupper( $value3[0] ); |
| 282 |
switch( $ruleLabel ) { |
| 283 |
case Vcalendar::BYDAY: |
| 284 |
$value4 = explode( Util::$COMMA, $value3[1] ); |
| 285 |
if( 1 < count( $value4 )) { |
| 286 |
foreach( $value4 as $v5ix => $value5 ) { |
| 287 |
$value4[$v5ix] = |
| 288 |
self::updateDayNoAndDayName( trim((string) $value5 )); |
| 289 |
} |
| 290 |
} |
| 291 |
else { |
| 292 |
$value4 = self::updateDayNoAndDayName( |
| 293 |
trim((string) $value3[1] ) |
| 294 |
); |
| 295 |
} |
| 296 |
$recur[$ruleLabel] = $value4; |
| 297 |
break; |
| 298 |
default: |
| 299 |
$value4 = explode( Util::$COMMA, $value3[1] ); |
| 300 |
if( 1 < count( $value4 )) { |
| 301 |
$value3[1] = $value4; |
| 302 |
} |
| 303 |
$recur[$ruleLabel] = $value3[1]; |
| 304 |
break; |
| 305 |
} // end - switch $ruleLabel |
| 306 |
} // end - foreach( $values.. . |
| 307 |
return $recur; |
| 308 |
} |
| 309 |
|
| 310 |
/* |
| 311 |
* Return array, day rel pos number (opt) and day name abbr |
| 312 |
* |
| 313 |
* @param string $dayValueBase |
| 314 |
* @return array |
| 315 |
* @since 2.27.16 - 2019-03-03 |
| 316 |
*/ |
| 317 |
private static function updateDayNoAndDayName( $dayValueBase ) |
| 318 |
{ |
| 319 |
$output = []; |
| 320 |
$dayno = $dayName = false; |
| 321 |
if(( ctype_alpha( substr( $dayValueBase, -1 ))) && |
| 322 |
( ctype_alpha( substr( $dayValueBase, -2, 1 )))) { |
| 323 |
$dayName = substr( $dayValueBase, -2, 2 ); |
| 324 |
if( 2 < strlen( $dayValueBase )) { |
| 325 |
$dayno = (int) substr( $dayValueBase, 0, ( strlen( $dayValueBase ) - 2 )); |
| 326 |
} |
| 327 |
} |
| 328 |
if( false !== $dayno ) { |
| 329 |
$output[] = $dayno; |
| 330 |
} |
| 331 |
if( false !== $dayName ) { |
| 332 |
$output[Vcalendar::DAY] = $dayName; |
| 333 |
} |
| 334 |
return $output; |
| 335 |
} |
| 336 |
|
| 337 |
/** |
| 338 |
* Convert input format for EXRULE and RRULE to internal format |
| 339 |
* |
| 340 |
* "The value of the UNTIL rule part MUST have the same value type as the "DTSTART" property." |
| 341 |
* "If specified as a DATE-TIME value, then it MUST be specified in a UTC time format." |
| 342 |
* @param array $rexrule |
| 343 |
* @param array $params merged with dtstart params |
| 344 |
* @return array |
| 345 |
* @throws Exception |
| 346 |
* @throws InvalidArgumentException |
| 347 |
* @since 2.29.25 - 2020-09-02 |
| 348 |
* @todo "The BYSECOND, BYMINUTE and BYHOUR rule parts MUST NOT be specified |
| 349 |
* when the associated "DTSTART" property has a DATE value type." |
| 350 |
*/ |
| 351 |
public static function setRexrule( $rexrule, array $params ) |
| 352 |
{ |
| 353 |
static $ERR = 'Invalid input date \'%s\''; |
| 354 |
$input = []; |
| 355 |
if( empty( $rexrule )) { |
| 356 |
return $input; |
| 357 |
} |
| 358 |
$params = [ Util::$LCparams => $params ]; |
| 359 |
$isValueDate = ParameterFactory::isParamsValueSet( $params, Vcalendar::DATE ); |
| 360 |
$paramTZid = ParameterFactory::getParamTzid( $params ); |
| 361 |
$rexrule = array_change_key_case( $rexrule, CASE_UPPER ); |
| 362 |
foreach( $rexrule as $ruleLabel => $ruleValue ) { |
| 363 |
switch( true ) { |
| 364 |
case ( Vcalendar::UNTIL != $ruleLabel ) : |
| 365 |
$input[$ruleLabel] = $ruleValue; |
| 366 |
break; |
| 367 |
case ( $ruleValue instanceof DateTimeInterface ) : |
| 368 |
$ruleValue = DateTimeFactory::cnvrtDateTimeInterface( $ruleValue ); |
| 369 |
$input[$ruleLabel] = |
| 370 |
DateTimeFactory::setDateTimeTimeZone( |
| 371 |
$ruleValue, |
| 372 |
Vcalendar::UTC |
| 373 |
); |
| 374 |
ParameterFactory::ifExistRemove( |
| 375 |
$params[Util::$LCparams], |
| 376 |
Vcalendar::TZID |
| 377 |
); |
| 378 |
break; |
| 379 |
case ( DateTimeFactory::isStringAndDate( $ruleValue )) : |
| 380 |
list( $dateStr, $timezonePart ) = |
| 381 |
DateTimeFactory::splitIntoDateStrAndTimezone( $ruleValue ); |
| 382 |
$isLocalTime = ( empty( $timezonePart ) && empty( $paramTZid )); |
| 383 |
$dateTime = DateTimeFactory::getDateTimeWithTimezoneFromString( |
| 384 |
$dateStr, |
| 385 |
$isLocalTime ? null : $timezonePart, |
| 386 |
$isLocalTime ? Vcalendar::UTC : $paramTZid, |
| 387 |
true |
| 388 |
); |
| 389 |
if( $isValueDate ) { |
| 390 |
ParameterFactory::ifExistRemove( |
| 391 |
$params[Util::$LCparams], |
| 392 |
Vcalendar::TZID |
| 393 |
); |
| 394 |
} |
| 395 |
else { |
| 396 |
$dateTime = DateTimeFactory::setDateTimeTimeZone( |
| 397 |
$dateTime, |
| 398 |
Vcalendar::UTC |
| 399 |
); |
| 400 |
ParameterFactory::ifExistRemove( |
| 401 |
$params[Util::$LCparams], |
| 402 |
Vcalendar::TZID |
| 403 |
); |
| 404 |
} |
| 405 |
$input[$ruleLabel] = $dateTime; |
| 406 |
break; |
| 407 |
default : |
| 408 |
throw new InvalidArgumentException( |
| 409 |
sprintf( $ERR, var_export( $ruleValue, true )) |
| 410 |
); |
| 411 |
break; |
| 412 |
} // end switch |
| 413 |
} // end foreach( $rexrule as $ruleLabel => $ruleValue ) |
| 414 |
$output = self::orderRRuleKeys( $input ); |
| 415 |
|
| 416 |
if( ! isset( $output[Vcalendar::UNTIL] )) { |
| 417 |
ParameterFactory::ifExistRemove( |
| 418 |
$params[Util::$LCparams], |
| 419 |
Vcalendar::TZID |
| 420 |
); |
| 421 |
} |
| 422 |
try { |
| 423 |
RecurFactory2::assertRecur( $output ); |
| 424 |
} |
| 425 |
catch( LogicException $e ) { |
| 426 |
throw new InvalidArgumentException( $e->getMessage(), null, $e ); |
| 427 |
} |
| 428 |
return [ Util::$LCvalue => $output ] + $params; |
| 429 |
} |
| 430 |
|
| 431 |
/** |
| 432 |
* @param array $input |
| 433 |
* @return array |
| 434 |
* @since 2.29.25 - 2020-09-02 |
| 435 |
*/ |
| 436 |
private static function orderRRuleKeys( array $input ) |
| 437 |
{ |
| 438 |
static $RKEYS1 = [ |
| 439 |
Vcalendar::FREQ, |
| 440 |
Vcalendar::UNTIL, |
| 441 |
Vcalendar::COUNT, |
| 442 |
Vcalendar::INTERVAL, |
| 443 |
Vcalendar::BYSECOND, |
| 444 |
Vcalendar::BYMINUTE, |
| 445 |
Vcalendar::BYHOUR |
| 446 |
]; |
| 447 |
static $RKEYS2 = [ |
| 448 |
Vcalendar::BYMONTHDAY, |
| 449 |
Vcalendar::BYYEARDAY, |
| 450 |
Vcalendar::BYWEEKNO, |
| 451 |
Vcalendar::BYMONTH, |
| 452 |
Vcalendar::BYSETPOS, |
| 453 |
Vcalendar::WKST |
| 454 |
]; |
| 455 |
static $RKEYS3 = [ |
| 456 |
Vcalendar::BYSECOND, |
| 457 |
Vcalendar::BYMINUTE, |
| 458 |
Vcalendar::BYHOUR, |
| 459 |
Vcalendar::BYMONTHDAY, |
| 460 |
Vcalendar::BYYEARDAY, |
| 461 |
Vcalendar::BYWEEKNO, |
| 462 |
Vcalendar::BYMONTH, |
| 463 |
Vcalendar::BYSETPOS, |
| 464 |
]; |
| 465 |
/* set recurrence rule specification in rfc2445 order */ |
| 466 |
$output = []; |
| 467 |
foreach( $RKEYS1 as $rKey1 ) { |
| 468 |
if( isset( $input[$rKey1] )) { |
| 469 |
$output[$rKey1] = $input[$rKey1]; |
| 470 |
} |
| 471 |
} |
| 472 |
if( isset( $input[Vcalendar::BYDAY] )) { |
| 473 |
self::orderRRuleBydayKey( $input, $output ); |
| 474 |
} |
| 475 |
foreach( $RKEYS2 as $rKey2 ) { |
| 476 |
if( isset( $input[$rKey2] )) { |
| 477 |
$output[$rKey2] = $input[$rKey2]; |
| 478 |
} |
| 479 |
} |
| 480 |
foreach( $RKEYS3 as $rKey3 ) { |
| 481 |
if( ! isset( $output[$rKey3] )) { |
| 482 |
continue; |
| 483 |
} |
| 484 |
if( is_string( $output[$rKey3] )) { |
| 485 |
$temp = explode( UTIL::$COMMA, $output[$rKey3] ); |
| 486 |
if( 1 == count( $temp )) { |
| 487 |
$output[$rKey3] = reset( $temp ); |
| 488 |
} |
| 489 |
else { |
| 490 |
sort( $temp ); |
| 491 |
$output[$rKey3] = array_unique( $temp ); |
| 492 |
} |
| 493 |
} |
| 494 |
elseif( is_array( $output[$rKey3] )) { |
| 495 |
sort( $output[$rKey3] ); |
| 496 |
$output[$rKey3] = array_unique( $output[$rKey3] ); |
| 497 |
} |
| 498 |
} // end foreach |
| 499 |
return $output; |
| 500 |
} |
| 501 |
|
| 502 |
/** |
| 503 |
* Ensure RRULE BYDAY array and upper case.. . |
| 504 |
* |
| 505 |
* @param array $input |
| 506 |
* @param array $output |
| 507 |
* @since 2.29.27 - 2020-09-19 |
| 508 |
*/ |
| 509 |
private static function orderRRuleBydayKey( array $input, array & $output ) |
| 510 |
{ |
| 511 |
if( empty( $input[Vcalendar::BYDAY] )) { |
| 512 |
// results in error |
| 513 |
$output[Vcalendar::BYDAY] = []; |
| 514 |
return; |
| 515 |
} |
| 516 |
if( ! is_array( $input[Vcalendar::BYDAY] )) { |
| 517 |
// single day |
| 518 |
$output[Vcalendar::BYDAY] = [ |
| 519 |
Vcalendar::DAY => strtoupper( $input[Vcalendar::BYDAY] ), |
| 520 |
]; |
| 521 |
return; |
| 522 |
} |
| 523 |
$cntStr = $cntNum = 0; |
| 524 |
foreach( $input[Vcalendar::BYDAY] as $BYDAYx => $BYDAYv ) { |
| 525 |
if( is_array( $BYDAYv )) { |
| 526 |
break; |
| 527 |
} |
| 528 |
if( is_string( $BYDAYv ) && ctype_alpha( $BYDAYv )) { |
| 529 |
$cntStr += 1; |
| 530 |
continue; |
| 531 |
} |
| 532 |
if( empty( $BYDAYv )) { |
| 533 |
$input[Vcalendar::BYDAY] = [ Util::$SP0 ]; |
| 534 |
$cntStr += 1; |
| 535 |
continue; |
| 536 |
} |
| 537 |
$cntNum += 1; |
| 538 |
} // end foreach |
| 539 |
if(( 1 == $cntStr ) || ( 1 < $cntNum )) { // single day OR invalid format... |
| 540 |
$input[Vcalendar::BYDAY] = [ $input[Vcalendar::BYDAY] ]; |
| 541 |
} |
| 542 |
elseif( 1 < $cntStr ) { // split (single) days |
| 543 |
$days = []; |
| 544 |
foreach( $input[Vcalendar::BYDAY] as $BYDAYx => $BYDAYv ) { |
| 545 |
$days[] = [ Vcalendar::DAY => $BYDAYv ]; |
| 546 |
} |
| 547 |
$input[Vcalendar::BYDAY] = $days; |
| 548 |
} |
| 549 |
foreach( $input[Vcalendar::BYDAY] as $BYDAYx => $BYDAYv ) { |
| 550 |
$nIx = 0; |
| 551 |
foreach( $BYDAYv as $BYDAYx2 => $BYDAYv2 ) { |
| 552 |
switch( true ) { |
| 553 |
case ( 0 == strcasecmp( Vcalendar::DAY, $BYDAYx2 )) : |
| 554 |
// day abbr with key |
| 555 |
$output[Vcalendar::BYDAY][$BYDAYx][$BYDAYx2] = strtoupper( $BYDAYv2 ); |
| 556 |
break; |
| 557 |
case ( is_string( $BYDAYv2 ) && ctype_alpha( $BYDAYv2 )) : |
| 558 |
// day abbr without key, set key |
| 559 |
$output[Vcalendar::BYDAY][$BYDAYx][Vcalendar::DAY] = |
| 560 |
strtoupper( $BYDAYv2 ); |
| 561 |
break; |
| 562 |
default : |
| 563 |
// rel pos day number. force key from 0 (1++ results in error) |
| 564 |
$output[Vcalendar::BYDAY][$BYDAYx][$nIx++] = $BYDAYv2; |
| 565 |
break; |
| 566 |
} // end switch |
| 567 |
} // end foreach |
| 568 |
ksort( $output[Vcalendar::BYDAY][$BYDAYx], SORT_NATURAL ); |
| 569 |
} // end foreach |
| 570 |
ksort( $output[Vcalendar::BYDAY], SORT_NATURAL ); |
| 571 |
} |
| 572 |
|
| 573 |
/** |
| 574 |
* Update array $result with dates based on a recur pattern |
| 575 |
* |
| 576 |
* If missing, UNTIL is set 1 year from startdate (emergency break) |
| 577 |
* |
| 578 |
* @param array $result array to update, array([Y-m-d] => bool) |
| 579 |
* @param array $recur pattern for recurrency (only value part, params ignored) |
| 580 |
* @param string|DateTime $wDateIn component start date |
| 581 |
* @param string|DateTime $fcnStartIn start date |
| 582 |
* @param string|DateTime $fcnEndIn end date |
| 583 |
* @throws Exception |
| 584 |
* @since 2.29.24 - 2020-08-29 |
| 585 |
* @todo BYHOUR, BYMINUTE, BYSECOND, WEEKLY at year end/start OR not at all |
| 586 |
*/ |
| 587 |
public static function recur2date( |
| 588 |
& $result, |
| 589 |
$recur, |
| 590 |
$wDateIn, |
| 591 |
$fcnStartIn, |
| 592 |
$fcnEndIn = null |
| 593 |
) { |
| 594 |
if( ! isset( $recur[Vcalendar::FREQ] )) { // "MUST be specified.. ." ?? |
| 595 |
$recur[Vcalendar::FREQ] = Vcalendar::DAILY; |
| 596 |
} |
| 597 |
if( ! isset( $recur[Vcalendar::INTERVAL] )) { |
| 598 |
$recur[Vcalendar::INTERVAL] = 1; |
| 599 |
} |
| 600 |
switch( true ) { |
| 601 |
case RecurFactory2::isRecurDaily1( $recur ) : |
| 602 |
$result = $result + |
| 603 |
RecurFactory2::recurDaily1( |
| 604 |
$recur, |
| 605 |
$wDateIn, |
| 606 |
$fcnStartIn, |
| 607 |
$fcnEndIn |
| 608 |
); |
| 609 |
ksort( $result, SORT_NUMERIC ); |
| 610 |
break; |
| 611 |
case RecurFactory2::isRecurDaily2( $recur ) : |
| 612 |
$result = $result + |
| 613 |
RecurFactory2::recurDaily2( |
| 614 |
$recur, |
| 615 |
$wDateIn, |
| 616 |
$fcnStartIn, |
| 617 |
$fcnEndIn |
| 618 |
); |
| 619 |
ksort( $result, SORT_NUMERIC ); |
| 620 |
break; |
| 621 |
case RecurFactory2::isRecurMonthly1( $recur ) : |
| 622 |
$result = $result + |
| 623 |
RecurFactory2::recurMonthly1( |
| 624 |
$recur, |
| 625 |
$wDateIn, |
| 626 |
$fcnStartIn, |
| 627 |
$fcnEndIn |
| 628 |
); |
| 629 |
ksort( $result, SORT_NUMERIC ); |
| 630 |
break; |
| 631 |
case RecurFactory2::isRecurMonthly2( $recur ) : |
| 632 |
$result = $result + |
| 633 |
RecurFactory2::recurMonthlyYearly3( |
| 634 |
$recur, |
| 635 |
$wDateIn, |
| 636 |
$fcnStartIn, |
| 637 |
$fcnEndIn |
| 638 |
); |
| 639 |
ksort( $result, SORT_NUMERIC ); |
| 640 |
break; |
| 641 |
case RecurFactory2::isRecurWeekly1( $recur ) : |
| 642 |
$result = $result + |
| 643 |
RecurFactory2::recurWeekly1( |
| 644 |
$recur, |
| 645 |
$wDateIn, |
| 646 |
$fcnStartIn, |
| 647 |
$fcnEndIn |
| 648 |
); |
| 649 |
ksort( $result, SORT_NUMERIC ); |
| 650 |
break; |
| 651 |
case RecurFactory2::isRecurWeekly2( $recur ) : |
| 652 |
$result = $result + |
| 653 |
RecurFactory2::recurWeekly2( |
| 654 |
$recur, |
| 655 |
$wDateIn, |
| 656 |
$fcnStartIn, |
| 657 |
$fcnEndIn |
| 658 |
); |
| 659 |
ksort( $result, SORT_NUMERIC ); |
| 660 |
break; |
| 661 |
case RecurFactory2::isRecurYearly1( $recur ) : |
| 662 |
$result = $result + |
| 663 |
RecurFactory2::recurYearly1( |
| 664 |
$recur, |
| 665 |
$wDateIn, |
| 666 |
$fcnStartIn, |
| 667 |
$fcnEndIn |
| 668 |
); |
| 669 |
ksort( $result, SORT_NUMERIC ); |
| 670 |
break; |
| 671 |
case RecurFactory2::isRecurYearly2( $recur ) : |
| 672 |
$result = $result + |
| 673 |
RecurFactory2::recurMonthlyYearly3( |
| 674 |
$recur, |
| 675 |
$wDateIn, |
| 676 |
$fcnStartIn, |
| 677 |
$fcnEndIn |
| 678 |
); |
| 679 |
ksort( $result, SORT_NUMERIC ); |
| 680 |
break; |
| 681 |
default : |
| 682 |
self::fullRecur2date( |
| 683 |
$result, |
| 684 |
$recur, |
| 685 |
$wDateIn, |
| 686 |
$fcnStartIn, |
| 687 |
$fcnEndIn |
| 688 |
); |
| 689 |
} // end switch |
| 690 |
} |
| 691 |
|
| 692 |
/** |
| 693 |
* Update array $result with dates based on a recur pattern |
| 694 |
* |
| 695 |
* If missing, UNTIL is set 1 year from startdate (emergency break) |
| 696 |
* |
| 697 |
* @param array $result array to update, array([Y-m-d] => bool) |
| 698 |
* @param array $recur pattern for recurrency (only value part, params ignored) |
| 699 |
* @param string|DateTime $wDateIn component start date |
| 700 |
* @param string|DateTime $fcnStartIn start date |
| 701 |
* @param string|DateTime $fcnEndIn end date |
| 702 |
* @throws Exception |
| 703 |
* @since 2.26 - 2018-11-10 |
| 704 |
* @todo BYHOUR, BYMINUTE, BYSECOND, WEEKLY at year end/start OR not at all |
| 705 |
*/ |
| 706 |
public static function fullRecur2date( |
| 707 |
& $result, |
| 708 |
$recur, |
| 709 |
$wDateIn, |
| 710 |
$fcnStartIn, |
| 711 |
$fcnEndIn = null |
| 712 |
) { |
| 713 |
static $YEAR2DAYARR = [ 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY' ]; |
| 714 |
if( ! isset( $recur[Vcalendar::FREQ] )) { // "MUST be specified.. ." |
| 715 |
$recur[Vcalendar::FREQ] = Vcalendar::DAILY; |
| 716 |
} // ?? |
| 717 |
if( ! isset( $recur[Vcalendar::INTERVAL] )) { |
| 718 |
$recur[Vcalendar::INTERVAL] = 1; |
| 719 |
} |
| 720 |
$wDate = self::reFormatDate( $wDateIn ); |
| 721 |
$wDateYMD = sprintf( |
| 722 |
self::$YMDs, |
| 723 |
$wDate[self::$LCYEAR], |
| 724 |
$wDate[self::$LCMONTH], |
| 725 |
$wDate[self::$LCDAY] |
| 726 |
); |
| 727 |
$wDateHis = sprintf( |
| 728 |
self::$HIS, |
| 729 |
$wDate[self::$LCHOUR], |
| 730 |
$wDate[self::$LCMIN], |
| 731 |
$wDate[self::$LCSEC] |
| 732 |
); |
| 733 |
$untilHis = $wDateHis; |
| 734 |
$fcnStart = self::reFormatDate( $fcnStartIn ); |
| 735 |
$fcnStartYMD = sprintf( |
| 736 |
self::$YMDs, |
| 737 |
$fcnStart[self::$LCYEAR], |
| 738 |
$fcnStart[self::$LCMONTH], |
| 739 |
$fcnStart[self::$LCDAY] |
| 740 |
); |
| 741 |
if( ! empty( $fcnEndIn )) { |
| 742 |
$fcnEnd = self::reFormatDate( $fcnEndIn ); |
| 743 |
} |
| 744 |
else { |
| 745 |
$fcnEnd = $fcnStart; |
| 746 |
$fcnEnd[self::$LCYEAR] += self::EXTENDYEAR; |
| 747 |
} |
| 748 |
$fcnEndYMD = sprintf( |
| 749 |
self::$YMDs, |
| 750 |
$fcnEnd[self::$LCYEAR], |
| 751 |
$fcnEnd[self::$LCMONTH], |
| 752 |
$fcnEnd[self::$LCDAY] |
| 753 |
); |
| 754 |
if( ! isset( $recur[Vcalendar::COUNT] ) && ! isset( $recur[Vcalendar::UNTIL] )) { |
| 755 |
$recur[Vcalendar::UNTIL] = $fcnEnd; // ?? |
| 756 |
} // create break |
| 757 |
if( isset( $recur[Vcalendar::UNTIL] )) { |
| 758 |
$recur[Vcalendar::UNTIL] = self::reFormatDate( $recur[Vcalendar::UNTIL] ); |
| 759 |
if( $fcnEnd > $recur[Vcalendar::UNTIL] ) { |
| 760 |
$fcnEnd = $recur[Vcalendar::UNTIL]; // emergency break |
| 761 |
$fcnEndYMD = sprintf( |
| 762 |
self::$YMDs, |
| 763 |
$fcnEnd[self::$LCYEAR], |
| 764 |
$fcnEnd[self::$LCMONTH], |
| 765 |
$fcnEnd[self::$LCDAY] |
| 766 |
); |
| 767 |
} |
| 768 |
if( isset( $recur[Vcalendar::UNTIL][self::$LCHOUR] )) { |
| 769 |
$untilHis = sprintf( |
| 770 |
self::$HIS, |
| 771 |
$recur[Vcalendar::UNTIL][self::$LCHOUR], |
| 772 |
$recur[Vcalendar::UNTIL][self::$LCMIN], |
| 773 |
$recur[Vcalendar::UNTIL][self::$LCSEC] |
| 774 |
); |
| 775 |
} |
| 776 |
else { |
| 777 |
$untilHis = sprintf( self::$HIS, 23, 59, 59 ); |
| 778 |
} |
| 779 |
} // end if( isset( $recur[Vcalendar::UNTIL] )) |
| 780 |
if( $wDateYMD > $fcnEndYMD ) { |
| 781 |
return; // nothing to do.. . |
| 782 |
} |
| 783 |
$recurFreqIsYearly = ( Vcalendar::YEARLY == $recur[Vcalendar::FREQ] ); |
| 784 |
$recurFreqIsMonthly = ( Vcalendar::MONTHLY == $recur[Vcalendar::FREQ] ); |
| 785 |
$recurFreqIsWeekly = ( Vcalendar::WEEKLY == $recur[Vcalendar::FREQ] ); |
| 786 |
$recurFreqIsDaily = ( Vcalendar::DAILY == $recur[Vcalendar::FREQ] ); |
| 787 |
$wkst = ( Util::issetKeyAndEquals( $recur, Vcalendar::WKST, Vcalendar::SU )) |
| 788 |
? 24 * 60 * 60 |
| 789 |
: 0; // ?? |
| 790 |
$recurCount = ( ! isset( $recur[Vcalendar::BYSETPOS] )) ? 1 : 0; // DTSTART counts as the first occurrence |
| 791 |
/* find out how to step up dates and set index for interval \count */ |
| 792 |
$step = []; |
| 793 |
if( $recurFreqIsYearly ) { |
| 794 |
$step[self::$LCYEAR] = 1; |
| 795 |
} |
| 796 |
elseif( $recurFreqIsMonthly ) { |
| 797 |
$step[self::$LCMONTH] = 1; |
| 798 |
} |
| 799 |
elseif( $recurFreqIsWeekly ) { |
| 800 |
$step[self::$LCDAY] = 7; |
| 801 |
} |
| 802 |
else { |
| 803 |
$step[self::$LCDAY] = 1; |
| 804 |
} |
| 805 |
if( isset( $step[self::$LCYEAR] ) && isset( $recur[Vcalendar::BYMONTH] )) { |
| 806 |
$step = [ self::$LCMONTH => 1 ]; |
| 807 |
} |
| 808 |
if( empty( $step ) && isset( $recur[Vcalendar::BYWEEKNO] )) { // ?? |
| 809 |
$step = [ self::$LCDAY => 7 ]; |
| 810 |
} |
| 811 |
if( isset( $recur[Vcalendar::BYYEARDAY] ) || |
| 812 |
isset( $recur[Vcalendar::BYMONTHDAY] ) || |
| 813 |
isset( $recur[Vcalendar::BYDAY] )) { |
| 814 |
$step = [ self::$LCDAY => 1 ]; |
| 815 |
} |
| 816 |
$intervalArr = []; |
| 817 |
if( 1 < $recur[Vcalendar::INTERVAL] ) { |
| 818 |
$intervalIx = self::recurIntervalIx( |
| 819 |
$recur[Vcalendar::FREQ], |
| 820 |
$wDate, |
| 821 |
$wkst |
| 822 |
); |
| 823 |
$intervalArr = [ $intervalIx => 0 ]; |
| 824 |
} |
| 825 |
if( isset( $recur[Vcalendar::BYSETPOS] )) { // save start date + weekno |
| 826 |
$bysetPosymd1 = $bysetPosymd2 = $bysetPosw1 = $bysetPosw2 = []; |
| 827 |
if( is_array( $recur[Vcalendar::BYSETPOS] )) { |
| 828 |
foreach( $recur[Vcalendar::BYSETPOS] as $bix => $bval ) { |
| 829 |
$recur[Vcalendar::BYSETPOS][$bix] = (int) $bval; |
| 830 |
} |
| 831 |
} |
| 832 |
else { |
| 833 |
$recur[Vcalendar::BYSETPOS] = [ (int) $recur[Vcalendar::BYSETPOS] ]; |
| 834 |
} |
| 835 |
if( $recurFreqIsYearly ) { |
| 836 |
// start from beginning of year |
| 837 |
$wDate[self::$LCMONTH] = $wDate[self::$LCDAY] = 1; |
| 838 |
$wDateYMD = sprintf( |
| 839 |
self::$YMDs, |
| 840 |
$wDate[self::$LCYEAR], |
| 841 |
$wDate[self::$LCMONTH], |
| 842 |
$wDate[self::$LCDAY] |
| 843 |
); |
| 844 |
// make sure to count last year |
| 845 |
self::stepDate( $fcnEnd, $fcnEndYMD, [ self::$LCYEAR => 1 ] ); |
| 846 |
} |
| 847 |
elseif( $recurFreqIsMonthly ) { |
| 848 |
// start from beginning of month |
| 849 |
$wDate[self::$LCDAY] = 1; |
| 850 |
$wDateYMD = sprintf( |
| 851 |
self::$YMDs, |
| 852 |
$wDate[self::$LCYEAR], |
| 853 |
$wDate[self::$LCMONTH], |
| 854 |
$wDate[self::$LCDAY] |
| 855 |
); |
| 856 |
// make sure to count last month |
| 857 |
self::stepDate( $fcnEnd, $fcnEndYMD, [ self::$LCMONTH => 1 ] ); |
| 858 |
} |
| 859 |
else { |
| 860 |
self::stepDate( $fcnEnd, $fcnEndYMD, $step ); |
| 861 |
} // make sure to \count whole last period |
| 862 |
$bysetPosWold = self::getWeekNumber( |
| 863 |
0, |
| 864 |
0, |
| 865 |
$wkst, |
| 866 |
$wDate[self::$LCMONTH], |
| 867 |
$wDate[self::$LCDAY], |
| 868 |
$wDate[self::$LCYEAR] |
| 869 |
); |
| 870 |
$bysetPosYold = $wDate[self::$LCYEAR]; |
| 871 |
$bysetPosMold = $wDate[self::$LCMONTH]; |
| 872 |
$bysetPosDold = $wDate[self::$LCDAY]; |
| 873 |
} // end if( isset( $recur[Vcalendar::BYSETPOS] )) |
| 874 |
else { |
| 875 |
self::stepDate( $wDate, $wDateYMD, $step ); |
| 876 |
} |
| 877 |
$yearOld = null; |
| 878 |
$dayCnts = []; |
| 879 |
/* MAIN LOOP */ |
| 880 |
while( true ) { |
| 881 |
if( $wDateYMD . $wDateHis > $fcnEndYMD . $untilHis ) { |
| 882 |
break; |
| 883 |
} |
| 884 |
if( isset( $recur[Vcalendar::COUNT] ) && |
| 885 |
( $recurCount >= $recur[Vcalendar::COUNT] )) { |
| 886 |
break; |
| 887 |
} |
| 888 |
if( $yearOld != $wDate[self::$LCYEAR] ) { // $yearOld=null 1:st time |
| 889 |
$yearOld = $wDate[self::$LCYEAR]; |
| 890 |
$dayCnts = self::initDayCnts( $wDate, $recur, $wkst ); |
| 891 |
} |
| 892 |
/* check interval */ |
| 893 |
if( 1 < $recur[Vcalendar::INTERVAL] ) { |
| 894 |
/* create interval index */ |
| 895 |
$intervalIx = self::recurIntervalIx( |
| 896 |
$recur[Vcalendar::FREQ], |
| 897 |
$wDate, |
| 898 |
$wkst |
| 899 |
); |
| 900 |
/* check interval */ |
| 901 |
$currentKey = array_keys( $intervalArr ); |
| 902 |
$currentKey = end( $currentKey ); // get last index |
| 903 |
if( $currentKey != $intervalIx ) { |
| 904 |
$intervalArr = [ $intervalIx => ( $intervalArr[$currentKey] + 1 ) ]; |
| 905 |
} |
| 906 |
if(( $recur[Vcalendar::INTERVAL] != $intervalArr[$intervalIx] ) && |
| 907 |
( 0 != $intervalArr[$intervalIx] )) { |
| 908 |
/* step up date */ |
| 909 |
self::stepDate( $wDate, $wDateYMD, $step ); |
| 910 |
continue; |
| 911 |
} |
| 912 |
else { // continue within the selected interval |
| 913 |
$intervalArr[$intervalIx] = 0; |
| 914 |
} |
| 915 |
} // endif( 1 < $recur['INTERVAL'] ) |
| 916 |
$updateOK = true; |
| 917 |
if( $updateOK && isset( $recur[Vcalendar::BYMONTH] )) { |
| 918 |
$updateOK = self::recurBYcntcheck( |
| 919 |
$recur[Vcalendar::BYMONTH], |
| 920 |
$wDate[self::$LCMONTH], |
| 921 |
( $wDate[self::$LCMONTH] - 13 ) |
| 922 |
); |
| 923 |
} |
| 924 |
if( $updateOK && isset( $recur[Vcalendar::BYWEEKNO] )) { |
| 925 |
$updateOK = self::recurBYcntcheck( |
| 926 |
$recur[Vcalendar::BYWEEKNO], |
| 927 |
$dayCnts[$wDate[self::$LCMONTH]][$wDate[self::$LCDAY]][self::$WEEKNO_UP], |
| 928 |
$dayCnts[$wDate[self::$LCMONTH]][$wDate[self::$LCDAY]][self::$WEEKNO_DOWN] |
| 929 |
); |
| 930 |
} |
| 931 |
if( $updateOK && isset( $recur[Vcalendar::BYYEARDAY] )) { |
| 932 |
$updateOK = self::recurBYcntcheck( |
| 933 |
$recur[Vcalendar::BYYEARDAY], |
| 934 |
$dayCnts[$wDate[self::$LCMONTH]][$wDate[self::$LCDAY]][self::$YEARCNT_UP], |
| 935 |
$dayCnts[$wDate[self::$LCMONTH]][$wDate[self::$LCDAY]][self::$YEARCNT_DOWN] |
| 936 |
); |
| 937 |
} |
| 938 |
if( $updateOK && isset( $recur[Vcalendar::BYMONTHDAY] )) { |
| 939 |
$updateOK = self::recurBYcntcheck( |
| 940 |
$recur[Vcalendar::BYMONTHDAY], |
| 941 |
$wDate[self::$LCDAY], |
| 942 |
$dayCnts[$wDate[self::$LCMONTH]][$wDate[self::$LCDAY]][self::$MONTHCNT_DOWN] |
| 943 |
); |
| 944 |
} |
| 945 |
if( $updateOK && isset( $recur[Vcalendar::BYDAY] )) { |
| 946 |
$updateOK = false; |
| 947 |
$m = $wDate[self::$LCMONTH]; |
| 948 |
$d = $wDate[self::$LCDAY]; |
| 949 |
if( isset( $recur[Vcalendar::BYDAY][Vcalendar::DAY] )) { // single day, opt with year/month day order no |
| 950 |
$dayNumberExists = $dayNumberSw = $dayNameSw = false; |
| 951 |
if( $recur[Vcalendar::BYDAY][Vcalendar::DAY] == |
| 952 |
$dayCnts[$m][$d][Vcalendar::DAY] ) { |
| 953 |
$dayNameSw = true; |
| 954 |
} |
| 955 |
if( isset( $recur[Vcalendar::BYDAY][0] )) { |
| 956 |
$dayNumberExists = true; |
| 957 |
if( $recurFreqIsMonthly || isset( $recur[Vcalendar::BYMONTH] )) { |
| 958 |
$dayNumberSw = self::recurBYcntcheck( |
| 959 |
$recur[Vcalendar::BYDAY][0], |
| 960 |
$dayCnts[$m][$d][self::$MONTHDAYNO_UP], |
| 961 |
$dayCnts[$m][$d][self::$MONTHDAYNO_DOWN] |
| 962 |
); |
| 963 |
} |
| 964 |
elseif( $recurFreqIsYearly ) { |
| 965 |
$dayNumberSw = self::recurBYcntcheck( |
| 966 |
$recur[Vcalendar::BYDAY][0], |
| 967 |
$dayCnts[$m][$d][self::$YEARDAYNO_UP], |
| 968 |
$dayCnts[$m][$d][self::$YEARDAYNO_DOWN] |
| 969 |
); |
| 970 |
} |
| 971 |
} |
| 972 |
if(( $dayNumberExists && $dayNumberSw && $dayNameSw ) || |
| 973 |
( ! $dayNumberExists && ! $dayNumberSw && $dayNameSw )) { |
| 974 |
$updateOK = true; |
| 975 |
} |
| 976 |
} // end if( isset( $recur[Vcalendar::BYDAY][Vcalendar::DAY] )) |
| 977 |
else { // multiple days |
| 978 |
foreach( $recur[Vcalendar::BYDAY] as $byDayValue ) { |
| 979 |
$dayNumberExists = $dayNumberSw = $dayNameSw = false; |
| 980 |
if( isset( $byDayValue[Vcalendar::DAY] ) && |
| 981 |
( $byDayValue[Vcalendar::DAY] == |
| 982 |
$dayCnts[$m][$d][Vcalendar::DAY] )) { |
| 983 |
$dayNameSw = true; |
| 984 |
} |
| 985 |
if( isset( $byDayValue[0] )) { |
| 986 |
$dayNumberExists = true; |
| 987 |
if( $recurFreqIsMonthly || |
| 988 |
isset( $recur[Vcalendar::BYMONTH] )) { |
| 989 |
$dayNumberSw = self::recurBYcntcheck( |
| 990 |
$byDayValue[Util::$ZERO], |
| 991 |
$dayCnts[$m][$d][self::$MONTHDAYNO_UP], |
| 992 |
$dayCnts[$m][$d][self::$MONTHDAYNO_DOWN] |
| 993 |
); |
| 994 |
} |
| 995 |
elseif( $recurFreqIsYearly ) { |
| 996 |
$dayNumberSw = self::recurBYcntcheck( |
| 997 |
$byDayValue[Util::$ZERO], |
| 998 |
$dayCnts[$m][$d][self::$YEARDAYNO_UP], |
| 999 |
$dayCnts[$m][$d][self::$YEARDAYNO_DOWN] |
| 1000 |
); |
| 1001 |
} |
| 1002 |
} // end if( isset( $byDayValue[0] )) |
| 1003 |
if(( $dayNumberExists && $dayNumberSw && $dayNameSw ) || |
| 1004 |
( ! $dayNumberExists && ! $dayNumberSw && $dayNameSw )) { |
| 1005 |
$updateOK = true; |
| 1006 |
break; |
| 1007 |
} |
| 1008 |
} // end foreach( $recur[Vcalendar::BYDAY] as $byDayValue ) |
| 1009 |
} // end else |
| 1010 |
} // end if( $updateOK && isset( $recur[Vcalendar::BYDAY] )) |
| 1011 |
/* check BYSETPOS */ |
| 1012 |
if( $updateOK ) { |
| 1013 |
if( isset( $recur[Vcalendar::BYSETPOS] ) && |
| 1014 |
( in_array( $recur[Vcalendar::FREQ], $YEAR2DAYARR ))) { |
| 1015 |
if( $recurFreqIsWeekly ) { |
| 1016 |
if( $bysetPosWold == |
| 1017 |
$dayCnts[$wDate[self::$LCMONTH]][$wDate[self::$LCDAY]][self::$WEEKNO_UP] ) { |
| 1018 |
$bysetPosw1[] = $wDateYMD; |
| 1019 |
} |
| 1020 |
else { |
| 1021 |
$bysetPosw2[] = $wDateYMD; |
| 1022 |
} |
| 1023 |
} |
| 1024 |
else { |
| 1025 |
if(( $recurFreqIsYearly && |
| 1026 |
( $bysetPosYold == $wDate[self::$LCYEAR] )) || |
| 1027 |
( $recurFreqIsMonthly && |
| 1028 |
(( $bysetPosYold == $wDate[self::$LCYEAR] ) && |
| 1029 |
( $bysetPosMold == $wDate[self::$LCMONTH] ))) || |
| 1030 |
( $recurFreqIsDaily && |
| 1031 |
(( $bysetPosYold == $wDate[self::$LCYEAR] ) && |
| 1032 |
( $bysetPosMold == $wDate[self::$LCMONTH] ) && |
| 1033 |
( $bysetPosDold == $wDate[self::$LCDAY] )))) { |
| 1034 |
$bysetPosymd1[] = $wDateYMD; |
| 1035 |
} |
| 1036 |
else { |
| 1037 |
$bysetPosymd2[] = $wDateYMD; |
| 1038 |
} |
| 1039 |
} // end else |
| 1040 |
} |
| 1041 |
else { // ! isset( $recur[Vcalendar::BYSETPOS] ) |
| 1042 |
if( checkdate( |
| 1043 |
(int) $wDate[self::$LCMONTH], |
| 1044 |
(int) $wDate[self::$LCDAY], |
| 1045 |
(int) $wDate[self::$LCYEAR] )) { |
| 1046 |
/* update result array if BYSETPOS is not set */ |
| 1047 |
$recurCount++; |
| 1048 |
if( $fcnStartYMD <= $wDateYMD ) { // only output within period |
| 1049 |
$result[$wDateYMD] = true; |
| 1050 |
} |
| 1051 |
} |
| 1052 |
$updateOK = false; |
| 1053 |
} |
| 1054 |
} |
| 1055 |
/* step up date */ |
| 1056 |
self::stepDate( $wDate, $wDateYMD, $step ); |
| 1057 |
/* check if BYSETPOS is set for updating result array */ |
| 1058 |
if( $updateOK && isset( $recur[Vcalendar::BYSETPOS] )) { |
| 1059 |
$bysetPos = false; |
| 1060 |
if( $recurFreqIsYearly && |
| 1061 |
( $bysetPosYold != $wDate[self::$LCYEAR] )) { |
| 1062 |
$bysetPos = true; |
| 1063 |
$bysetPosYold = $wDate[self::$LCYEAR]; |
| 1064 |
} |
| 1065 |
elseif( $recurFreqIsMonthly && |
| 1066 |
(( $bysetPosYold != $wDate[self::$LCYEAR] ) || |
| 1067 |
( $bysetPosMold != $wDate[self::$LCMONTH] ))) { |
| 1068 |
$bysetPos = true; |
| 1069 |
$bysetPosYold = $wDate[self::$LCYEAR]; |
| 1070 |
$bysetPosMold = $wDate[self::$LCMONTH]; |
| 1071 |
} |
| 1072 |
elseif( $recurFreqIsWeekly ) { |
| 1073 |
$weekNo = self::getWeekNumber( |
| 1074 |
0, |
| 1075 |
0, |
| 1076 |
$wkst, |
| 1077 |
$wDate[self::$LCMONTH], |
| 1078 |
$wDate[self::$LCDAY], |
| 1079 |
$wDate[self::$LCYEAR] |
| 1080 |
); |
| 1081 |
if( $bysetPosWold != $weekNo ) { |
| 1082 |
$bysetPosWold = $weekNo; |
| 1083 |
$bysetPos = true; |
| 1084 |
} |
| 1085 |
} |
| 1086 |
elseif( $recurFreqIsDaily && |
| 1087 |
(( $bysetPosYold != $wDate[self::$LCYEAR] ) || |
| 1088 |
( $bysetPosMold != $wDate[self::$LCMONTH] ) || |
| 1089 |
( $bysetPosDold != $wDate[self::$LCDAY] ))) { |
| 1090 |
$bysetPos = true; |
| 1091 |
$bysetPosYold = $wDate[self::$LCYEAR]; |
| 1092 |
$bysetPosMold = $wDate[self::$LCMONTH]; |
| 1093 |
$bysetPosDold = $wDate[self::$LCDAY]; |
| 1094 |
} |
| 1095 |
if( $bysetPos ) { |
| 1096 |
if( isset( $recur[Vcalendar::BYWEEKNO] )) { |
| 1097 |
$bysetPosArr1 = &$bysetPosw1; |
| 1098 |
$bysetPosArr2 = &$bysetPosw2; |
| 1099 |
} |
| 1100 |
else { |
| 1101 |
$bysetPosArr1 = &$bysetPosymd1; |
| 1102 |
$bysetPosArr2 = &$bysetPosymd2; |
| 1103 |
} |
| 1104 |
|
| 1105 |
foreach( $recur[Vcalendar::BYSETPOS] as $ix ) { |
| 1106 |
if( 0 > $ix ) { // both positive and negative BYSETPOS allowed |
| 1107 |
$ix = ( count( $bysetPosArr1 ) + $ix + 1 ); |
| 1108 |
} |
| 1109 |
$ix--; |
| 1110 |
if( isset( $bysetPosArr1[$ix] )) { |
| 1111 |
if( $fcnStartYMD <= $bysetPosArr1[$ix] ) { // only output within period |
| 1112 |
$result[$bysetPosArr1[$ix]] = true; |
| 1113 |
} |
| 1114 |
$recurCount++; |
| 1115 |
} |
| 1116 |
if( isset( $recur[Vcalendar::COUNT] ) && |
| 1117 |
( $recurCount >= $recur[Vcalendar::COUNT] )) { |
| 1118 |
break; |
| 1119 |
} |
| 1120 |
} |
| 1121 |
$bysetPosArr1 = $bysetPosArr2; |
| 1122 |
$bysetPosArr2 = []; |
| 1123 |
} // end if( $bysetPos ) |
| 1124 |
} // end if( $updateOK && isset( $recur['BYSETPOS'] )) |
| 1125 |
} // end while( true ) |
| 1126 |
ksort( $result ); |
| 1127 |
} |
| 1128 |
|
| 1129 |
/** |
| 1130 |
* Checking BYDAY (etc) hits, recur2date help function |
| 1131 |
* |
| 1132 |
* @since 2.6.12 - 2011-01-03 |
| 1133 |
* @param array $BYvalue |
| 1134 |
* @param int $upValue |
| 1135 |
* @param int $downValue |
| 1136 |
* @return bool |
| 1137 |
*/ |
| 1138 |
private static function recurBYcntcheck( $BYvalue, $upValue, $downValue ) |
| 1139 |
{ |
| 1140 |
if( is_array( $BYvalue ) && |
| 1141 |
( in_array( $upValue, $BYvalue ) || in_array( $downValue, $BYvalue )) |
| 1142 |
) { |
| 1143 |
return true; |
| 1144 |
} |
| 1145 |
elseif(( $BYvalue == $upValue ) || ( $BYvalue == $downValue )) { |
| 1146 |
return true; |
| 1147 |
} |
| 1148 |
else { |
| 1149 |
return false; |
| 1150 |
} |
| 1151 |
} |
| 1152 |
|
| 1153 |
/** |
| 1154 |
* (re-)Calculate internal index, recur2date help function |
| 1155 |
* |
| 1156 |
* @param string $freq |
| 1157 |
* @param array $date |
| 1158 |
* @param int $wkst |
| 1159 |
* @return bool |
| 1160 |
* @since 2.26 - 2018-11-10 |
| 1161 |
*/ |
| 1162 |
private static function recurIntervalIx( $freq, $date, $wkst ) |
| 1163 |
{ |
| 1164 |
/* create interval index */ |
| 1165 |
switch( $freq ) { |
| 1166 |
case Vcalendar::YEARLY : |
| 1167 |
$intervalIx = $date[self::$LCYEAR]; |
| 1168 |
break; |
| 1169 |
case Vcalendar::MONTHLY : |
| 1170 |
$intervalIx = |
| 1171 |
$date[self::$LCYEAR] . Util::$MINUS . $date[self::$LCMONTH]; |
| 1172 |
break; |
| 1173 |
case Vcalendar::WEEKLY : |
| 1174 |
$intervalIx = self::getWeekNumber( |
| 1175 |
0, 0, $wkst, |
| 1176 |
$date[self::$LCMONTH], $date[self::$LCDAY], $date[self::$LCYEAR] |
| 1177 |
); |
| 1178 |
break; |
| 1179 |
case Vcalendar::DAILY : |
| 1180 |
default: |
| 1181 |
$intervalIx = |
| 1182 |
$date[self::$LCYEAR] . |
| 1183 |
Util::$MINUS . |
| 1184 |
$date[self::$LCMONTH] . |
| 1185 |
Util::$MINUS . |
| 1186 |
$date[self::$LCDAY]; |
| 1187 |
break; |
| 1188 |
} // end switch |
| 1189 |
return $intervalIx; |
| 1190 |
} |
| 1191 |
|
| 1192 |
/** |
| 1193 |
* Return updated date, array and timpstamp |
| 1194 |
* |
| 1195 |
* @param array $date date to step |
| 1196 |
* @param string $dateYMD date YMD |
| 1197 |
* @param array $step default array( Util::$LCDAY => 1 ) |
| 1198 |
* @return void |
| 1199 |
*/ |
| 1200 |
private static function stepDate( & $date, & $dateYMD, $step = null ) |
| 1201 |
{ |
| 1202 |
if( is_null( $step )) { |
| 1203 |
$step = [ self::$LCDAY => 1 ]; |
| 1204 |
} |
| 1205 |
if( ! isset( $date[self::$LCHOUR] )) { |
| 1206 |
$date[self::$LCHOUR] = 0; |
| 1207 |
} |
| 1208 |
if( ! isset( $date[self::$LCMIN] )) { |
| 1209 |
$date[self::$LCMIN] = 0; |
| 1210 |
} |
| 1211 |
if( ! isset( $date[self::$LCSEC] )) { |
| 1212 |
$date[self::$LCSEC] = 0; |
| 1213 |
} |
| 1214 |
if( isset( $step[self::$LCDAY] )) { |
| 1215 |
$daysInMonth = self::getDaysInMonth( |
| 1216 |
$date[self::$LCHOUR], |
| 1217 |
$date[self::$LCMIN], |
| 1218 |
$date[self::$LCSEC], |
| 1219 |
$date[self::$LCMONTH], |
| 1220 |
$date[self::$LCDAY], |
| 1221 |
$date[self::$LCYEAR] |
| 1222 |
); |
| 1223 |
} |
| 1224 |
foreach( $step as $stepix => $stepvalue ) { |
| 1225 |
$date[$stepix] += $stepvalue; |
| 1226 |
} |
| 1227 |
if( isset( $step[self::$LCMONTH] )) { |
| 1228 |
if( 12 < $date[self::$LCMONTH] ) { |
| 1229 |
$date[self::$LCYEAR] += 1; |
| 1230 |
$date[self::$LCMONTH] -= 12; |
| 1231 |
} |
| 1232 |
} |
| 1233 |
elseif( isset( $step[self::$LCDAY] )) { |
| 1234 |
if( $daysInMonth < $date[self::$LCDAY] ) { |
| 1235 |
$date[self::$LCDAY] -= $daysInMonth; |
| 1236 |
$date[self::$LCMONTH] += 1; |
| 1237 |
if( 12 < $date[self::$LCMONTH] ) { |
| 1238 |
$date[self::$LCYEAR] += 1; |
| 1239 |
$date[self::$LCMONTH] -= 12; |
| 1240 |
} |
| 1241 |
} |
| 1242 |
} |
| 1243 |
$dateYMD = sprintf( |
| 1244 |
self::$YMDs, |
| 1245 |
$date[self::$LCYEAR], |
| 1246 |
$date[self::$LCMONTH], |
| 1247 |
$date[self::$LCDAY] |
| 1248 |
); |
| 1249 |
} |
| 1250 |
|
| 1251 |
/** |
| 1252 |
* Return initiated $dayCnts |
| 1253 |
* |
| 1254 |
* @param array $wDate |
| 1255 |
* @param array $recur |
| 1256 |
* @param int $wkst |
| 1257 |
* @return array |
| 1258 |
*/ |
| 1259 |
private static function initDayCnts( array $wDate, array $recur, $wkst ) |
| 1260 |
{ |
| 1261 |
$dayCnts = []; |
| 1262 |
$yearDayCnt = []; |
| 1263 |
$yearDays = 0; |
| 1264 |
foreach( self::$DAYNAMES as $dn ) { |
| 1265 |
$yearDayCnt[$dn] = 0; |
| 1266 |
} |
| 1267 |
for( $m = 1; $m <= 12; $m++ ) { // count up and update up-counters |
| 1268 |
$dayCnts[$m] = []; |
| 1269 |
$weekDayCnt = []; |
| 1270 |
foreach( self::$DAYNAMES as $dn ) { |
| 1271 |
$weekDayCnt[$dn] = 0; |
| 1272 |
} |
| 1273 |
$daysInMonth = self::getDaysInMonth( 0, 0, 0, $m, 1, $wDate[self::$LCYEAR] ); |
| 1274 |
for( $d = 1; $d <= $daysInMonth; $d++ ) { |
| 1275 |
$dayCnts[$m][$d] = []; |
| 1276 |
if( isset( $recur[Vcalendar::BYYEARDAY] )) { |
| 1277 |
$yearDays++; |
| 1278 |
$dayCnts[$m][$d][self::$YEARCNT_UP] = $yearDays; |
| 1279 |
} |
| 1280 |
if( isset( $recur[Vcalendar::BYDAY] )) { |
| 1281 |
$day = self::getDayInWeek( 0, 0, 0, $m, $d, $wDate[self::$LCYEAR] ); |
| 1282 |
$dayCnts[$m][$d][Vcalendar::DAY] = $day; |
| 1283 |
$weekDayCnt[$day]++; |
| 1284 |
$dayCnts[$m][$d][self::$MONTHDAYNO_UP] = $weekDayCnt[$day]; |
| 1285 |
$yearDayCnt[$day]++; |
| 1286 |
$dayCnts[$m][$d][self::$YEARDAYNO_UP] = $yearDayCnt[$day]; |
| 1287 |
} |
| 1288 |
if( isset( $recur[Vcalendar::BYWEEKNO] ) || |
| 1289 |
( $recur[Vcalendar::FREQ] == Vcalendar::WEEKLY )) { |
| 1290 |
$dayCnts[$m][$d][self::$WEEKNO_UP] = |
| 1291 |
self::getWeekNumber(0,0, $wkst, $m, $d, $wDate[self::$LCYEAR] ); |
| 1292 |
} |
| 1293 |
} // end for( $d = 1; $d <= $daysInMonth; $d++ ) |
| 1294 |
} // end for( $m = 1; $m <= 12; $m++ ) |
| 1295 |
$daycnt = 0; |
| 1296 |
$yearDayCnt = []; |
| 1297 |
if( isset( $recur[Vcalendar::BYWEEKNO] ) || |
| 1298 |
( $recur[Vcalendar::FREQ] == Vcalendar::WEEKLY )) { |
| 1299 |
$weekNo = null; |
| 1300 |
for( $d = 31; $d > 25; $d-- ) { // get last weekno for year |
| 1301 |
if( ! $weekNo ) { |
| 1302 |
$weekNo = $dayCnts[12][$d][self::$WEEKNO_UP]; |
| 1303 |
} |
| 1304 |
elseif( $weekNo < $dayCnts[12][$d][self::$WEEKNO_UP] ) { |
| 1305 |
$weekNo = $dayCnts[12][$d][self::$WEEKNO_UP]; |
| 1306 |
break; |
| 1307 |
} |
| 1308 |
} // end for |
| 1309 |
} |
| 1310 |
for( $m = 12; $m > 0; $m-- ) { // count down and update down-counters |
| 1311 |
$weekDayCnt = []; |
| 1312 |
foreach( self::$DAYNAMES as $dn ) { |
| 1313 |
$yearDayCnt[$dn] = $weekDayCnt[$dn] = 0; |
| 1314 |
} |
| 1315 |
$monthCnt = 0; |
| 1316 |
$daysInMonth = self::getDaysInMonth( 0, 0, 0, $m, 1, $wDate[self::$LCYEAR] ); |
| 1317 |
for( $d = $daysInMonth; $d > 0; $d-- ) { |
| 1318 |
if( isset( $recur[Vcalendar::BYYEARDAY] )) { |
| 1319 |
$daycnt -= 1; |
| 1320 |
$dayCnts[$m][$d][self::$YEARCNT_DOWN] = $daycnt; |
| 1321 |
} |
| 1322 |
if( isset( $recur[Vcalendar::BYMONTHDAY] )) { |
| 1323 |
$monthCnt -= 1; |
| 1324 |
$dayCnts[$m][$d][self::$MONTHCNT_DOWN] = $monthCnt; |
| 1325 |
} |
| 1326 |
if( isset( $recur[Vcalendar::BYDAY] )) { |
| 1327 |
$day = $dayCnts[$m][$d][Vcalendar::DAY]; |
| 1328 |
$weekDayCnt[$day] -= 1; |
| 1329 |
$dayCnts[$m][$d][self::$MONTHDAYNO_DOWN] = $weekDayCnt[$day]; |
| 1330 |
$yearDayCnt[$day] -= 1; |
| 1331 |
$dayCnts[$m][$d][self::$YEARDAYNO_DOWN] = $yearDayCnt[$day]; |
| 1332 |
} |
| 1333 |
if( isset( $recur[Vcalendar::BYWEEKNO] ) || |
| 1334 |
( $recur[Vcalendar::FREQ] == Vcalendar::WEEKLY )) { |
| 1335 |
$dayCnts[$m][$d][self::$WEEKNO_DOWN] = |
| 1336 |
( $dayCnts[$m][$d][self::$WEEKNO_UP] - $weekNo - 1 ); |
| 1337 |
} |
| 1338 |
} // end for( $d = $daysInMonth; $d > 0; $d-- ) |
| 1339 |
} // end for( $m = 12; $m > 0; $m-- ) |
| 1340 |
return $dayCnts; |
| 1341 |
} |
| 1342 |
|
| 1343 |
/** |
| 1344 |
* Return a reformatted input date |
| 1345 |
* |
| 1346 |
* @param mixed $inputDate |
| 1347 |
* @return array |
| 1348 |
* @throws Exception |
| 1349 |
* @since 2.29.21 - 2020-01-31 |
| 1350 |
*/ |
| 1351 |
private static function reFormatDate( $inputDate ) |
| 1352 |
{ |
| 1353 |
static $Y = 'Y'; |
| 1354 |
static $M = 'm'; |
| 1355 |
static $D = 'd'; |
| 1356 |
static $H = 'H'; |
| 1357 |
static $I = 'i'; |
| 1358 |
static $S = 'i'; |
| 1359 |
if( is_array( $inputDate )) { |
| 1360 |
return $inputDate; |
| 1361 |
} |
| 1362 |
if( ! $inputDate instanceof DateTime ) { |
| 1363 |
$inputDate = DateTimeFactory::factory( $inputDate ); |
| 1364 |
} |
| 1365 |
return [ |
| 1366 |
self::$LCYEAR => (int) $inputDate->format( $Y ), |
| 1367 |
self::$LCMONTH => (int) $inputDate->format( $M ), |
| 1368 |
self::$LCDAY => (int) $inputDate->format( $D ), |
| 1369 |
self::$LCHOUR => (int) $inputDate->format( $H ), |
| 1370 |
self::$LCMIN => (int) $inputDate->format( $I ), |
| 1371 |
self::$LCSEC => (int) $inputDate->format( $S ), |
| 1372 |
]; |
| 1373 |
} |
| 1374 |
|
| 1375 |
/** |
| 1376 |
* Return week number |
| 1377 |
* |
| 1378 |
* @param int $hour |
| 1379 |
* @param int $min |
| 1380 |
* @param int $sec |
| 1381 |
* @param int $month |
| 1382 |
* @param int $day |
| 1383 |
* @param int $year |
| 1384 |
* @return int |
| 1385 |
*/ |
| 1386 |
private static function getWeekNumber( $hour, $min, $sec, $month, $day, $year ) |
| 1387 |
{ |
| 1388 |
static $UCW = 'W'; // week number |
| 1389 |
return (int) date( $UCW, mktime( $hour, $min, $sec, $month, $day, $year )); |
| 1390 |
} |
| 1391 |
|
| 1392 |
/** |
| 1393 |
* Return number of days in month |
| 1394 |
* |
| 1395 |
* @param int $hour |
| 1396 |
* @param int $min |
| 1397 |
* @param int $sec |
| 1398 |
* @param int $month |
| 1399 |
* @param int $day |
| 1400 |
* @param int $year |
| 1401 |
* @return int |
| 1402 |
*/ |
| 1403 |
private static function getDaysInMonth( $hour, $min, $sec, $month, $day, $year ) |
| 1404 |
{ |
| 1405 |
static $LCT = 't'; // number of days in month |
| 1406 |
return (int) date( $LCT, mktime( $hour, $min, $sec, $month, $day, $year )); |
| 1407 |
} |
| 1408 |
|
| 1409 |
/** |
| 1410 |
* Return (string) 2-pos day in week |
| 1411 |
* |
| 1412 |
* @param int $hour |
| 1413 |
* @param int $min |
| 1414 |
* @param int $sec |
| 1415 |
* @param int $month |
| 1416 |
* @param int $day |
| 1417 |
* @param int $year |
| 1418 |
* @return string |
| 1419 |
*/ |
| 1420 |
private static function getDayInWeek( $hour, $min, $sec, $month, $day, $year ) |
| 1421 |
{ |
| 1422 |
static $LCW = 'w'; // day of week number |
| 1423 |
$dayNo = (int) date( |
| 1424 |
$LCW, |
| 1425 |
mktime( $hour, $min, $sec, $month, $day, $year ) |
| 1426 |
); |
| 1427 |
return self::$DAYNAMES[$dayNo]; |
| 1428 |
} |
| 1429 |
} |
| 1430 |
|