diff --git a/product/ERP5/mixin/periodicity.py b/product/ERP5/mixin/periodicity.py
new file mode 100644
index 0000000000000000000000000000000000000000..7fd503162b8ac1370a54db99d1104a10279abff3
--- /dev/null
+++ b/product/ERP5/mixin/periodicity.py
@@ -0,0 +1,207 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2004,2007,2009 Nexedi SA and Contributors. All Rights Reserved.
+#                    Sebastien Robin <seb@nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+##############################################################################
+
+from DateTime import DateTime
+from Products.ERP5Type.Message import Message
+from Products.ERP5Type.DateUtils import addToDate, atTheEndOfPeriod
+
+class PeriodicityMixin:
+  """
+  Periodicity is a Mixin Class used to calculate date periodicity.
+  """
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.AccessContentsInformation)
+
+  def _validateMinute(self, date, previous_date):
+    """
+    Validate if date's minute matches the periodicity definition
+    """
+    periodicity_minute_frequency = self.getPeriodicityMinuteFrequency()
+    periodicity_minute_list = self.getPeriodicityMinuteList()
+    if (periodicity_minute_frequency is None) and \
+       (periodicity_minute_list in ([], None, ())):
+      # in this case, we may want to have an periodicity every hour 
+      # based on the start date
+      # without defining anything about minutes periodicity, 
+      # so we compare with minutes with the one defined 
+      # in the previous alarm date
+      return (date.minute() == previous_date.minute())
+    if periodicity_minute_frequency not in ('', None):
+      return (date.minute() % periodicity_minute_frequency) == 0
+    elif len(periodicity_minute_list) > 0:
+      return date.minute() in periodicity_minute_list
+
+  def _validateHour(self, date):
+    """
+    Validate if date's hour matches the periodicity definition
+    """
+    periodicity_hour_frequency = self.getPeriodicityHourFrequency()
+    periodicity_hour_list = self.getPeriodicityHourList()
+    if (periodicity_hour_frequency is None) and \
+       (periodicity_hour_list in ([], None, ())):
+      return 1
+    if periodicity_hour_frequency not in ('', None):
+      return (date.hour() % periodicity_hour_frequency) == 0
+    elif len(periodicity_hour_list) > 0:
+      return date.hour() in periodicity_hour_list
+
+  def _validateDay(self, date):
+    """
+    Validate if date's day matches the periodicity definition
+    """
+    periodicity_day_frequency = self.getPeriodicityDayFrequency()
+    periodicity_month_day_list = self.getPeriodicityMonthDayList()
+    if (periodicity_day_frequency is None) and \
+       (periodicity_month_day_list in ([], None, ())):
+      return 1
+    if periodicity_day_frequency not in ('', None):
+      return (date.day() % periodicity_day_frequency) == 0
+    elif len(periodicity_month_day_list) > 0:
+      return date.day() in periodicity_month_day_list
+
+  def _validateWeek(self, date):
+    """
+    Validate if date's week matches the periodicity definition
+    """
+    periodicity_week_frequency = self.getPeriodicityWeekFrequency()
+    periodicity_week_day_list = self.getPeriodicityWeekDayList()
+    periodicity_week_list = self.getPeriodicityWeekList()
+    if (periodicity_week_frequency is None) and \
+       (periodicity_week_day_list in ([], None, ())) and \
+       (periodicity_week_list is None):
+      return 1
+    if periodicity_week_frequency not in ('', None):
+      if not((date.week() % periodicity_week_frequency) == 0):
+        return 0
+    if periodicity_week_day_list not in (None, (), []):
+      if not (date.Day() in periodicity_week_day_list):
+        return 0
+    if periodicity_week_list not in (None, (), []):
+      if not (date.week() in periodicity_week_list):
+        return 0
+    return 1
+
+  def _validateMonth(self, date):
+    """
+    Validate if date's month matches the periodicity definition
+    """
+    periodicity_month_frequency = self.getPeriodicityMonthFrequency()
+    periodicity_month_list = self.getPeriodicityMonthList()
+    if (periodicity_month_frequency is None) and \
+       (periodicity_month_list in ([], None, ())):
+      return 1
+    if periodicity_month_frequency not in ('', None):
+      return (date.month() % periodicity_month_frequency) == 0
+    elif len(periodicity_month_list) > 0:
+      return date.month() in periodicity_month_list
+
+  security.declareProtected(Permissions.AccessContentsInformation, 'getNextPeriodicalDate')
+  def getNextPeriodicalDate(self, current_date, next_start_date=None):
+    """
+    Get the next date where this periodic event should start.
+
+    We have to take into account the start date, because
+    sometimes an event may be started by hand. We must be
+    sure to never forget to start an event, even with some
+    delays.
+
+    Here are some rules :
+    - if the periodicity start date is in the past and we never starts
+      this periodic event, then return the periodicity start date.
+    - if the periodicity start date is in the past but we already
+      have started the periodic event, then see
+
+    XXX Better API is needed. It may defined which minimal time duration has to 
+    be added in order to calculate next date.
+    Ex: here, we use minute as smaller duration.
+    On CalendarPeriod, day is the smaller duration.
+    """
+    periodicity_stop_date = self.getPeriodicityStopDate()
+    if next_start_date is None:
+      next_start_date = current_date
+    if next_start_date > current_date \
+          or (periodicity_stop_date is not None \
+              and next_start_date >= periodicity_stop_date):
+      return None
+
+    previous_date = next_start_date
+    next_start_date = max(addToDate(next_start_date, minute=1), current_date)
+    while 1:
+      if not self._validateMonth(next_start_date):
+        next_start_date = atTheEndOfPeriod(next_start_date, 'month')
+      elif not (self._validateDay(next_start_date) and
+                self._validateWeek(next_start_date)):
+        next_start_date = atTheEndOfPeriod(next_start_date, 'day')
+      elif not self._validateMinute(next_start_date, previous_date):
+        next_start_date = addToDate(next_start_date, minute=1)
+      elif not self._validateHour(next_start_date):
+        next_start_date = addToDate(next_start_date, hour=1)
+      else:
+        parts = list(next_start_date.parts())
+        parts[5] = previous_date.second() # XXX keep old behaviour
+        return DateTime(*parts)
+
+  # XXX May be we should create a Date class for following methods ???
+  security.declareProtected(Permissions.AccessContentsInformation, 'getWeekDayList')
+  def getWeekDayList(self):
+    """
+    returns something like ['Sunday','Monday',...]
+    """
+    return DateTime._days
+
+  security.declareProtected(Permissions.AccessContentsInformation, 'getWeekDayItemList')
+  def getWeekDayItemList(self):
+    """
+    returns something like [('Sunday', 'Sunday'), ('Monday', 'Monday'),...]
+    """
+    return [(Message(domain='erp5_ui', message=x), x) \
+            for x in self.getWeekDayList()]
+
+  security.declareProtected(Permissions.AccessContentsInformation, 'getWeekDayItemList')
+  def getMonthItemList(self):
+    """
+    returns something like [('January', 1), ('February', 2),...]
+    """
+    # DateTime._months return '' as first item
+    return [(Message(domain='erp5_ui', message=DateTime._months[i]), i) \
+            for i in range(1, len(DateTime._months))]
+
+  security.declareProtected(Permissions.AccessContentsInformation,'getPeriodicityWeekDayList')
+  def getPeriodicityWeekDayList(self):
+    """
+    Make sure that the list of days is ordered
+    """
+    #LOG('getPeriodicityWeekDayList',0,'we should order')
+    day_list = self._baseGetPeriodicityWeekDayList()
+    new_list = []
+    for day in self.getWeekDayList():
+      if day_list is not None:
+        if day in day_list:
+          new_list += [day]
+    return new_list