Commit d590ade9 authored by Nicolas Wavrant's avatar Nicolas Wavrant

erp5_core: fix addToDate when removing a month

The way addToDate was working with dates was not good, and creating
confusion when removing 1 month from the last days of a 31-day month, as
the previous day had less days than the current month:

date = DateTime(2023, 5, 31)
print date
print addToDate(date, month=-1)
> 2023/05/31 00:00:00 GMT+2
> 2023/05/01 00:00:00 GMT+2

This was even more confusing in March, with february having only 28
days:

date = DateTime(2023, 3, 31)
print date
print addToDate(date, month=-1)
> 2023/03/31 00:00:00 GMT+2
> 2023/03/03 00:00:00 GMT+2

The new behavior is to, when removing a month, if the new day of the new
month is more than the number of days in month to default to the last
day of the month. For exemple, removing one month from 31/05 becomes
30/04, and from there it will add/remove the days as necessary.

The real issue being that removing a month is ambiguous and can mean
a different thing for different people.

For reference, the reference implementation of timedelta in python
doesn't support adding months:

https://docs.python.org/3/library/datetime.html#datetime.timedelta

I hope my solution will make the more sense in ERP5's context.
parent efaea8fe
Pipeline #27540 failed with stage
in 0 seconds
...@@ -62,6 +62,20 @@ class TestDateUtils(unittest.TestCase): ...@@ -62,6 +62,20 @@ class TestDateUtils(unittest.TestCase):
addToDate(date, month=90).toZone('UTC').ISO()) addToDate(date, month=90).toZone('UTC').ISO())
self.assertEqual(DateTime('2090/01/01 %s' % self.timezone).toZone('UTC').ISO(), self.assertEqual(DateTime('2090/01/01 %s' % self.timezone).toZone('UTC').ISO(),
addToDate(date, year=90).toZone('UTC').ISO()) addToDate(date, year=90).toZone('UTC').ISO())
april_1 = DateTime('2001/04/01 %s' % self.timezone)
self.assertEqual(DateTime('2001/05/01 %s' % self.timezone).toZone('UTC').ISO(),
addToDate(april_1, month=1).toZone('UTC').ISO())
december_1 = DateTime('2001/12/01 %s' % self.timezone)
self.assertEqual(DateTime('2002/01/01 %s' % self.timezone).toZone('UTC').ISO(),
addToDate(december_1, month=1).toZone('UTC').ISO())
march_31 = DateTime('2001/03/31 %s' % self.timezone)
self.assertEqual(DateTime('2001/02/28 %s' % self.timezone).toZone('UTC').ISO(),
addToDate(march_31, month=-1).toZone('UTC').ISO())
self.assertEqual(DateTime('2001/02/27 %s' % self.timezone).toZone('UTC').ISO(),
addToDate(march_31, month=-1, day=-1).toZone('UTC').ISO())
may_31 = DateTime('2000/05/31 %s' % self.timezone)
self.assertEqual(DateTime('2000/04/30 %s' % self.timezone).toZone('UTC').ISO(),
addToDate(may_31, month=-1).toZone('UTC').ISO())
def test_negative_add_to_date(self): def test_negative_add_to_date(self):
date = DateTime('2000/01/01 %s' % self.timezone) date = DateTime('2000/01/01 %s' % self.timezone)
......
...@@ -110,6 +110,8 @@ def addToDate(date, to_add=None, **kw): ...@@ -110,6 +110,8 @@ def addToDate(date, to_add=None, **kw):
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: for key in key_list:
if key == 'day':
continue
if to_add.get(key, None) is not None: if to_add.get(key, None) is not None:
return_value[key] = return_value[key] + to_add[key] return_value[key] = return_value[key] + to_add[key]
del to_add[key] del to_add[key]
...@@ -125,7 +127,12 @@ def addToDate(date, to_add=None, **kw): ...@@ -125,7 +127,12 @@ def addToDate(date, to_add=None, **kw):
if local_key not in ('day', 'year'): if local_key not in ('day', 'year'):
treatPositiveValues(return_value, local_key) treatPositiveValues(return_value, local_key)
day_to_add = return_value['day'] - 1 day_to_add = min(
return_value['day'],
getNumberOfDayInMonth(
DateTime(return_value['year'], int(return_value['month']), 1)
)
) - 1
if to_add.get('day', None) is not None: if to_add.get('day', None) is not None:
day_to_add += to_add['day'] day_to_add += to_add['day']
return_value['day'] = 1 return_value['day'] = 1
......
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