Skip to content

Commit

Permalink
Closes #16 - support ability to parse multiple months in YEARLY BYMONTH
Browse files Browse the repository at this point in the history
Add test data
  • Loading branch information
u01jmg3 committed Jan 10, 2017
1 parent b951065 commit e507ec1
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 77 deletions.
28 changes: 28 additions & 0 deletions examples/MyCal.ics
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,32 @@ STATUS:CONFIRMED
SUMMARY;LANGUAGE=en-gb:BYDAY Test 2
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20170111
DTEND;VALUE=DATE:20170111
DTSTAMP;TZID="GMT Standard Time":20170121T195741Z
RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=5;BYMONTH=1,2,3
UID:f50e8b89a4a3b0070e0b687d03@google.com
CREATED:20170119T142040Z
DESCRIPTION;LANGUAGE=en-gb:BYMONTH Multiple Test 1
LAST-MODIFIED:20170409T150000Z
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY;LANGUAGE=en-gb:BYMONTH Multiple Test 1
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20170405
DTEND;VALUE=DATE:20170405
DTSTAMP;TZID="GMT Standard Time":20170121T195741Z
RRULE:FREQ=YEARLY;BYMONTH=4,5,6;BYDAY=WE;COUNT=5
UID:675f06aa795665ae50904ebf0e@google.com
CREATED:20170119T142040Z
DESCRIPTION;LANGUAGE=en-gb:BYMONTH Multiple Test 2
LAST-MODIFIED:20170409T150000Z
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY;LANGUAGE=en-gb:BYMONTH Multiple Test 2
TRANSP:TRANSPARENT
END:VEVENT
END:VCALENDAR
165 changes: 88 additions & 77 deletions src/ICal/ICal.php
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,11 @@ public function processRecurrences()
$recurringTimestamp = $startTimestamp;
$offset = "+{$interval} year";

// Deal with BYMONTH
if (isset($rrules['BYMONTH']) && $rrules['BYMONTH'] !== '') {
$bymonths = explode(',', $rrules['BYMONTH']);
}

