From 066d31ae0a6535df8518e87bd63febb033299620 Mon Sep 17 00:00:00 2001
From: Nicolas Delaby <nicolas@nexedi.com>
Date: Thu, 7 Feb 2008 18:10:58 +0000
Subject: [PATCH] Improve addToDate function with float handling

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@19146 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5Type/DateUtils.py | 83 ++++++++++++++++++++++++++++-------
 1 file changed, 66 insertions(+), 17 deletions(-)

diff --git a/product/ERP5Type/DateUtils.py b/product/ERP5Type/DateUtils.py
index 904408ebad..3802ae14bd 100644
--- a/product/ERP5Type/DateUtils.py
+++ b/product/ERP5Type/DateUtils.py
@@ -40,7 +40,7 @@ security.declarePublic('addToDate', 'getClosestDate',
     'getMonthFraction', 'getYearFraction', 'getAccountableYearFraction',
     'getBissextilCompliantYearFraction',
     'getDecimalNumberOfYearsBetween','roundMonthToGreaterEntireYear',
-    'roundDate', 'convertDateToHour')
+    'roundDate', 'convertDateToHour', 'getNumberOfDayInMonth')
 
 millis = DateTime('2000/01/01 12:00:00.001') - DateTime('2000/01/01 12:00:00')
 centis = millis * 10
@@ -51,7 +51,7 @@ number_of_seconds_in_minute = 60.
 number_of_days_in_year = 365.
 hour = 1/24.
 same_movement_interval = hour
-  
+
 accountable_days_in_month = 30.
 accountable_months_in_year = 12.
 number_of_hours_in_year  = 8760
@@ -65,30 +65,64 @@ def addToDate(date, to_add=None, **kw):
   if to_add is not None:
     kw.update(to_add)
   to_add = kw
-  for key in ('year', 'month', 'day', 'hour', 'minute', 'second'):
+  key_list = ('second', 'minute', 'hour', 'day', 'month', 'year')
+  reverse_key_list = key_list[::-1]
+  for key in reverse_key_list:
     method = getattr(date, key)
     return_value[key] = method()
-  larger_key_dict = { 'second':'minute', 'minute':'hour', 
-                      'hour':'day', 'month':'year' }
+  larger_key_dict = { 'second':'minute',
+                      'minute':'hour',
+                      'hour'  :'day',
+                      'month' :'year' }
   number_of_in_dict = { 'second' : number_of_seconds_in_minute,
                         'minute' : number_of_minutes_in_hour,
                         'hour'   : number_of_hours_in_day,
+                        'day'    : getNumberOfDayInMonth(date),
                         'month'  : number_of_months_in_year }
-              
-  for key in ('second', 'minute', 'hour', 'month'):
-    if to_add.get(key, None) is not None:
-      return_value[key] = return_value[key] + to_add[key]
-    while (key == 'month' and return_value[key] <= 0) or \
-          (key != 'month' and return_value[key] < 0):
+  lesser_key_dict = {'minute':'second',
+                     'hour'  :'minute',
+                     'day'   :'hour',
+                     'month' :'day',
+                     'year'  :'month'}
+  number_less_of_in_dict = {'minute' : number_of_seconds_in_minute,
+                            'hour'   : number_of_minutes_in_hour,
+                            'day'    : number_of_hours_in_day,
+                            'month'  : getNumberOfDayInMonth(date),
+                            'year'   : number_of_months_in_year}
+
+  def treatNegativeValues(return_value, key):
+    while key != 'day' and (key == 'month' and return_value[key] <= 0) or \
+            (key != 'month' and return_value[key] < 0):
       return_value[key] = return_value[key] + number_of_in_dict[key]
-      return_value[ larger_key_dict[key] ] = return_value[ larger_key_dict[key] ] - 1
-    while (key == 'month' and return_value[key] > number_of_in_dict[key]) or \
-          (key != 'month' and return_value[key] >= number_of_in_dict[key]):
+      return_value[larger_key_dict[key]] = return_value[larger_key_dict[key]] - 1
+
+  def treatPositiveValues(return_value, key):
+    while key != 'day' and (key == 'month' and return_value[key] >\
+       number_of_in_dict[key]) or (key != 'month' and return_value[key] >= \
+       number_of_in_dict[key]):
       return_value[key] = return_value[key] - number_of_in_dict[key]
-      return_value[ larger_key_dict[key] ] = return_value[ larger_key_dict[key] ] + 1
-      
+      return_value[larger_key_dict[key]] = return_value[larger_key_dict[key]] + 1
+
+  for key in key_list:
+    if to_add.get(key, None) is not None:
+      return_value[key] = return_value[key] + to_add[key]
+      del to_add[key]
+    if key not in ('day', 'year'):
+      treatNegativeValues(return_value, key)
+      treatPositiveValues(return_value, key)
+  for key in reverse_key_list[:-1]:
+    if 1 > return_value[key] % 1 > 0:
+      return_value[lesser_key_dict[key]] += return_value[key] % 1 * number_less_of_in_dict[key]
+      return_value[key] = int(return_value[key])
+      for local_key in return_value.keys():
+        if local_key not in ('day', 'year'):
+          treatPositiveValues(return_value, local_key)
+      if key in ('day', 'year') and to_add.get(key, None) is not None:
+        del to_add[key]
+
   if to_add.get('year', None) is not None:
     return_value['year'] = return_value['year'] + to_add['year']
+
   day_to_add = return_value['day'] - 1
   if to_add.get('day', None) is not None:
     day_to_add += to_add['day']
@@ -100,9 +134,10 @@ def addToDate(date, to_add=None, **kw):
                                                 return_value['minute'],
                                                 return_value['second'],
                                                 date.timezone()))
+
   return_date += day_to_add
   return return_date
-  
+
 def getClosestDate(date=None, target_date=None, 
                    precision='month', before=1, strict=1):
   """
@@ -396,3 +431,17 @@ def createDateTimeFromMillis(millis):
   date._millis = millis
   return date
 
+def getNumberOfDayInMonth(date):
+  month = date.month()
+  if month < 10 and month != 2:
+    if month % 2 == 0:
+      return 30
+    return 31
+  elif month >=10 and month != 2:
+    if month % 2 == 0:
+      return 31
+    return 30
+  else:
+    if date.isLeapYear():
+      return 29
+    return 28
-- 
2.30.9