Open florianchevallier opened 5 years ago
I've encountered something similar too, and I think it's the Z at the ISO date end. Weird
From what I can tell, it's a bug in rrulestr
where it reuses the DTSTART
from the first RRule for subsequent rrules
@davidgoli If you're interested I can open a new PR for this once my current one is merged
Until this gets fixed I implemented an alternative to rrulestr that should handle DTSTART as expected:
import { RRule, RRuleSet, rrulestr } from 'rrule';
/**
* Parse a rrule string as a RRuleSet.
*
* This also fixes a bug in {@link rrulestr} where the dtstart of the rrules following the first one are lost.
* {@link https://github.com/jakubroztocil/rrule/issues/332}
*
* Does not support EXRULE, RDATE, or EXDATE
*
* @throws Error if the rrule cannot be parsed into a rruleset.
* @param {string} rruleStr
* @returns {RRuleSet}
*/
export function parseRRuleSet(rruleStr: string): RRuleSet {
const set = new RRuleSet();
rruleStr = rruleStr.trim();
let dtStart = '';
for (let line of rruleStr.split('\n')) {
line = line.trim();
const { name, value, parms } = breakDownLine(line);
if (name !== 'RRULE' && dtStart) {
throw new Error('Incorrectly placed DTSTART found. Must be placed one line before RRULE');
}
switch (name) {
case 'RDATE': {
const dates = parseRDate(value, parms);
for (const date of dates) {
set.rdate(date);
}
break;
}
case 'DTSTART':
dtStart = line;
continue;
case 'RRULE': {
const rrule = parseRrule(`${dtStart}\n${line}`);
set.rrule(rrule);
dtStart = '';
break;
}
default:
throw new Error('parseRRuleSet only supports DTSTART, RDATE & RRULE for now');
}
}
return set;
}
export function parseRrule(rruleStr: string): RRule {
const rrule = rrulestr(rruleStr);
if (!(rrule instanceof RRule)) {
throw new Error('Cannot parse input as RRule. Is it an RRuleSet?');
}
return rrule;
}
function parseRDate(rdateval, parms): Date[] {
validateDateParm(parms);
return rdateval
.split(',')
.map(datestr => {
return parseRRuleDate(datestr);
});
}
function parseRRuleDate(until: string): Date {
const re = /^(\d{4})(\d{2})(\d{2})(T(\d{2})(\d{2})(\d{2})Z?)?$/;
const bits = re.exec(until);
if (!bits) {
throw new Error(`Invalid RRule Date value: ${until}`);
}
const parseInt = Number.parseInt;
return new Date(Date.UTC(
parseInt(bits[1], 10),
parseInt(bits[2], 10) - 1,
parseInt(bits[3], 10),
parseInt(bits[5], 10) || 0,
parseInt(bits[6], 10) || 0,
parseInt(bits[7], 10) || 0,
));
}
function validateDateParm(parms) {
parms.forEach(parm => {
if (!/(VALUE=DATE(-TIME)?)|(TZID=)/.test(parm)) {
throw new Error(`unsupported RDATE/EXDATE parm: ${parm}`);
}
});
}
function breakDownLine(line) {
const { name, value } = extractName(line);
const parms = name.split(';');
if (!parms) {
throw new Error('empty property name');
}
return {
name: parms[0].toUpperCase(),
parms: parms.slice(1),
value,
};
}
function extractName(line) {
if (line.indexOf(':') === -1) {
return {
name: 'RRULE',
value: line,
};
}
const [name, value] = pythonSplit(line, ':', 1);
return {
name,
value,
};
}
function pythonSplit(str: string, sep: string, splitCount?: number) {
const splits = str.split(sep);
return splitCount
? splits.slice(0, splitCount).concat([splits.slice(splitCount).join(sep)])
: splits;
}
Thank you so mutch this works for me.
Reporting an issue
Thank you for taking an interest in
rrule
! Please include the following in your report:rrule
you are using$ date
from the command line of the machine showing the bug)rrule version : ^2.6.0 on MacOS date: Lun 18 mar 2019 16:47:35 CET
Hi,
I'm trying to create a
rruleset
from a string passed in the DB, and I have a different behavior when using tworruleset.rrule
or just one.I'm not really clear, so here is the code sample :
And here is the
console.log
associated, with the difference of the toString() of the two methods highlighted with a + and - :Obviously, the second result acts weirdly. I don't know if it's a bug, but it feels like one.