// Check if BYDAY rule exists
if (isset($rrules['BYDAY']) && $rrules['BYDAY'] !== '') {
while ($recurringTimestamp <= $until) {
Expand All @@ -1087,23 +1092,93 @@ public function processRecurrences()
$timezoneOffset = ($this->useTimeZoneWithRRules) ? $initialStart->getTimezone()->getOffset($recurringTimeZone) : 0;
$yearRecurringTimestamp += ($timezoneOffset !== $initialStartOffset) ? $initialStartOffset - $timezoneOffset : 0;

$eventStartDesc = "{$this->dayOrdinals[$dayNumber]} {$this->weekdays[$weekDay]}"
. " of {$this->monthNames[$rrules['BYMONTH']]} "
. gmdate('Y H:i:s', $yearRecurringTimestamp);
$eventStartTimestamp = strtotime($eventStartDesc);

if (intval($rrules['BYDAY']) === 0) {
$lastDayDesc = "last {$this->weekdays[$weekDay]}"
. " of {$this->monthNames[$rrules['BYMONTH']]} "
foreach ($bymonths as $bymonth) {
$eventStartDesc = "{$this->dayOrdinals[$dayNumber]} {$this->weekdays[$weekDay]}"
. " of {$this->monthNames[$bymonth]} "
. gmdate('Y H:i:s', $yearRecurringTimestamp);
$eventStartTimestamp = strtotime($eventStartDesc);

if (intval($rrules['BYDAY']) === 0) {
$lastDayDesc = "last {$this->weekdays[$weekDay]}"
. " of {$this->monthNames[$bymonth]} "
. gmdate('Y H:i:s', $yearRecurringTimestamp);
} else {
$lastDayDesc = "{$this->dayOrdinals[$dayNumber]} {$this->weekdays[$weekDay]}"
. " of {$this->monthNames[$bymonth]} "
. gmdate('Y H:i:s', $yearRecurringTimestamp);
}
$lastDayTimestamp = strtotime($lastDayDesc);

do {
if ($eventStartTimestamp > $startTimestamp && $eventStartTimestamp < $until) {
$anEvent['DTSTART'] = date(self::DATE_TIME_FORMAT, $eventStartTimestamp) . ($isAllDayEvent || $initialStartTimeZoneName === 'Z' ? 'Z' : '');
$anEvent['DTSTART_array'][1] = $anEvent['DTSTART'];
$anEvent['DTSTART_array'][2] = $eventStartTimestamp;
$anEvent['DTEND_array'] = $anEvent['DTSTART_array'];
$anEvent['DTEND_array'][2] += $eventTimestampOffset;
$anEvent['DTEND'] = date(
self::DATE_TIME_FORMAT,
$anEvent['DTEND_array'][2]
) . ($isAllDayEvent || $initialEndTimeZoneName === 'Z' ? 'Z' : '');
$anEvent['DTEND_array'][1] = $anEvent['DTEND'];

$searchDate = $anEvent['DTSTART'];
$isExcluded = array_filter($anEvent['EXDATE_array'][1], function ($val) use ($searchDate) {
return $this->iCalDateToUnixTimestamp($searchDate) === $this->iCalDateToUnixTimestamp($val);
});

if (isset($anEvent['UID'])) {
if (isset($this->alteredRecurrenceInstances[$anEvent['UID']]) && in_array($yearRecurringTimestamp, $this->alteredRecurrenceInstances[$anEvent['UID']])) {
$isExcluded = true;
}
}

if (!$isExcluded) {
$events[] = $anEvent;
$this->eventCount++;

// If RRULE[COUNT] is reached then break
if (isset($rrules['COUNT'])) {
$countNb++;

if ($countNb >= $countOrig) {
break 3;
}
}
}
}

$eventStartTimestamp += 7 * 86400;
} while ($eventStartTimestamp <= $lastDayTimestamp);
}

// Move forwards
$recurringTimestamp = strtotime($offset, $recurringTimestamp);
}
} else {
$day = gmdate('d', $startTimestamp);

// Step through years
while ($recurringTimestamp <= $until) {
$yearRecurringTimestamp = $recurringTimestamp;

// Adjust timezone from initial event
$recurringTimeZone = \DateTime::createFromFormat('U', $yearRecurringTimestamp);
$timezoneOffset = ($this->useTimeZoneWithRRules) ? $initialStart->getTimezone()->getOffset($recurringTimeZone) : 0;
$yearRecurringTimestamp += ($timezoneOffset !== $initialStartOffset) ? $initialStartOffset - $timezoneOffset : 0;

$eventStartDescs = array();
if (isset($rrules['BYMONTH']) && $rrules['BYMONTH'] !== '') {
foreach ($bymonths as $bymonth) {
array_push($eventStartDescs, "$day {$this->monthNames[$bymonth]} " . gmdate('Y H:i:s', $yearRecurringTimestamp));
}
} else {
$lastDayDesc = "{$this->dayOrdinals[$dayNumber]} {$this->weekdays[$weekDay]}"
. " of {$this->monthNames[$rrules['BYMONTH']]} "
. gmdate('Y H:i:s', $yearRecurringTimestamp);
array_push($eventStartDescs, $day . gmdate('F Y H:i:s', $yearRecurringTimestamp));
}
$lastDayTimestamp = strtotime($lastDayDesc);

do {
foreach ($eventStartDescs as $eventStartDesc) {
$eventStartTimestamp = strtotime($eventStartDesc);

if ($eventStartTimestamp > $startTimestamp && $eventStartTimestamp < $until) {
$anEvent['DTSTART'] = date(self::DATE_TIME_FORMAT, $eventStartTimestamp) . ($isAllDayEvent || $initialStartTimeZoneName === 'Z' ? 'Z' : '');
$anEvent['DTSTART_array'][1] = $anEvent['DTSTART'];
Expand Down Expand Up @@ -1141,70 +1216,6 @@ public function processRecurrences()
}
}
}

$eventStartTimestamp += 7 * 86400;
} while ($eventStartTimestamp <= $lastDayTimestamp);

// Move forwards
$recurringTimestamp = strtotime($offset, $recurringTimestamp);
}
} else {
$day = gmdate('d', $startTimestamp);

// Step through years
while ($recurringTimestamp <= $until) {
$yearRecurringTimestamp = $recurringTimestamp;

// Adjust timezone from initial event
$recurringTimeZone = \DateTime::createFromFormat('U', $yearRecurringTimestamp);
$timezoneOffset = ($this->useTimeZoneWithRRules) ? $initialStart->getTimezone()->getOffset($recurringTimeZone) : 0;
$yearRecurringTimestamp += ($timezoneOffset !== $initialStartOffset) ? $initialStartOffset - $timezoneOffset : 0;

// Add specific month dates
if (isset($rrules['BYMONTH']) && $rrules['BYMONTH'] !== '') {
$eventStartDesc = "$day {$this->monthNames[$rrules['BYMONTH']]} " . gmdate('Y H:i:s', $yearRecurringTimestamp);
} else {
$eventStartDesc = $day . gmdate('F Y H:i:s', $yearRecurringTimestamp);
}

$eventStartTimestamp = strtotime($eventStartDesc);

if ($eventStartTimestamp > $startTimestamp && $eventStartTimestamp < $until) {
$anEvent['DTSTART'] = date(self::DATE_TIME_FORMAT, $eventStartTimestamp) . ($isAllDayEvent || $initialStartTimeZoneName === 'Z' ? 'Z' : '');
$anEvent['DTSTART_array'][1] = $anEvent['DTSTART'];
$anEvent['DTSTART_array'][2] = $eventStartTimestamp;
$anEvent['DTEND_array'] = $anEvent['DTSTART_array'];
$anEvent['DTEND_array'][2] += $eventTimestampOffset;
$anEvent['DTEND'] = date(
self::DATE_TIME_FORMAT,
$anEvent['DTEND_array'][2]
) . ($isAllDayEvent || $initialEndTimeZoneName === 'Z' ? 'Z' : '');
$anEvent['DTEND_array'][1] = $anEvent['DTEND'];

$searchDate = $anEvent['DTSTART'];
$isExcluded = array_filter($anEvent['EXDATE_array'][1], function ($val) use ($searchDate) {
return $this->iCalDateToUnixTimestamp($searchDate) === $this->iCalDateToUnixTimestamp($val);
});

if (isset($anEvent['UID'])) {
if (isset($this->alteredRecurrenceInstances[$anEvent['UID']]) && in_array($yearRecurringTimestamp, $this->alteredRecurrenceInstances[$anEvent['UID']])) {
$isExcluded = true;
}
}

if (!$isExcluded) {
$events[] = $anEvent;
$this->eventCount++;

// If RRULE[COUNT] is reached then break
if (isset($rrules['COUNT'])) {
$countNb++;

if ($countNb >= $countOrig) {
break 2;
}
}
}
}

// Move forwards
Expand Down

0 comments on commit e507ec1

Please sign in to comment.