Natural language parsing and deparsing of recurring events

calendar, recurring, date, event, NLP
pip install recurrent==0.4.1



Recurrent is a python library for natural language parsing and formatting of dates and recurring events. It turns strings like "every tuesday and thurs until next month" into RFC-compliant RRULES, to be fed into a calendar api or python-dateutil's rrulestr. It will also accept such rrules and return a natural language representation of them.

pip install recurrent


Date times

  • next tuesday
  • tomorrow
  • in an hour
  • in 15 mins
  • Mar 4th at 9am
  • 3rd Thu in Apr at 10 o'clock
  • 40th day of 2020

Recurring events

  • on weekdays
  • every fourth of the month from jan 1 2010 to dec 25th 2020
  • each thurs until next month
  • once a year on the fourth thursday in november
  • tuesdays and thursdays at 3:15
  • wednesdays at 9 o'clock
  • fridays at 11am
  • daily except in June
  • daily except on June 23rd and July 4th
  • every monday except each 2nd monday in March
  • fridays twice
  • fridays 3x
  • every other friday for 5 times
  • every 3 fridays from november until february
  • fridays starting in may for 10 occurrences
  • tuesdays for the next six weeks
  • every Mon-Wed for the next 2 months
  • every Mon thru Wed for the next year
  • every other Fri for the next three years
  • monthly on the first and last instance of wed and fri
  • every Tue and Fri in week 14
  • every year on Dec 25

Messy strings

  • Please schedule the meeting for every other tuesday at noon
  • Set an alarm for next tuesday at 11pm


>>> import datetime
>>> from recurrent.event_parser import RecurringEvent
>>> r = RecurringEvent(now_date=datetime.datetime(2010, 1, 1))
>>> r.parse('every day starting next tuesday until feb')
>>> r.is_recurring
>>> r.get_params()
{'dtstart': '20100105', 'freq': 'daily', 'interval': 1, 'until': '20100201'}

>>> r.parse('feb 2nd')
datetime.datetime(2010, 2, 2, 0, 0)

>>> r.parse('not a date at all')

>>> r.format('DTSTART:20100105\nRRULE:FREQ=DAILY;INTERVAL=1;UNTIL=20100201')
'daily from Tue Jan 5, 2010 to Mon Feb 1, 2010'
>>> r.format(r.parse('fridays twice'))
'every Fri twice'

You can then use python-dateutil to work with the recurrence rules.

>>> from dateutil import rrule
>>> rr = rrule.rrulestr(r.get_RFC_rrule())
>>> rr.after(datetime.datetime(2010, 1, 2))
datetime.datetime(2010, 1, 5, 0, 0)
>>> rr.after(datetime.datetime(2010, 1, 25))
datetime.datetime(2010, 1, 26, 0, 0)

You can specify a (custom) localisation to change the parsing behaviour of parsedatetime

consts = parsedatetime.Constants(localeID='en_US', usePyICU=False)
consts.use24 = True

r = RecurringEvent(now_date=datetime.datetime(2010, 1, 1), parse_constants=consts)


Recurrent uses parsedatetime to parse dates and python.dateutil if available to optimize some results.

Things it can't do

Recurrent is regrettably quite U.S. (and completely english) centric. Contributions from other perspectives are welcome :)


Recurrent is inspired by the similar Ruby library Tickle by Joshua Lippiner. It also uses the parsedatetime library for fuzzy human date parsing. The handling of COUNT, BYSETPOS, BYWEEKNO, EXDATE and EXRULE, and the format function was supplied by Joe Cool


Ken Van Haren @squaredloss