diff --git a/build/ical.js b/build/ical.js index 93b864f7..bfd3803d 100644 --- a/build/ical.js +++ b/build/ical.js @@ -6684,10 +6684,7 @@ ICAL.RecurIterator = (function() { this.increment_year(this.rule.interval); } - var next = ICAL.Time.fromDayOfYear(this.days[0], this.last.year); - - this.last.day = next.day; - this.last.month = next.month; + this._nextByYearDay(); } if (this.rule.freq == "MONTHLY" && this.has_by_data("BYDAY")) { @@ -7247,15 +7244,27 @@ ICAL.RecurIterator = (function() { } while (this.days.length == 0); } - var next = ICAL.Time.fromDayOfYear(this.days[this.days_index], - this.last.year); - - this.last.day = next.day; - this.last.month = next.month; + this._nextByYearDay(); return 1; }, + _nextByYearDay: function _nextByYearDay() { + var doy = this.days[this.days_index]; + var year = this.last.year; + if (doy < 1) { + // Time.fromDayOfYear(doy, year) indexes relative to the + // start of the given year. That is different from the + // semantics of BYYEARDAY where negative indexes are an + // offset from the end of the given year. + doy += 1; + year += 1; + } + var next = ICAL.Time.fromDayOfYear(doy, year); + this.last.day = next.day; + this.last.month = next.month; + }, + ruleDayOfWeek: function ruleDayOfWeek(dow) { var matches = dow.match(/([+-]?[0-9])?(MO|TU|WE|TH|FR|SA|SU)/); if (matches) { diff --git a/lib/ical/recur_iterator.js b/lib/ical/recur_iterator.js index e166fa22..d27a3175 100644 --- a/lib/ical/recur_iterator.js +++ b/lib/ical/recur_iterator.js @@ -240,10 +240,7 @@ ICAL.RecurIterator = (function() { this.increment_year(this.rule.interval); } - var next = ICAL.Time.fromDayOfYear(this.days[0], this.last.year); - - this.last.day = next.day; - this.last.month = next.month; + this._nextByYearDay(); } if (this.rule.freq == "MONTHLY" && this.has_by_data("BYDAY")) { @@ -803,15 +800,27 @@ ICAL.RecurIterator = (function() { } while (this.days.length == 0); } - var next = ICAL.Time.fromDayOfYear(this.days[this.days_index], - this.last.year); - - this.last.day = next.day; - this.last.month = next.month; + this._nextByYearDay(); return 1; }, + _nextByYearDay: function _nextByYearDay() { + var doy = this.days[this.days_index]; + var year = this.last.year; + if (doy < 1) { + // Time.fromDayOfYear(doy, year) indexes relative to the + // start of the given year. That is different from the + // semantics of BYYEARDAY where negative indexes are an + // offset from the end of the given year. + doy += 1; + year += 1; + } + var next = ICAL.Time.fromDayOfYear(doy, year); + this.last.day = next.day; + this.last.month = next.month; + }, + ruleDayOfWeek: function ruleDayOfWeek(dow) { var matches = dow.match(/([+-]?[0-9])?(MO|TU|WE|TH|FR|SA|SU)/); if (matches) { diff --git a/test/recur_iterator_test.js b/test/recur_iterator_test.js index 1a501211..0761cf1e 100644 --- a/test/recur_iterator_test.js +++ b/test/recur_iterator_test.js @@ -879,4 +879,65 @@ suite('recur_iterator', function() { '2019-01-07T08:00:00' ] }); + + // Tycho brahe days - yearly, byYearDay with negative offsets + testRRULE('FREQ=YEARLY;BYYEARDAY=1,2,4,6,11,12,20,42,48,49,-306,-303,' + + '-293,-292,-266,-259,-258,-239,-228,-209,-168,-164,-134,-133,' + + '-113,-105,-87,-56,-44,-26,-21,-14', { + dtStart: '2015-01-01', + dates: [ + '2015-01-01', + '2015-01-02', + '2015-01-04', + '2015-01-06', + '2015-01-11', + '2015-01-12', + '2015-01-20', + '2015-02-11', + '2015-02-17', + '2015-02-18', + '2015-03-01', + '2015-03-04', + '2015-03-14', + '2015-03-15', + '2015-04-10', + '2015-04-17', + '2015-04-18', + '2015-05-07', + '2015-05-18', + '2015-06-06', + '2015-07-17', + '2015-07-21', + '2015-08-20', + '2015-08-21', + '2015-09-10', + '2015-09-18', + '2015-10-06', + '2015-11-06', + '2015-11-18', + '2015-12-06', + '2015-12-11', + '2015-12-18' + ] + }); + + // Leap year - yearly, byYearDay with negative offsets + testRRULE('FREQ=YEARLY;BYYEARDAY=-308,-307,-306', { + dtStart: '2012-01-01', + dates: [ + '2012-02-28', + '2012-02-29', + '2012-03-01', + ] + }); + + // Non-leap year - yearly, byYearDay with negative offsets + testRRULE('FREQ=YEARLY;BYYEARDAY=-307,-306,-305', { + dtStart: '2013-01-01', + dates: [ + '2013-02-28', + '2013-03-01', + '2013-03-02', + ] + }); });