Page MenuHomePhabricator

D16614.id40004.diff
No OneTemporary

D16614.id40004.diff

diff --git a/src/parser/calendar/data/PhutilCalendarRecurrenceRule.php b/src/parser/calendar/data/PhutilCalendarRecurrenceRule.php
--- a/src/parser/calendar/data/PhutilCalendarRecurrenceRule.php
+++ b/src/parser/calendar/data/PhutilCalendarRecurrenceRule.php
@@ -210,7 +210,7 @@
$constants = implode('|', $constants);
$pattern = '/^(?:[+-]?([1-9]\d?))?('.$constants.')\z/';
- foreach ($by_day as $value) {
+ foreach ($by_day as $key => $value) {
$matches = null;
if (!preg_match($pattern, $value, $matches)) {
throw new Exception(
@@ -234,6 +234,9 @@
$magnitude,
$maximum));
}
+
+ // Normalize "+3FR" into "3FR".
+ $by_day[$key] = ltrim($value, '+');
}
$this->byDay = array_fuse($by_day);
@@ -589,9 +592,10 @@
$by_monthday = $this->getByMonthDay();
// Likewise, we need to generate all months if we have BYYEARDAY or
- // BYWEEKNO.
+ // BYWEEKNO or BYDAY.
$by_yearday = $this->getByYearDay();
$by_weekno = $this->getByWeekNumber();
+ $by_day = $this->getByDay();
while (!$this->setMonths) {
$this->nextYear();
@@ -601,6 +605,7 @@
|| $by_monthday
|| $by_yearday
|| $by_weekno
+ || $by_day
|| ($scale < self::SCALE_MONTHLY);
if ($is_dynamic) {
@@ -776,6 +781,19 @@
}
}
+ $frequency = $this->getFrequency();
+ $is_yearly = ($frequency == self::FREQUENCY_YEARLY);
+ $is_monthly = ($frequency == self::FREQUENCY_MONTHLY);
+
+ // As a special case, BYDAY applies to relative month offsets if BYMONTH
+ // is present in a YEARLY rule.
+ if ($is_yearly) {
+ if ($this->getByMonth()) {
+ $is_yearly = false;
+ $is_monthly = true;
+ }
+ }
+
$weeks = array();
foreach ($selection as $key => $info) {
if ($info['month'] != $this->stateMonth) {
@@ -783,11 +801,20 @@
}
if ($by_day) {
- // TODO: This only handles "BYDAY=MO,TU". It does not yet properly
- // handle "BYDAY=+1FR" (e.g., the first Friday in the month).
-
if (empty($by_day[$info['weekday']])) {
- continue;
+ if ($is_yearly) {
+ if (empty($by_day[$info['weekday.yearly']]) &&
+ empty($by_day[$info['-weekday.yearly']])) {
+ continue;
+ }
+ } else if ($is_monthly) {
+ if (empty($by_day[$info['weekday.monthly']]) &&
+ empty($by_day[$info['-weekday.monthly']])) {
+ continue;
+ }
+ } else {
+ continue;
+ }
}
}
@@ -910,11 +937,25 @@
$weekday_map = self::getWeekdayIndexMap();
$weekday_map = array_flip($weekday_map);
+ $yearly_counts = array();
+ $monthly_counts = array();
+
$month_number = 1;
$month_day = 1;
for ($year_day = 1; $year_day <= $max_day; $year_day++) {
$key = "{$month_number}M{$month_day}D";
+ $short_day = $weekday_map[$weekday];
+ if (empty($yearly_counts[$short_day])) {
+ $yearly_counts[$short_day] = 0;
+ }
+ $yearly_counts[$short_day]++;
+
+ if (empty($monthly_counts[$month_number][$short_day])) {
+ $monthly_counts[$month_number][$short_day] = 0;
+ }
+ $monthly_counts[$month_number][$short_day]++;
+
$info = array(
'year' => $year,
'key' => $key,
@@ -924,7 +965,9 @@
'yearday' => $year_day,
'-yearday' => -$max_day + $year_day - 1,
'week' => $week_number,
- 'weekday' => $weekday_map[$weekday],
+ 'weekday' => $short_day,
+ 'weekday.yearly' => $yearly_counts[$short_day],
+ 'weekday.monthly' => $monthly_counts[$month_number][$short_day],
);
$info_map[$key] = $info;
@@ -984,6 +1027,22 @@
$info['-week'] = -$week_number + $week - 1;
}
+ // Do all the arithmetic to figure out if this is the -19th Thursday
+ // in the year and such.
+ $month_number = $info['month'];
+ $short_day = $info['weekday'];
+ $monthly_count = $monthly_counts[$month_number][$short_day];
+ $monthly_index = $info['weekday.monthly'];
+ $info['-weekday.monthly'] = -$monthly_count + $monthly_index - 1;
+ $info['-weekday.monthly'] .= $short_day;
+ $info['weekday.monthly'] .= $short_day;
+
+ $yearly_count = $yearly_counts[$short_day];
+ $yearly_index = $info['weekday.yearly'];
+ $info['-weekday.yearly'] = -$yearly_count + $yearly_index - 1;
+ $info['-weekday.yearly'] .= $short_day;
+ $info['weekday.yearly'] .= $short_day;
+
$info_map[$key] = $info;
}
diff --git a/src/parser/calendar/data/__tests__/PhutilCalendarRecurrenceRuleTestCase.php b/src/parser/calendar/data/__tests__/PhutilCalendarRecurrenceRuleTestCase.php
--- a/src/parser/calendar/data/__tests__/PhutilCalendarRecurrenceRuleTestCase.php
+++ b/src/parser/calendar/data/__tests__/PhutilCalendarRecurrenceRuleTestCase.php
@@ -209,6 +209,54 @@
);
$tests[] = array(
+ 'BYDAY' => array('1TU', '-1TH'),
+ );
+ $expect[] = array(
+ '19971225',
+ '19980106',
+ '19981231',
+ );
+
+ // Same test as above, just making sure the optional "+" syntax works.
+ $tests[] = array(
+ 'BYDAY' => array('+1TU', '-1TH'),
+ );
+ $expect[] = array(
+ '19971225',
+ '19980106',
+ '19981231',
+ );
+
+ $tests[] = array(
+ 'BYDAY' => array('3TU', '-3TH'),
+ );
+ $expect[] = array(
+ '19971211',
+ '19980120',
+ '19981217',
+ );
+
+ $tests[] = array(
+ 'BYMONTH' => array(1, 3),
+ 'BYDAY' => array('1TU', '-1TH'),
+ );
+ $expect[] = array(
+ '19980106',
+ '19980129',
+ '19980303',
+ );
+
+ $tests[] = array(
+ 'BYMONTH' => array(1, 3),
+ 'BYDAY' => array('3TU', '-3TH'),
+ );
+ $expect[] = array(
+ '19980115',
+ '19980120',
+ '19980312',
+ );
+
+ $tests[] = array(
'BYYEARDAY' => array(1, 100, 200, 365),
'COUNT' => 4,
);

File Metadata

Mime Type
text/plain
Expires
Sun, Mar 16, 2:55 AM (3 d, 4 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7531706
Default Alt Text
D16614.id40004.diff (5 KB)

Event Timeline