Commit 844918e8 authored by Julien Muchembled's avatar Julien Muchembled

Fix DateUtils.atTheEndOfPeriod and an infinite loop in some timezones

Contrary to Europe/Paris, Zope performs automatic DST calculation in US/Eastern
timezone, which caused infinite loop in Alarm.getNextPeriodicalDate.

Reenable testOpenOrder.testPeriodicityDateList (still failing though),
and create a testOpenOrder.testPeriodicityDateListUniversal to show that it
works with UTC times.

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@31657 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 30712e8e
...@@ -35,7 +35,7 @@ from Products.ERP5Type.XMLObject import XMLObject ...@@ -35,7 +35,7 @@ from Products.ERP5Type.XMLObject import XMLObject
from Acquisition import aq_base from Acquisition import aq_base
from DateTime import DateTime from DateTime import DateTime
from Products.ERP5Type.Message import Message from Products.ERP5Type.Message import Message
from Products.ERP5Type.DateUtils import addToDate from Products.ERP5Type.DateUtils import addToDate, atTheEndOfPeriod
from Products.ERP5Security.ERP5UserManager import SUPER_USER from Products.ERP5Security.ERP5UserManager import SUPER_USER
from AccessControl.SecurityManagement import getSecurityManager, \ from AccessControl.SecurityManagement import getSecurityManager, \
setSecurityManager, newSecurityManager setSecurityManager, newSecurityManager
...@@ -158,40 +158,23 @@ class PeriodicityMixin: ...@@ -158,40 +158,23 @@ class PeriodicityMixin:
or (periodicity_stop_date is not None \ or (periodicity_stop_date is not None \
and next_start_date >= periodicity_stop_date): and next_start_date >= periodicity_stop_date):
return None return None
else:
# Make sure the old date is not too far away
day_count = int(current_date - next_start_date)
next_start_date = next_start_date + day_count
previous_date = next_start_date previous_date = next_start_date
next_start_date = addToDate(next_start_date, minute=1) next_start_date = max(addToDate(next_start_date, minute=1), current_date)
while 1: while 1:
validate_minute = self._validateMinute(next_start_date, previous_date) if not self._validateMonth(next_start_date):
validate_hour = self._validateHour(next_start_date) next_start_date = atTheEndOfPeriod(next_start_date, 'month')
validate_day = self._validateDay(next_start_date) elif not (self._validateDay(next_start_date) and
validate_week = self._validateWeek(next_start_date) self._validateWeek(next_start_date)):
validate_month = self._validateMonth(next_start_date) next_start_date = atTheEndOfPeriod(next_start_date, 'day')
if (next_start_date >= current_date \ elif not self._validateMinute(next_start_date, previous_date):
and validate_minute and validate_hour and validate_day \
and validate_week and validate_month):
break
else:
if not(validate_minute):
next_start_date = addToDate(next_start_date, minute=1) next_start_date = addToDate(next_start_date, minute=1)
else: elif not self._validateHour(next_start_date):
if not(validate_hour):
next_start_date = addToDate(next_start_date, hour=1) next_start_date = addToDate(next_start_date, hour=1)
else: else:
if not(validate_day and validate_week and validate_month): parts = list(next_start_date.parts())
# We have to reset hours and minutes in order to make sure parts[5] = previous_date.second() # XXX keep old behaviour
# we will start at the beginning of the next day return DateTime(*parts)
next_start_date = DateTime(next_start_date.Date() + ' 00:00:00 %s' % next_start_date.timezone())
next_start_date = addToDate(next_start_date, day=1)
else:
# Everything is right, but the date is still not bigger
# than the current date, so we must continue
next_start_date = addToDate(next_start_date, minute=1)
return next_start_date
# XXX May be we should create a Date class for following methods ??? # XXX May be we should create a Date class for following methods ???
security.declareProtected(Permissions.AccessContentsInformation, 'getWeekDayList') security.declareProtected(Permissions.AccessContentsInformation, 'getWeekDayList')
......
...@@ -172,59 +172,64 @@ class TestOpenOrder(ERP5TypeTestCase): ...@@ -172,59 +172,64 @@ class TestOpenOrder(ERP5TypeTestCase):
transaction.commit() transaction.commit()
self.tic() self.tic()
def testPeriodicityDateList(self): def testPeriodicityDateList(self, timezone=None):
""" """
Make sure that periodicity line can generate correct schedule. Make sure that periodicity line can generate correct schedule.
""" """
self.fail('Test disabled because it freezes') #self.fail('Test disabled because it freezes')
def D(yr, mo, dy, hr=0, mn=0, sc=0):
return DateTime(yr, mo, dy, hr, mn, sc, timezone)
# This across Summer time period, if server's timezone uses it. # This across Summer time period, if server's timezone uses it.
self.assertEqual(self.portal.sale_trade_condition_module.main_trade_condition.internet_connection_periodicity_line.getDatePeriodList( self.assertEqual(self.portal.sale_trade_condition_module.main_trade_condition.internet_connection_periodicity_line.getDatePeriodList(
DateTime(2008,1,15), DateTime(2008,12,1)), D(2008,1,15), D(2008,12,1)),
[(DateTime(2008,2,1,0,1), DateTime(2008,2,29)), [(D(2008,2,1,0,1), DateTime(2008,2,29)),
(DateTime(2008,3,1,0,1), DateTime(2008,3,31)), (D(2008,3,1,0,1), DateTime(2008,3,31)),
(DateTime(2008,4,1,0,1), DateTime(2008,4,30)), (D(2008,4,1,0,1), DateTime(2008,4,30)),
(DateTime(2008,5,1,0,1), DateTime(2008,5,31)), (D(2008,5,1,0,1), DateTime(2008,5,31)),
(DateTime(2008,6,1,0,1), DateTime(2008,6,30)), (D(2008,6,1,0,1), DateTime(2008,6,30)),
(DateTime(2008,7,1,0,1), DateTime(2008,7,31)), (D(2008,7,1,0,1), DateTime(2008,7,31)),
(DateTime(2008,8,1,0,1), DateTime(2008,8,31)), (D(2008,8,1,0,1), DateTime(2008,8,31)),
(DateTime(2008,9,1,0,1), DateTime(2008,9,30)), (D(2008,9,1,0,1), DateTime(2008,9,30)),
(DateTime(2008,10,1,0,1), DateTime(2008,10,31)), (D(2008,10,1,0,1), DateTime(2008,10,31)),
(DateTime(2008,11,1,0,1), DateTime(2008,11,30)), (D(2008,11,1,0,1), DateTime(2008,11,30)),
]) ])
self.assertEqual(self.portal.sale_trade_condition_module.main_trade_condition.bread_periodicity_line.getDatePeriodList( self.assertEqual(self.portal.sale_trade_condition_module.main_trade_condition.bread_periodicity_line.getDatePeriodList(
DateTime(2008,2,26), DateTime(2008,3,5)), D(2008,2,26), D(2008,3,5)),
[(DateTime(2008,2,26,6,0), DateTime(2008,2,26,6,0)), [(D(2008,2,26,6,0), D(2008,2,26,6,0)),
(DateTime(2008,2,26,12,0), DateTime(2008,2,26,12,0)), (D(2008,2,26,12,0), D(2008,2,26,12,0)),
(DateTime(2008,2,27,6,0), DateTime(2008,2,27,6,0)), (D(2008,2,27,6,0), D(2008,2,27,6,0)),
(DateTime(2008,2,27,12,0), DateTime(2008,2,27,12,0)), (D(2008,2,27,12,0), D(2008,2,27,12,0)),
(DateTime(2008,2,28,6,0), DateTime(2008,2,28,6,0)), (D(2008,2,28,6,0), D(2008,2,28,6,0)),
(DateTime(2008,2,28,12,0), DateTime(2008,2,28,12,0)), (D(2008,2,28,12,0), D(2008,2,28,12,0)),
(DateTime(2008,2,29,6,0), DateTime(2008,2,29,6,0)), (D(2008,2,29,6,0), D(2008,2,29,6,0)),
(DateTime(2008,2,29,12,0), DateTime(2008,2,29,12,0)), (D(2008,2,29,12,0), D(2008,2,29,12,0)),
(DateTime(2008,3,1,6,0), DateTime(2008,3,1,6,0)), (D(2008,3,1,6,0), D(2008,3,1,6,0)),
(DateTime(2008,3,1,12,0), DateTime(2008,3,1,12,0)), (D(2008,3,1,12,0), D(2008,3,1,12,0)),
(DateTime(2008,3,3,6,0), DateTime(2008,3,3,6,0)), (D(2008,3,3,6,0), D(2008,3,3,6,0)),
(DateTime(2008,3,3,12,0), DateTime(2008,3,3,12,0)), (D(2008,3,3,12,0), D(2008,3,3,12,0)),
(DateTime(2008,3,4,6,0), DateTime(2008,3,4,6,0)), (D(2008,3,4,6,0), D(2008,3,4,6,0)),
(DateTime(2008,3,4,12,0), DateTime(2008,3,4,12,0)), (D(2008,3,4,12,0), D(2008,3,4,12,0)),
]) ])
self.assertEqual(self.portal.sale_trade_condition_module.main_trade_condition.water_periodicity_line.getDatePeriodList( self.assertEqual(self.portal.sale_trade_condition_module.main_trade_condition.water_periodicity_line.getDatePeriodList(
DateTime(2008,2,16), DateTime(2008,4,15)), D(2008,2,16), D(2008,4,15)),
[(DateTime(2008,2,18,10,0), DateTime(2008,3,3,10,0)), [(D(2008,2,18,10,0), D(2008,3,3,10,0)),
(DateTime(2008,3,3,10,0), DateTime(2008,3,17,10,0)), (D(2008,3,3,10,0), D(2008,3,17,10,0)),
(DateTime(2008,3,17,10,0), DateTime(2008,3,31,10,0)), (D(2008,3,17,10,0), D(2008,3,31,10,0)),
(DateTime(2008,3,31,10,0), DateTime(2008,4,14,10,0)), (D(2008,3,31,10,0), D(2008,4,14,10,0)),
(DateTime(2008,4,14,10,0), DateTime(2008,4,28,10,0)), (D(2008,4,14,10,0), D(2008,4,28,10,0)),
]) ])
self.assertEqual(self.portal.sale_trade_condition_module.main_trade_condition.training_periodicity_line.getDatePeriodList( self.assertEqual(self.portal.sale_trade_condition_module.main_trade_condition.training_periodicity_line.getDatePeriodList(
DateTime(2008,2,16), DateTime(2008,3,6)), D(2008,2,16), D(2008,3,6)),
[(DateTime(2008,2,18,10,0), DateTime(2008,2,19,10,0)), [(D(2008,2,18,10,0), D(2008,2,19,10,0)),
(DateTime(2008,2,25,10,0), DateTime(2008,2,26,10,0)), (D(2008,2,25,10,0), D(2008,2,26,10,0)),
(DateTime(2008,3,3,10,0), DateTime(2008,3,4,10,0)), (D(2008,3,3,10,0), D(2008,3,4,10,0)),
]) ])
def testPeriodicityDateListUniversal(self):
self.testPeriodicityDateList('Universal')
def testOpenOrderRule(self): def testOpenOrderRule(self):
""" """
Make sure that Open Order Rule can generate simulation movements by Make sure that Open Order Rule can generate simulation movements by
......
...@@ -506,21 +506,16 @@ def atTheEndOfPeriod(date, period): ...@@ -506,21 +506,16 @@ def atTheEndOfPeriod(date, period):
If timezone is Universal, strftime('%Z') return empty string If timezone is Universal, strftime('%Z') return empty string
and TimeZone is replaced by local zone, and TimeZone is replaced by local zone,
so date formating is manualy rendered. so date formating is manualy rendered.
XXXSunday is hardcoded
""" """
if period == 'year': if period == 'year':
end = addToDate(DateTime('%s/01/01 00:00:00 %s' % (date.year(), date.timezone())), **{period:1}) end = addToDate(DateTime('%s/01/01 00:00:00 %s' % (date.year(), date.timezone())), **{period:1})
elif period == 'month': elif period == 'month':
end = addToDate(DateTime('%s/%s/01 00:00:00 %s' % (date.year(), zfill(date.month(), 2), date.timezone())), **{period:1}) end = addToDate(DateTime('%s/%s/01 00:00:00 %s' % (date.year(), zfill(date.month(), 2), date.timezone())), **{period:1})
elif period == 'day': elif period == 'day':
end = addToDate(DateTime('%s/%s/%s 00:00:00 %s' % (date.year(), zfill(date.month(), 2), zfill(date.day(), 2), date.timezone())), **{period:1}) end = addToDate(date.earliestTime(), hour=36).earliestTime()
elif period == 'week': elif period == 'week':
day_of_week = date.strftime('%A') end = atTheEndOfPeriod(date, 'day')
end = DateTime('%s/%s/%s 00:00:00 %s' % (date.year(), zfill(date.month(), 2), zfill(date.day(), 2), date.timezone())) end = addToDate(end, day=(1-end.dow()) % 7)
while day_of_week != 'Sunday':
end = addToDate(end, day=1)
day_of_week = end.strftime('%A')
end = addToDate(end, day=1)
else: else:
raise NotImplementedError, 'Period "%s" not Handled yet' % period raise NotImplementedError, 'Period "%s" not Handled yet' % period
return end return end
...@@ -167,6 +167,24 @@ class TestDateUtils(unittest.TestCase): ...@@ -167,6 +167,24 @@ class TestDateUtils(unittest.TestCase):
self.assertEqual(atTheEndOfPeriod(date, 'month').pCommonZ(), 'Feb. 1, 2008 12:00 am Universal') self.assertEqual(atTheEndOfPeriod(date, 'month').pCommonZ(), 'Feb. 1, 2008 12:00 am Universal')
self.assertEqual(atTheEndOfPeriod(date, 'week').pCommonZ(), 'Jan. 7, 2008 12:00 am Universal') self.assertEqual(atTheEndOfPeriod(date, 'week').pCommonZ(), 'Jan. 7, 2008 12:00 am Universal')
self.assertEqual(atTheEndOfPeriod(date, 'day').pCommonZ(), 'Jan. 2, 2008 12:00 am Universal') self.assertEqual(atTheEndOfPeriod(date, 'day').pCommonZ(), 'Jan. 2, 2008 12:00 am Universal')
# Switch to summer time
self.assertEqual('Apr. 6, 2008 12:00 am US/Eastern',
atTheEndOfPeriod(DateTime('2008/04/05 23:59:59 US/Eastern'), 'day').pCommonZ())
self.assertEqual('Apr. 7, 2008 12:00 am US/Eastern',
atTheEndOfPeriod(DateTime('2008/04/06 00:00:00 US/Eastern'), 'day').pCommonZ())
self.assertEqual('Apr. 7, 2008 12:00 am US/Eastern',
atTheEndOfPeriod(DateTime('2008/04/06 23:59:59 US/Eastern'), 'day').pCommonZ())
self.assertEqual('May 1, 2008 12:00 am US/Eastern',
atTheEndOfPeriod(DateTime('2008/04/01 US/Eastern'), 'month').pCommonZ())
# Switch to winter time
self.assertEqual('Oct. 26, 2008 12:00 am US/Eastern',
atTheEndOfPeriod(DateTime('2008/10/25 23:59:59 US/Eastern'), 'day').pCommonZ())
self.assertEqual('Oct. 27, 2008 12:00 am US/Eastern',
atTheEndOfPeriod(DateTime('2008/10/26 00:00:00 US/Eastern'), 'day').pCommonZ())
self.assertEqual('Oct. 27, 2008 12:00 am US/Eastern',
atTheEndOfPeriod(DateTime('2008/10/26 23:59:59 US/Eastern'), 'day').pCommonZ())
self.assertEqual('Nov. 1, 2008 12:00 am US/Eastern',
atTheEndOfPeriod(DateTime('2008/10/01 US/Eastern'), 'month').pCommonZ())
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment