diff --git a/Makefile.am b/Makefile.am
index 88662c0..fb3fe7c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2912,6 +2912,16 @@ EXTRA_DIST += \
 	units/systemd-journal-gatewayd.service.in
 
 # ------------------------------------------------------------------------------
+if HAVE_ICAL
+libsystemd_shared_la_SOURCES += \
+	src/shared/recurrencespec.c \
+	src/shared/recurrencespec.h
+
+libsystemd_shared_la_LIBADD += \
+	$(ICAL_LIBS)
+endif
+
+# ------------------------------------------------------------------------------
 if ENABLE_COREDUMP
 systemd_coredump_SOURCES = \
 	src/journal/coredump.c
diff --git a/configure.ac b/configure.ac
index d94af7b..290065f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -534,6 +534,18 @@ fi
 AM_CONDITIONAL(HAVE_MICROHTTPD, [test "$have_microhttpd" = "yes"])
 
 # ------------------------------------------------------------------------------
+have_ical=no
+AC_ARG_ENABLE(ical, AS_HELP_STRING([--disable-ical], [disable ical support]))
+if test "x$enable_ical" != "xno"; then
+        PKG_CHECK_MODULES(ICAL, [libical >= 0.48],
+                [AC_DEFINE(HAVE_ICAL, 1, [Define if ical is available]) have_ical=yes], have_ical=no)
+        if test "x$have_ical" = xno -a "x$enable_ical" = xyes; then
+                AC_MSG_ERROR([*** ical support requested but libraries not found])
+        fi
+fi
+AM_CONDITIONAL(HAVE_ICAL, [test "$have_ical" = "yes"])
+
+# ------------------------------------------------------------------------------
 have_binfmt=no
 AC_ARG_ENABLE(binfmt, AS_HELP_STRING([--disable-binfmt], [disable binfmt tool]))
 if test "x$enable_binfmt" != "xno"; then
@@ -849,6 +861,7 @@ AC_MSG_RESULT([
         GCRYPT:                  ${have_gcrypt}
         QRENCODE:                ${have_qrencode}
         MICROHTTPD:              ${have_microhttpd}
+        ICAL:                    ${have_ical}
         CHKCONFIG:               ${have_chkconfig}
         binfmt:                  ${have_binfmt}
         vconsole:                ${have_vconsole}
diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c
index 75add81..2385d8a 100644
--- a/src/core/dbus-timer.c
+++ b/src/core/dbus-timer.c
@@ -78,7 +78,7 @@ static int bus_timer_append_monotonic_timers(DBusMessageIter *i, const char *pro
                 size_t l;
                 bool b;
 
-                if (k->base == TIMER_CALENDAR)
+                if (k->base == TIMER_CALENDAR || k->base == TIMER_RECURRENCE)
                         continue;
 
                 t = timer_base_to_string(k->base);
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 8436d4f..19631f8 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -1157,6 +1157,9 @@ int config_parse_timer(
         TimerValue *v;
         TimerBase b;
         CalendarSpec *c = NULL;
+#ifdef HAVE_ICAL
+        RecurrenceSpec *r = NULL;
+#endif
         clockid_t id;
 
         assert(filename);
@@ -1176,6 +1179,16 @@ int config_parse_timer(
                 return 0;
         }
 
+#ifdef HAVE_ICAL
+        if (b == TIMER_RECURRENCE) {
+                if (recurrence_spec_from_string(rvalue, &r) < 0) {
+                        log_error("[%s:%u] Failed to parse recurrence specification, ignoring: %s", filename, line, rvalue);
+                        return 0;
+                }
+
+                id = CLOCK_REALTIME;
+        } else
+#endif
         if (b == TIMER_CALENDAR) {
                 if (calendar_spec_from_string(rvalue, &c) < 0) {
                         log_error("[%s:%u] Failed to parse calendar specification, ignoring: %s", filename, line, rvalue);
diff --git a/src/core/timer.c b/src/core/timer.c
index 8061f79..dce4e52 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -160,6 +160,19 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
 
         LIST_FOREACH(value, v, t->values) {
 
+#ifdef HAVE_ICAL
+                if (v->base == TIMER_RECURRENCE) {
+                        _cleanup_free_ char *p = NULL;
+
+                        recurrence_spec_to_string(v->recurrence_spec, &p);
+
+                        fprintf(f,
+                                "%s%s: %s\n",
+                                prefix,
+                                timer_base_to_string(v->base),
+                                strna(p));
+                } else
+#endif
                 if (v->base == TIMER_CALENDAR) {
                         _cleanup_free_ char *p = NULL;
 
@@ -170,7 +183,7 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
                                 prefix,
                                 timer_base_to_string(v->base),
                                 strna(p));
-                } else  {
+                } else {
                         char timespan1[FORMAT_TIMESPAN_MAX];
 
                         fprintf(f,
@@ -246,9 +259,14 @@ static void timer_enter_waiting(Timer *t, bool initial) {
                 if (v->disabled)
                         continue;
 
-                if (v->base == TIMER_CALENDAR) {
+                if (v->base == TIMER_RECURRENCE || v->base == TIMER_CALENDAR) {
 
-                        r = calendar_spec_next_usec(v->calendar_spec, ts.realtime, &v->next_elapse);
+#ifdef HAVE_ICAL
+                        if (v->base == TIMER_RECURRENCE)
+                            r = recurrence_spec_next_usec(v->recurrence_spec, ts.realtime, &v->next_elapse);
+                        else
+#endif
+                            r = calendar_spec_next_usec(v->calendar_spec, ts.realtime, &v->next_elapse);
                         if (r < 0)
                                 continue;
 
@@ -576,6 +594,9 @@ static const char* const timer_base_table[_TIMER_BASE_MAX] = {
         [TIMER_STARTUP] = "OnStartupSec",
         [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
         [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
+#ifdef HAVE_ICAL
+        [TIMER_RECURRENCE] = "Recurrence",
+#endif
         [TIMER_CALENDAR] = "OnCalendar"
 };
 
diff --git a/src/core/timer.h b/src/core/timer.h
index c145348..b5c4432 100644
--- a/src/core/timer.h
+++ b/src/core/timer.h
@@ -26,6 +26,10 @@ typedef struct Timer Timer;
 #include "unit.h"
 #include "calendarspec.h"
 
+#ifdef HAVE_ICAL
+#include "recurrencespec.h"
+#endif
+
 typedef enum TimerState {
         TIMER_DEAD,
         TIMER_WAITING,
@@ -43,6 +47,7 @@ typedef enum TimerBase {
         TIMER_UNIT_ACTIVE,
         TIMER_UNIT_INACTIVE,
         TIMER_CALENDAR,
+        TIMER_RECURRENCE,
         _TIMER_BASE_MAX,
         _TIMER_BASE_INVALID = -1
 } TimerBase;
@@ -54,6 +59,9 @@ typedef struct TimerValue {
 
         usec_t value; /* only for monotonic events */
         CalendarSpec *calendar_spec; /* only for calendar events */
+#ifdef HAVE_ICAL
+        RecurrenceSpec *recurrence_spec; /* only for iCal recurrence events */
+#endif
         usec_t next_elapse;
 
         LIST_FIELDS(struct TimerValue, value);
diff --git a/src/shared/recurrencespec.c b/src/shared/recurrencespec.c
new file mode 100644
index 0000000..678e1d6
--- /dev/null
+++ b/src/shared/recurrencespec.c
@@ -0,0 +1,108 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2013 David Strauss
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <string.h>
+#include <libical/ical.h>
+
+#include "recurrencespec.h"
+
+void recurrence_spec_free(RecurrenceSpec *c) {
+        assert(c);
+        free(c);
+}
+
+int recurrence_spec_normalize(RecurrenceSpec *c) {
+        assert(c);
+        return 0;
+}
+
+bool recurrence_spec_valid(RecurrenceSpec *c) {
+        assert(c);
+        return true;
+}
+
+int recurrence_spec_to_string(const RecurrenceSpec *c, char **p) {
+        char *buf = NULL;
+        RecurrenceSpec r;
+
+        assert(c);
+        assert(p);
+
+        r = *c;
+        buf = icalrecurrencetype_as_string_r(&r);
+        *p = buf;
+        return 0;
+}
+
+int recurrence_spec_from_string(const char *p, RecurrenceSpec **spec) {
+        RecurrenceSpec *c;
+        int r;
+
+        assert(p);
+        assert(spec);
+
+        if (isempty(p))
+                return -EINVAL;
+
+        c = new0(RecurrenceSpec, 1);
+        if (!c)
+                return -ENOMEM;
+
+        *c = icalrecurrencetype_from_string(p);
+
+        if (icalerrno != ICAL_NO_ERROR) {
+                r = -EINVAL;
+                goto fail;
+        }
+
+        *spec = c;
+        return 0;
+
+fail:
+        recurrence_spec_free(c);
+        return r;
+}
+
+int recurrence_spec_next_usec(const RecurrenceSpec *spec, usec_t usec, usec_t *next) {
+        time_t t, start;
+        struct icaltimetype icstart, next_event;
+        icalrecur_iterator *ritr;
+
+        assert(spec);
+        assert(next);
+
+        start = time(NULL);
+        icstart = icaltime_from_timet_with_zone(start, 0, 0);
+        ritr = icalrecur_iterator_new(*spec, icstart);
+        if(ritr) {
+                for (next_event = icalrecur_iterator_next(ritr);
+                     !icaltime_is_null_time(next_event);
+                     next_event = icalrecur_iterator_next(ritr)) {
+                        t = icaltime_as_timet(next_event);
+                        if (t >= start)
+                                break;
+                }
+        }
+
+        *next = (usec_t) t * USEC_PER_SEC;
+        return 0;
+}
diff --git a/src/shared/recurrencespec.h b/src/shared/recurrencespec.h
new file mode 100644
index 0000000..2fd3da1
--- /dev/null
+++ b/src/shared/recurrencespec.h
@@ -0,0 +1,38 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2013 David Strauss
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <libical/ical.h>
+#include "util.h"
+
+typedef struct icalrecurrencetype RecurrenceSpec;
+
+void recurrence_spec_free(RecurrenceSpec *c);
+
+int recurrence_spec_normalize(RecurrenceSpec *spec);
+bool recurrence_spec_valid(RecurrenceSpec *spec);
+
+int recurrence_spec_to_string(const RecurrenceSpec *spec, char **p);
+int recurrence_spec_from_string(const char *p, RecurrenceSpec **spec);
+
+int recurrence_spec_next_usec(const RecurrenceSpec *spec, usec_t usec, usec_t *next);
