docs: function(options, noCache) {
  
      // RFC string
      this._string = null;
  
      var defaults = _.clone(RRule.DEFAULT_OPTIONS);
      options = options || {};
  
      this._cache = noCache ? null : {
          all: false,
          before: [],
          after: [],
          between: []
      };
  
      var invalid = _(options)
          .chain()
          .keys()
          .reject(function(name){
              return _.has(defaults, name)
          })
          .value();
      if (invalid.length) {
          throw 'Invalid options: ' + invalid.join(', ')
      }
  
      if (!RRule.FREQUENCIES[options.freq] && options.byeaster === null) {
          throw 'Invalid frequency: ' + String(options.freq)
      }
  
      // used by toString()
      this.origOptions = _.clone(options);
      var opts;
      this.options = opts = _.extend({}, defaults, options);
  
      if (opts.byeaster !== null) {
          opts.freq = RRule.YEARLY;
      }
  
      if (!opts.dtstart) {
          opts.dtstart = new Date();
          opts.dtstart.setMilliseconds(0);
      }
  
      if (opts.wkst === null) {
          opts.wkst = RRule.MO.weekday;
      } else if (typeof opts.wkst == 'number') {
          // cool, just keep it like that
      } else {
          opts.wkst = opts.wkst.weekday;
      }
  
      if (opts.bysetpos !== null) {
          if (typeof opts.bysetpos == 'number') {
              opts.bysetpos = [opts.bysetpos];
          }
          for (var i = 0; i < opts.bysetpos.length; i++) {
              var v = opts.bysetpos[i];
              if (v == 0 || !(-366 <= v && v <= 366)) {
                  throw 'bysetpos must be between 1 and 366, or between -366 and -1';
              }
          }
      }
  
      if (!(plb(opts.byweekno) || plb(opts.byyearday)
          || plb(opts.bymonthday) || opts.byweekday !== null
          || opts.byeaster !== null))
      {
          switch (opts.freq) {
              case RRule.YEARLY:
                  if (!opts.bymonth) {
                      opts.bymonth = opts.dtstart.getMonth() + 1;
                  }
                  opts.bymonthday = opts.dtstart.getDate();
                  break;
              case RRule.MONTHLY:
                  opts.bymonthday = opts.dtstart.getDate();
                  break;
              case RRule.WEEKLY:
                  opts.byweekday = dateutil.getWeekday(
                                              opts.dtstart);
                  break;
          }
      }
  
      // bymonth
      if (opts.bymonth !== null
          && !(opts.bymonth instanceof Array)) {
          opts.bymonth = [opts.bymonth];
      }
  
      // byyearday
      if (opts.byyearday !== null
          && !(opts.byyearday instanceof Array)) {
          opts.byyearday = [opts.byyearday];
      }
  
      // bymonthday
      if (opts.bymonthday === null) {
          opts.bymonthday = [];
          opts.bynmonthday = [];
      } else if (opts.bymonthday instanceof Array) {
          var bymonthday = [], bynmonthday = [];
  
          for (i = 0; i < opts.bymonthday.length; i++) {
              var v = opts.bymonthday[i];
              if (v > 0) {
                  bymonthday.push(v);
              } else if (v < 0) {
                  bynmonthday.push(v);
              }
          }
          opts.bymonthday = bymonthday;
          opts.bynmonthday = bynmonthday;
      } else {
          if (opts.bymonthday < 0) {
              opts.bynmonthday = [opts.bymonthday];
              opts.bymonthday = [];
          } else {
              opts.bynmonthday = [];
              opts.bymonthday = [opts.bymonthday];
          }
      }
  
      // byweekno
      if (opts.byweekno !== null
          && !(opts.byweekno instanceof Array)) {
          opts.byweekno = [opts.byweekno];
      }
  
      // byweekday / bynweekday
      if (opts.byweekday === null) {
          opts.bynweekday = null;
      } else if (typeof opts.byweekday == 'number') {
          opts.byweekday = [opts.byweekday];
          opts.bynweekday = null;
  
      } else if (opts.byweekday instanceof Weekday) {
  
          if (!opts.byweekday.n || opts.freq > RRule.MONTHLY) {
              opts.byweekday = [opts.byweekday.weekday];
              opts.bynweekday = null;
          } else {
              opts.bynweekday = [
                  [opts.byweekday.weekday,
                   opts.byweekday.n]
              ];
              opts.byweekday = null;
          }
  
      } else {
          var byweekday = [], bynweekday = [];
  
          for (i = 0; i < opts.byweekday.length; i++) {
              var wday = opts.byweekday[i];
  
              if (typeof wday == 'number') {
                  byweekday.push(wday);
              } else if (!wday.n || opts.freq > RRule.MONTHLY) {
                  byweekday.push(wday.weekday);
              } else {
                  bynweekday.push([wday.weekday, wday.n]);
              }
          }
          opts.byweekday = plb(byweekday) ? byweekday : null;
          opts.bynweekday = plb(bynweekday) ? bynweekday : null;
      }
  
      // byhour
      if (opts.byhour === null) {
          opts.byhour = (opts.freq < RRule.HOURLY)
              ? [opts.dtstart.getHours()]
              : null;
      } else if (typeof opts.byhour == 'number') {
          opts.byhour = [opts.byhour];
      }
  
      // byminute
      if (opts.byminute === null) {
          opts.byminute = (opts.freq < RRule.MINUTELY)
              ? [opts.dtstart.getMinutes()]
              : null;
      } else if (typeof opts.byminute == 'number') {
          opts.byminute = [opts.byminute];
      }
  
      // bysecond
      if (opts.bysecond === null) {
          opts.bysecond = (opts.freq < RRule.SECONDLY)
              ? [opts.dtstart.getSeconds()]
              : null;
      } else if (typeof opts.bysecond == 'number') {
          opts.bysecond = [opts.bysecond];
      }
  
      if (opts.freq >= RRule.HOURLY) {
          this.timeset = null;
      } else {
          this.timeset = [];
          for (i = 0; i < opts.byhour.length; i++) {
              var hour = opts.byhour[i];
              for (var j = 0; j < opts.byminute.length; j++) {
                  var minute = opts.byminute[j];
                  for (var k = 0; k < opts.bysecond.length; k++) {
                      var second = opts.bysecond[k];
                      // python:
                      // datetime.time(hour, minute, second,
                      // tzinfo=self._tzinfo))
                      this.timeset.push(new dateutil.Time(hour, minute, second));
                  }
              }
          }
          dateutil.sort(this.timeset);
      }
  
  }