jenkins-bot has submitted this change and it was merged.
Change subject: [FEAT] Date: Add/subtract months from a date ......................................................................
[FEAT] Date: Add/subtract months from a date
This allows to add or subtract any number of months with a calendar that has 12 months. It can also calculate the difference between two days in terms of months.
Because Python's datetime module is based around a calendar with 12 months it works directly on it and assumes that a year has always 12 months.
Bug: T73124 Change-Id: I4b204465b7224f4d86e3f4462847002f3382ac3a --- M pywikibot/date.py M tests/date_tests.py 2 files changed, 78 insertions(+), 1 deletion(-)
Approvals: Xqt: Looks good to me, approved jenkins-bot: Verified
diff --git a/pywikibot/date.py b/pywikibot/date.py index 350bdf7..7181ba8 100644 --- a/pywikibot/date.py +++ b/pywikibot/date.py @@ -14,6 +14,8 @@ __version__ = '$Id$' #
+import calendar +import datetime import re import sys
@@ -2343,5 +2345,52 @@ return formats['YearAD'][lang](year)
+def apply_month_delta(date, month_delta=1, add_overlap=False): + """ + Add or subtract months from the date. + + By default if the new month has less days then the day of the date it + chooses the last day in the new month. For example a date in the March 31st + added by one month will result in April 30th. + + When the overlap is enabled, and there is overlap, then the new_date will be + one month off and get_month_delta will report a number one higher. + + It does only work on calendars with 12 months per year, and where the months + are numbered consecutively beginning by 1. + + @param date: The starting date + @type date: date + @param month_delta: The amount of months added or subtracted. + @type month_delta: int + @param add_overlap: Add any missing days to the date, increasing the month + once more. + @type add_overlap: bool + @return: The end date + @rtype: type of date + """ + if int(month_delta) != month_delta: + raise ValueError('Month delta must be an integer') + month = (date.month - 1) + month_delta + year = date.year + month // 12 + month = month % 12 + 1 + day = min(date.day, calendar.monthrange(year, month)[1]) + new_date = date.replace(year, month, day) + if add_overlap and day != date.day: + assert(date.day > day) + new_date += datetime.timedelta(days=date.day - day) + return new_date + + +def get_month_delta(date1, date2): + """ + Return the difference between to dates in months. + + It does only work on calendars with 12 months per year, and where the months + are consecutive and non-negative numbers. + """ + return date2.month - date1.month + (date2.year - date1.year) * 12 + + if __name__ == "__main__": print(__doc__) diff --git a/tests/date_tests.py b/tests/date_tests.py index 8c236f3..c7058ed 100644 --- a/tests/date_tests.py +++ b/tests/date_tests.py @@ -5,7 +5,7 @@ # # Distributed under the terms of the MIT license. # -__version__ = '$Id$' +from datetime import datetime
from pywikibot import date from tests.aspects import unittest, MetaTestCaseClass, TestCase @@ -60,6 +60,34 @@ net = False
+class TestMonthDelta(TestCase): + + """Tests for adding months to a date and getting the months between two.""" + + net = False + + def test_apply_positive_delta(self): + """Test adding months to a date.""" + self.assertEqual(datetime(2012, 3, 10), date.apply_month_delta(datetime(2012, 1, 10), 2)) + self.assertEqual(datetime(2012, 3, 31), date.apply_month_delta(datetime(2012, 1, 31), 2)) + self.assertEqual(datetime(2012, 2, 29), date.apply_month_delta(datetime(2012, 1, 31))) + self.assertEqual(datetime(2012, 3, 2), date.apply_month_delta(datetime(2012, 1, 31), add_overlap=True)) + + def test_apply_negative_delta(self): + """Test adding months to a date.""" + self.assertEqual(datetime(2012, 1, 10), date.apply_month_delta(datetime(2012, 3, 10), -2)) + self.assertEqual(datetime(2012, 1, 31), date.apply_month_delta(datetime(2012, 3, 31), -2)) + self.assertEqual(datetime(2012, 2, 29), date.apply_month_delta(datetime(2012, 3, 31), -1)) + self.assertEqual(datetime(2012, 3, 2), date.apply_month_delta(datetime(2012, 3, 31), -1, add_overlap=True)) + + def test_get_delta(self): + """Test that the delta is calculated correctly.""" + self.assertEqual(date.get_month_delta(datetime(2012, 1, 31), datetime(2012, 3, 31)), 2) + self.assertEqual(date.get_month_delta(datetime(2012, 3, 31), datetime(2012, 1, 31)), -2) + self.assertEqual(date.get_month_delta(datetime(2012, 3, 31), datetime(2013, 1, 31)), 10) + self.assertEqual(date.get_month_delta(datetime(2014, 3, 31), datetime(2013, 3, 31)), -12) + + if __name__ == '__main__': try: unittest.main()
pywikibot-commits@lists.wikimedia.org