rrecur
v2.0.0
Published
Convert between RFC2445 RRULE and its JSON equivalent. Useful with rrule.js.
Maintainers
Readme
rrecur
Convert between RFC2445 RRULE and its JSON equivalent AND see the future (and past). Useful with rrule.js.
If you want UIs like these:
Google Calendar: http://imgur.com/a/gT7Af
Thunderbird Calendar: http://imgur.com/a/LhnWU
Kendo Calendar: http://imgur.com/a/zVLyg
You need a schedule-logic library like this to actually interpret RRULEs and schedule the events.
Usage
parse & stringify
This snippet will work both in the Browser and with Node.js (hence the scary bit at the bottom).
From JSON to RRULE
(function (exports) {
'use strict';
var Rrecur = exports.Rrecur || require('rrecur').Rrecur
, rfcString
;
// every other month on the first and last sunday
rfcString = Rrecur.stringify({
"freq": "monthly"
, "interval": "2"
, "count": "10"
, "byday": ["1su","-1su"]
});
}('undefined' !== typeof exports && exports || new Function('return this')()));From RRULE to JSON
(function (exports) {
'use strict';
var Rrecur = exports.Rrecur || require('rrecur').Rrecur
, rfcString
, rruleObject
;
rfcString = "RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU";
rruleObject = Rrecur.parse(rfcString);
// Also supports sans-RRULE prefix plus DTSTART for the sake of `rrule.js` compatability
rfcString = "DTSTART=20140616T103000Z;FREQ=DAILY;BYHOUR=10;BYMINUTE=30;BYSECOND=0;UNTIL=20150616T153000Z";
rruleObject = Rrecur.parse(rfcString);
}('undefined' !== typeof exports && exports || new Function('return this')()));next occurrence of an event
Scenario: I want an alarm to go off every day at 10:30am, given my location.
Since we can't specify timezones with a JavaScript date object, we pretend internally that local time is zoneless time. UTC is handled exactly as UTC.
So let's say it's 8:00am on our server in Utah on the first day of summer and we want to set an alarm for 10:30am in New York (30 minutes from now) every mon, wed, fri:
var rrecur
;
rrecur = Rrecur.create({
dtstart: {
zoneless: Rrecur.toLocaleISOString(new Date(2014,06,21, 10,30,0), "GMT-0400 (EDT)")
// OR utc: new Date(2014,06,21, 10,30,0).toISOString()
, locale: "GMT-0400 (EDT)"
}
, rrule: {
freq: 'daily'
, until: Rrecur.toAdjustedISOString(new Date(2014,06,22, 10,30,0), "GMT-0400 (EDT)")
, count: 1
, byhour: [10]
, byminute: [30]
, byday: ['mo','we','fr'] // Or [Rrecur.weekdays[1], Rrecur.weekdays[3], Rrecur.weekdays[5]]
// `bysecond` will default to 00, since that's what's specified in `dtstart`
}
}, new Date());
rrecur.next(); // 2014-05-21T10:30:00.000-0400If you didn't specify locale then you would get back a time in UTC
or in GMT-0600 (MDT) that you would need to manually adjust.
Whether you specify zoneless or utc, you must still specify locale.
one-time events
var rrecur
;
rrecur = Rrecur.create({
dtstart: {
locale: new Date(2014,06,21, 10,30,0).toString()
}
, rrule: null
}, new Date());
// Assuming you specified the Date on a machine running on MDT
rrecur.next(); // 2014-07-21T16:30:00.000-0000For one-time events you may simply use a locale string, which will be converted
into to both zoneless and utc for internal purposes.
Also, rrule will be automatically populated to match the zoneless dtstart.
Installation
browser
NOTE: you only need rrecur.js for the basic JSON <-> RRULE conversion.
If you want to actually find the occurances you'll need
underscore.js, rrule.js, and moment.js, and rrecur-iterator.js.
However, in some future version I may be able to eliminate underscore.js and rrule.js.
via bower
bower install rrecurvia download
wget https://raw2.github.com/coolaj86/rrecurjs/master/rrecur.js
wget https://raw2.github.com/coolaj86/rrecurjs/master/rrecur-iterator.jsand insert the script tag, of course
<script src="underscore.js"></script>
<script src="rrule.js"></script>
<script src="moment.js"></script>
<script src="rrecur.js"></script>
<script src="rrecur-iterator.js"></script>script(src="underscore.js")
script(src="rrule.js")
script(src="moment.js")
script(src="rrecur.js")
script(src="rrecur-iterator.js")I know, it's a lot of dependencies... but that's just how it is.
In a future version it may be reasonable to drop underscore and rrule
(or at least substitute lodash for underscore),
but moment is a must. JavaScript's Date object is just too messed up.
node.js
npm install rrecurAPI
Convert between RFC2445 RRULE and its JSON equivalent.
Rrecur.parse(rruleStr)- parses a string rrule (allows non-standarddtstartin string)Rrecur.stringify(rruleObj)- stringifies an rrule object (allows non-standarddtstart)
Find the next (or previous) occurence of an event in an rrule chain.
Rrecur.create(rrule, localeDateString)- creates a wrapped instance fromrrule.jsfrom an rrule object or stringlocaleDateString- a string such asWed Jul 16 2014 10:30:00 GMT-0400 (EDT)Rrecur#previous()- cycles backwards through time to find a previous instance up todtstartRrecur#next()- cycles forwards through time to find the next instance up tountilorcount
Utility functions
Rrecur.toLocaleISOString(date, [locale])- ouput an ISO string with timezone information2014-06-21T10:00:00.000-0600instead ofSat Jun 21 2014 10:15:08 GMT-0600 (MDT)or2014-06-21T16:00:00.000Z- If
localeis specified in a format such as-04:00orGMT-0400 (EDT), the local (not UTC) time is still used, but the offset is replaced with the supplied locale.
Rrecur.toAdjustedISOString(date, locale)date- A local date object (with the wrong timezone)localeA JavaScript Locale string (or Date string) with the desired timezone- returns a UTC string adjusted to accurately represent the desired timezone
RRULEs
You put in an object like this:
{ "freq": "weekly"
, "until": "2015-01-01T10:30:00.000Z"
, "byday": ["tu", "th"]
, "byhour": [4]
, "byminute": [30]
}freq-yearly|monthly|weekly|daily|hourly|minutely|secondlyinterval- bi-weekly, tri-weekly, etcbymonthbyweeknobyyeardaybymonthdaybyday- 0-7, su,mo,tu,we,th,fr,sa,subyhourbyminutebyseconduntil- seems that this must be given in UTC as per spec, which is weirdcount- how many occurrenceswkst- which day the week starts on 0-7, sa,su,mobysetpos- not sure how this works - http://www.kanzaki.com/docs/ical/recur.htmldtstart- specifies the first event (or a date close to it), non-standard as part of rrule, but is part of ical- If you don't specify
dtstart, the current time will be used. - You cannot get
previous()beforedtstart
- If you don't specify
locale- specifies the locale in the formatGMT-0500 (EST)- non-standard, in generaltzid- specifies the locale, non-standard as part of rrule, but is part of ical
See https://github.com/jkbr/rrule#api for implementation details.
Examples
JSON version of iCal RRULE
{ "freq": "daily"
, "until": "2015-01-01T10:30:00.000Z"
, "byday": ["tu", "th"]
, "byhour": [4]
, "byminute": [30]
}Non-standard iCal directive
(once TZID is implemented, this will be standard)
DTSTART;LOCALE=GMT-0500 (EST):20140616T103000
RRULE:FREQ=DAILY;BYHOUR=10;BYMINUTE=30;BYSECOND=0;UNTIL=20150616T153000ZNon-standard iCal directive (rrule.js-flavored)
DTSTART=20140616T103000Z;FREQ=DAILY;BYHOUR=10;BYMINUTE=30;BYSECOND=0;UNTIL=20150616T153000ZAppendix
Because I needed a place to rant.
Timezones
Timezones are a PAIN! Right!?
We take care of the hard brainwork for you, but just so you know:
Here are the problems:
- JavaScript's native Date object has only two options - Locale and UTC
- The server may have a different locale than the client
rrule.jsonly operates on Locale - the local time of the serverRRULEs are zoneless - 10:30am is meant to be in the user's timeDTSTART;TZID=America/New_Yorkisn't yet supported byrrule.js
Here are solutions:
DTSTARTyou can includeLOCALE(recommended) or use UTC (confusing)UNTILyou must use UTC (confusing, but that's the way it is)- Try
Rrecur.toAdjustedISOString(new Date(2014,06,16,10,30,00), 'GMT-0600 (MDT)')
- Try
RRULEs are zoneless, but we do our best to put them in the right zone.- Everything is calculated in local time under the hood.
Remember:
- Always specify RRULEs in local time (except DTSTART and UNTIL in UTC).
- An RRULE for Monday at 10am will fire well after the sun is risen whether in California or China
- An RRULE for Monday at 10am will have a different UTC conversion in California than in China
dtstartshould always be in UTC or in local time with a localeuntilmust be specified in UTC- all calculations will be done in the local time of the server
