From a2e0fac538c0ad3d56ad22d560f9245baac0b8fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 14 Nov 2024 01:56:19 +0000 Subject: [PATCH 01/11] grass.temporal.datetime_math: Return a TypedDict from compute_datetime_delta --- python/grass/temporal/datetime_math.py | 101 +++++++++++++++---------- 1 file changed, 62 insertions(+), 39 deletions(-) diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index 9104ab4b689..29df80b87c9 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -13,6 +13,7 @@ import copy from datetime import datetime, timedelta +from typing import TypedDict from .core import get_tgis_message_interface @@ -490,7 +491,20 @@ def adjust_datetime_to_granularity(mydate: datetime, granularity): ############################################################################### -def compute_datetime_delta(start, end): +class datetime_delta(TypedDict): + """Typed dictionary to return the accumulated delta in year, month, day, + hour, minute and second as well as max_days. At runtime, it is a plain dict.""" + + year: int + month: int + day: int + hour: int + minute: int + second: int + max_days: int + + +def compute_datetime_delta(start: datetime, end: datetime) -> datetime_delta: """Return a dictionary with the accumulated delta in year, month, day, hour, minute and second @@ -618,90 +632,99 @@ def compute_datetime_delta(start, end): >>> compute_datetime_delta(start, end) {'hour': 0, 'month': 12, 'second': 31622405, 'max_days': 366, 'year': 1, 'day': 0, 'minute': 0} - :return: A dictionary with year, month, day, hour, minute and second as - keys() + :return: A dictionary with year, month, day, hour, minute, second and max_days as keys() """ # noqa: E501 - comp = {} + _year: int = 0 + _month: int = 0 + _day: int = 0 + _hour: int = 0 + _minute: int = 0 + _second: int = 0 - day_diff = (end - start).days - - comp["max_days"] = day_diff + day_diff: int = (end - start).days # Date # Count full years - d = end.year - start.year - comp["year"] = d + _year: int = end.year - start.year # Count full months if start.month == 1 and end.month == 1: - comp["month"] = 0 + _month = 0 elif start.day == 1 and end.day == 1: d = end.month - start.month if d < 0: - d += 12 * comp["year"] + d += 12 * _year elif d == 0: - d = 12 * comp["year"] - comp["month"] = d + d = 12 * _year + _month = d # Count full days if start.day == 1 and end.day == 1: - comp["day"] = 0 + _day = 0 else: - comp["day"] = day_diff + _day = day_diff # Time # Hours if start.hour == 0 and end.hour == 0: - comp["hour"] = 0 + _hour = 0 else: d = end.hour - start.hour if d < 0: d += 24 + 24 * day_diff else: d += 24 * day_diff - comp["hour"] = d + _hour = d # Minutes if start.minute == 0 and end.minute == 0: - comp["minute"] = 0 + _minute = 0 else: d = end.minute - start.minute if d != 0: - if comp["hour"]: - d += 60 * comp["hour"] + if _hour: + d += 60 * _hour else: d += 24 * 60 * day_diff elif d == 0: - d = 60 * comp["hour"] if comp["hour"] else 24 * 60 * day_diff + d = 60 * _hour if _hour else 24 * 60 * day_diff - comp["minute"] = d + _minute = d # Seconds if start.second == 0 and end.second == 0: - comp["second"] = 0 + _second = 0 else: d = end.second - start.second if d != 0: - if comp["minute"]: - d += 60 * comp["minute"] - elif comp["hour"]: - d += 3600 * comp["hour"] + if _minute: + d += 60 * _minute + elif _hour: + d += 3600 * _hour else: d += 24 * 60 * 60 * day_diff elif d == 0: - if comp["minute"]: - d = 60 * comp["minute"] - elif comp["hour"]: - d = 3600 * comp["hour"] + if _minute: + d = 60 * _minute + elif _hour: + d = 3600 * _hour else: d = 24 * 60 * 60 * day_diff - comp["second"] = d - - return comp + _second = d + + return datetime_delta( + year=_year, + month=_month, + day=_day, + hour=_hour, + minute=_minute, + second=_second, + max_days=day_diff, + ) def check_datetime_string(time_string: str, use_dateutil: bool = True): - """Check if a string can be converted into a datetime object and return the object + """Check if a string can be converted into a datetime object and return the object In case dateutil is not installed the supported ISO string formats are: @@ -924,11 +947,11 @@ def datetime_to_grass_datetime_string(dt: datetime | None) -> str: } -def create_suffix_from_datetime(start_time: datetime, granularity) -> str: +def create_suffix_from_datetime(start_time: datetime, granularity: str) -> str: """Create a datetime string based on a datetime object and a provided granularity that can be used as suffix for map names. - dateteime=2001-01-01 00:00:00, granularity="1 month" returns "2001_01" + datetime=2001-01-01 00:00:00, granularity="1 month" returns "2001_01" :param start_time: The datetime object :param granularity: The granularity for example "1 month" or "100 seconds" @@ -947,8 +970,8 @@ def create_time_suffix(mapp, end: bool = False): start = mapp.temporal_extent.get_start_time() sstring = start.isoformat().replace(":", "_").replace("-", "_") if end: - end = mapp.temporal_extent.get_end_time() - estring = end.isoformat().replace(":", "_").replace("-", "_") + end_time = mapp.temporal_extent.get_end_time() + estring = end_time.isoformat().replace(":", "_").replace("-", "_") return "{st}_{en}".format(st=sstring, en=estring) return sstring From 1ad80d829e322ca310cbcb1fa5089112a5320914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 14 Nov 2024 01:59:54 +0000 Subject: [PATCH 02/11] Use ternary operator `_day = 0 if start.day == 1 and end.day == 1 else day_diff` instead of `if`-`else`-block --- python/grass/temporal/datetime_math.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index 29df80b87c9..b593e03d6ae 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -659,10 +659,7 @@ def compute_datetime_delta(start: datetime, end: datetime) -> datetime_delta: _month = d # Count full days - if start.day == 1 and end.day == 1: - _day = 0 - else: - _day = day_diff + _day = 0 if start.day == 1 and end.day == 1 else day_diff # Time # Hours From 6458226bbbfb906e5c61ef672122b515686e8785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 14 Nov 2024 02:02:05 +0000 Subject: [PATCH 03/11] Update date in file header --- python/grass/temporal/datetime_math.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index b593e03d6ae..de2e91344bd 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -1,7 +1,7 @@ """ Functions for mathematical datetime operations -(C) 2011-2013 by the GRASS Development Team +(C) 2011-2024 by the GRASS Development Team This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. From 04fd2f36b24575a785cbc3def7141e2d27fd26a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 14 Nov 2024 04:12:07 +0000 Subject: [PATCH 04/11] grass.temporal.datetime_math: Create pytest tests for compute_datetime_delta and datetime_delta dict Removed corresponding tests from the deprecated python/grass/temporal/unit_tests.py file which isn't run --- ...st_datetime_math_compute_datetime_delta.py | 289 +++++++++++++++ python/grass/temporal/unit_tests.py | 346 +----------------- 2 files changed, 290 insertions(+), 345 deletions(-) create mode 100644 python/grass/temporal/tests/test_datetime_math_compute_datetime_delta.py diff --git a/python/grass/temporal/tests/test_datetime_math_compute_datetime_delta.py b/python/grass/temporal/tests/test_datetime_math_compute_datetime_delta.py new file mode 100644 index 00000000000..dbcbaa2830f --- /dev/null +++ b/python/grass/temporal/tests/test_datetime_math_compute_datetime_delta.py @@ -0,0 +1,289 @@ +from datetime import datetime +from grass.temporal.datetime_math import compute_datetime_delta, datetime_delta + + +def test_datetime_delta_typeddict_dict() -> None: + assert datetime_delta( + month=3, day=2, minute=5, year=1, hour=8, max_days=455, second=30 + ) == dict( # noqa: C408 + month=3, day=2, minute=5, year=1, hour=8, max_days=455, second=30 + ) + + +def test_datetime_delta_typeddict_dict_literal() -> None: + assert datetime_delta( + month=3, day=2, minute=5, year=1, hour=8, max_days=455, second=30 + ) == { + "month": 3, + "day": 2, + "minute": 5, + "year": 1, + "hour": 8, + "max_days": 455, + "second": 30, + } + + +def test_datetime_delta_typeddict_dict_different_order() -> None: + assert datetime_delta( + month=3, day=2, minute=5, year=1, hour=8, max_days=455, second=30 + ) == { + "max_days": 455, + "month": 3, + "minute": 5, + "year": 1, + "day": 2, + "hour": 8, + "second": 30, + } + + +def test_datetime_delta_typeddict_typeddict_different_order() -> None: + assert datetime_delta( + year=1, + month=3, + day=2, + hour=8, + minute=5, + second=30, + max_days=455, + ) == datetime_delta( + month=3, + day=2, + minute=5, + year=1, + hour=8, + max_days=455, + second=30, + ) + + +def test_datetime_delta_typeddict_dict_missing() -> None: + assert datetime_delta( + month=3, day=2, minute=5, year=1, hour=8, max_days=455, second=30 + ) != {"month": 3, "day": 2, "minute": 5, "hour": 8, "max_days": 455} + + +def test_compute_datetime_delta_same_date() -> None: + start = datetime(2001, 1, 1, 0, 0, 0) + end = datetime(2001, 1, 1, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=0, minute=0, second=0, max_days=0 + ) + + +def test_compute_datetime_delta_seconds_only() -> None: + start = datetime(2001, 1, 1, 0, 0, 14) + end = datetime(2001, 1, 1, 0, 0, 44) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=0, minute=0, second=30, max_days=0 + ) + + +def test_compute_datetime_delta_minute_seconds() -> None: + start = datetime(2001, 1, 1, 0, 0, 44) + end = datetime(2001, 1, 1, 0, 1, 14) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=0, minute=1, second=30, max_days=0 + ) + + +def test_compute_datetime_delta_minutes_seconds_same() -> None: + start = datetime(2001, 1, 1, 0, 0, 30) + end = datetime(2001, 1, 1, 0, 5, 30) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=0, minute=5, second=300, max_days=0 + ) + + +def test_compute_datetime_delta_minute_only() -> None: + start = datetime(2001, 1, 1, 0, 0, 0) + end = datetime(2001, 1, 1, 0, 1, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=0, minute=1, second=0, max_days=0 + ) + + +def test_compute_datetime_delta_hour_minutes_same() -> None: + start = datetime(2011, 10, 31, 0, 45, 0) + end = datetime(2011, 10, 31, 1, 45, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=1, minute=60, second=0, max_days=0 + ) + + +def test_compute_datetime_delta_hour_minutes() -> None: + start = datetime(2011, 10, 31, 0, 45, 0) + end = datetime(2011, 10, 31, 1, 15, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=1, minute=30, second=0, max_days=0 + ) + + +def test_compute_datetime_delta_hours_minutes() -> None: + start = datetime(2011, 10, 31, 0, 45, 0) + end = datetime(2011, 10, 31, 12, 15, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=12, minute=690, second=0, max_days=0 + ) + + +def test_compute_datetime_delta_hour_only() -> None: + start = datetime(2011, 10, 31, 0, 0, 0) + end = datetime(2011, 10, 31, 1, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=1, minute=0, second=0, max_days=0 + ) + + +def test_compute_datetime_delta_month_day_hour() -> None: + start = datetime(2011, 10, 31, 0, 0, 0) + end = datetime(2011, 11, 1, 1, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=1, hour=25, minute=0, second=0, max_days=1 + ) + + +def test_compute_datetime_delta_month_day_hours() -> None: + start = datetime(2011, 10, 31, 12, 0, 0) + end = datetime(2011, 11, 1, 6, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=18, minute=0, second=0, max_days=0 + ) + + +def test_compute_datetime_delta_month_hour() -> None: + start = datetime(2011, 11, 1, 0, 0, 0) + end = datetime(2011, 12, 1, 1, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=1, day=0, hour=721, minute=0, second=0, max_days=30 + ) + + +def test_compute_datetime_delta_days_only() -> None: + start = datetime(2011, 11, 1, 0, 0, 0) + end = datetime(2011, 11, 5, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=4, hour=0, minute=0, second=0, max_days=4 + ) + + +def test_compute_datetime_delta_month_days_less_than_month() -> None: + start = datetime(2011, 10, 6, 0, 0, 0) + end = datetime(2011, 11, 5, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=30, hour=0, minute=0, second=0, max_days=30 + ) + + +def test_compute_datetime_delta_year_month_days_less_than_month() -> None: + start = datetime(2011, 12, 2, 0, 0, 0) + end = datetime(2012, 1, 1, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=1, month=0, day=30, hour=0, minute=0, second=0, max_days=30 + ) + + +def test_compute_datetime_delta_month_only() -> None: + start = datetime(2011, 1, 1, 0, 0, 0) + end = datetime(2011, 2, 1, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=1, day=0, hour=0, minute=0, second=0, max_days=31 + ) + + +def test_compute_datetime_delta_year_month() -> None: + start = datetime(2011, 12, 1, 0, 0, 0) + end = datetime(2012, 1, 1, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=1, month=1, day=0, hour=0, minute=0, second=0, max_days=31 + ) + + +def test_compute_datetime_delta_year_months() -> None: + start = datetime(2011, 12, 1, 0, 0, 0) + end = datetime(2012, 6, 1, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=1, month=6, day=0, hour=0, minute=0, second=0, max_days=183 + ) + + +def test_compute_datetime_delta_years_only() -> None: + start = datetime(2011, 6, 1, 0, 0, 0) + end = datetime(2021, 6, 1, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=10, month=120, day=0, hour=0, minute=0, second=0, max_days=3653 + ) + + +def test_compute_datetime_delta_year_hours() -> None: + start = datetime(2011, 6, 1, 0, 0, 0) + end = datetime(2012, 6, 1, 12, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=1, month=12, day=0, hour=8796, minute=0, second=0, max_days=366 + ) + + +def test_compute_datetime_delta_year_hours_minutes() -> None: + start = datetime(2011, 6, 1, 0, 0, 0) + end = datetime(2012, 6, 1, 12, 30, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=1, month=12, day=0, hour=8796, minute=527790, second=0, max_days=366 + ) + + +def test_compute_datetime_delta_year_hours_seconds() -> None: + start = datetime(2011, 6, 1, 0, 0, 0) + end = datetime(2012, 6, 1, 12, 0, 5) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=1, month=12, day=0, hour=8796, minute=0, second=31665605, max_days=366 + ) + + +def test_compute_datetime_delta_year_minutes() -> None: + start = datetime(2011, 6, 1, 0, 0, 0) + end = datetime(2012, 6, 1, 0, 30, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=1, month=12, day=0, hour=0, minute=527070, second=0, max_days=366 + ) + + +def test_compute_datetime_delta_year_seconds() -> None: + start = datetime(2011, 6, 1, 0, 0, 0) + end = datetime(2012, 6, 1, 0, 0, 5) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=1, month=12, day=0, hour=0, minute=0, second=31622405, max_days=366 + ) + + +def test_compute_datetime_delta_month_full() -> None: + start = datetime(2011, 7, 3, 0, 0, 0) + end = datetime(2011, 8, 2, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=30, hour=0, minute=0, second=0, max_days=30 + ) diff --git a/python/grass/temporal/unit_tests.py b/python/grass/temporal/unit_tests.py index 8275e012f25..63ff4a0778d 100644 --- a/python/grass/temporal/unit_tests.py +++ b/python/grass/temporal/unit_tests.py @@ -21,7 +21,7 @@ AbstractDatasetComparisonKeyStartTime, ) from .core import init -from .datetime_math import compute_datetime_delta, increment_datetime_by_string +from .datetime_math import increment_datetime_by_string from .space_time_datasets import RasterDataset from .spatial_extent import SpatialExtent from .spatio_temporal_relationships import SpatioTemporalTopologyBuilder @@ -223,349 +223,6 @@ def test_adjust_datetime_to_granularity() -> None: ############################################################################### -def test_compute_datetime_delta() -> None: - print("Test 1") - start = datetime(2001, 1, 1, 0, 0, 0) - end = datetime(2001, 1, 1, 0, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["second"] - correct = 0 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 2") - start = datetime(2001, 1, 1, 0, 0, 14) - end = datetime(2001, 1, 1, 0, 0, 44) - - comp = compute_datetime_delta(start, end) - - result = comp["second"] - correct = 30 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 3") - start = datetime(2001, 1, 1, 0, 0, 44) - end = datetime(2001, 1, 1, 0, 1, 14) - - comp = compute_datetime_delta(start, end) - - result = comp["second"] - correct = 30 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 4") - start = datetime(2001, 1, 1, 0, 0, 30) - end = datetime(2001, 1, 1, 0, 5, 30) - - comp = compute_datetime_delta(start, end) - - result = comp["second"] - correct = 300 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 5") - start = datetime(2001, 1, 1, 0, 0, 0) - end = datetime(2001, 1, 1, 0, 1, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["minute"] - correct = 1 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 6") - start = datetime(2011, 10, 31, 0, 45, 0) - end = datetime(2011, 10, 31, 1, 45, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["minute"] - correct = 60 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 7") - start = datetime(2011, 10, 31, 0, 45, 0) - end = datetime(2011, 10, 31, 1, 15, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["minute"] - correct = 30 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 8") - start = datetime(2011, 10, 31, 0, 45, 0) - end = datetime(2011, 10, 31, 12, 15, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["minute"] - correct = 690 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 9") - start = datetime(2011, 10, 31, 0, 0, 0) - end = datetime(2011, 10, 31, 1, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["hour"] - correct = 1 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 10") - start = datetime(2011, 10, 31, 0, 0, 0) - end = datetime(2011, 11, 1, 1, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["hour"] - correct = 25 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 11") - start = datetime(2011, 10, 31, 12, 0, 0) - end = datetime(2011, 11, 1, 6, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["hour"] - correct = 18 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 12") - start = datetime(2011, 11, 1, 0, 0, 0) - end = datetime(2011, 12, 1, 1, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["hour"] - correct = 30 * 24 + 1 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 13") - start = datetime(2011, 11, 1, 0, 0, 0) - end = datetime(2011, 11, 5, 0, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["day"] - correct = 4 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 14") - start = datetime(2011, 10, 6, 0, 0, 0) - end = datetime(2011, 11, 5, 0, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["day"] - correct = 30 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 15") - start = datetime(2011, 12, 2, 0, 0, 0) - end = datetime(2012, 1, 1, 0, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["day"] - correct = 30 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 16") - start = datetime(2011, 1, 1, 0, 0, 0) - end = datetime(2011, 2, 1, 0, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["month"] - correct = 1 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 17") - start = datetime(2011, 12, 1, 0, 0, 0) - end = datetime(2012, 1, 1, 0, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["month"] - correct = 1 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 18") - start = datetime(2011, 12, 1, 0, 0, 0) - end = datetime(2012, 6, 1, 0, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["month"] - correct = 6 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 19") - start = datetime(2011, 6, 1, 0, 0, 0) - end = datetime(2021, 6, 1, 0, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["year"] - correct = 10 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 20") - start = datetime(2011, 6, 1, 0, 0, 0) - end = datetime(2012, 6, 1, 12, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["hour"] - d = end - start - correct = 12 + d.days * 24 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 21") - start = datetime(2011, 6, 1, 0, 0, 0) - end = datetime(2012, 6, 1, 12, 30, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["minute"] - d = end - start - correct = d.days * 24 * 60 + 12 * 60 + 30 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 22") - start = datetime(2011, 6, 1, 0, 0, 0) - end = datetime(2012, 6, 1, 12, 0, 5) - - comp = compute_datetime_delta(start, end) - - result = comp["second"] - d = end - start - correct = 5 + 60 * 60 * 12 + d.days * 24 * 60 * 60 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 23") - start = datetime(2011, 6, 1, 0, 0, 0) - end = datetime(2012, 6, 1, 0, 30, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["minute"] - d = end - start - correct = 30 + d.days * 24 * 60 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 24") - start = datetime(2011, 6, 1, 0, 0, 0) - end = datetime(2012, 6, 1, 0, 0, 5) - - comp = compute_datetime_delta(start, end) - - result = comp["second"] - d = end - start - correct = 5 + d.days * 24 * 60 * 60 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - def test_compute_absolute_time_granularity() -> None: # First we test intervals print("Test 1") @@ -1669,7 +1326,6 @@ def test_4d_rtree() -> None: test_adjust_datetime_to_granularity() test_spatial_extent_intersection() test_compute_absolute_time_granularity() - test_compute_datetime_delta() test_spatial_extent_intersection() test_spatial_relations() test_temporal_topology_builder() From fdd664c78caa222c0845183fddfa6b629d753731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 14 Nov 2024 07:50:32 -0500 Subject: [PATCH 05/11] Revert unintended changes in datetime_math --- python/grass/temporal/datetime_math.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index de2e91344bd..fb045919c6a 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -721,7 +721,7 @@ def compute_datetime_delta(start: datetime, end: datetime) -> datetime_delta: def check_datetime_string(time_string: str, use_dateutil: bool = True): - """Check if a string can be converted into a datetime object and return the object + """Check if a string can be converted into a datetime object and return the object In case dateutil is not installed the supported ISO string formats are: @@ -944,11 +944,11 @@ def datetime_to_grass_datetime_string(dt: datetime | None) -> str: } -def create_suffix_from_datetime(start_time: datetime, granularity: str) -> str: +def create_suffix_from_datetime(start_time: datetime, granularity) -> str: """Create a datetime string based on a datetime object and a provided granularity that can be used as suffix for map names. - datetime=2001-01-01 00:00:00, granularity="1 month" returns "2001_01" + dateteime=2001-01-01 00:00:00, granularity="1 month" returns "2001_01" :param start_time: The datetime object :param granularity: The granularity for example "1 month" or "100 seconds" @@ -967,8 +967,8 @@ def create_time_suffix(mapp, end: bool = False): start = mapp.temporal_extent.get_start_time() sstring = start.isoformat().replace(":", "_").replace("-", "_") if end: - end_time = mapp.temporal_extent.get_end_time() - estring = end_time.isoformat().replace(":", "_").replace("-", "_") + end = mapp.temporal_extent.get_end_time() + estring = end.isoformat().replace(":", "_").replace("-", "_") return "{st}_{en}".format(st=sstring, en=estring) return sstring From c0078de1681cecf029b233cc9b2e2e9312346a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 14 Nov 2024 07:51:32 -0500 Subject: [PATCH 06/11] DEBUG: Highlight test errors when month is wrongly initialized --- python/grass/temporal/datetime_math.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index fb045919c6a..587a976011c 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -635,7 +635,7 @@ def compute_datetime_delta(start: datetime, end: datetime) -> datetime_delta: :return: A dictionary with year, month, day, hour, minute, second and max_days as keys() """ # noqa: E501 _year: int = 0 - _month: int = 0 + _month: int = 110 _day: int = 0 _hour: int = 0 _minute: int = 0 From ea31473d1eddc19f63327072bd37599749abf32c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Fri, 15 Nov 2024 08:03:15 -0500 Subject: [PATCH 07/11] Rename test file to be catched by current pytest config --- ...delta.py => test_datetime_math_compute_datetime_delta_test.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename python/grass/temporal/tests/{test_datetime_math_compute_datetime_delta.py => test_datetime_math_compute_datetime_delta_test.py} (100%) diff --git a/python/grass/temporal/tests/test_datetime_math_compute_datetime_delta.py b/python/grass/temporal/tests/test_datetime_math_compute_datetime_delta_test.py similarity index 100% rename from python/grass/temporal/tests/test_datetime_math_compute_datetime_delta.py rename to python/grass/temporal/tests/test_datetime_math_compute_datetime_delta_test.py From daa5fe611793410b2af999d8ec4e0ff842350691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Fri, 27 Dec 2024 01:18:10 +0000 Subject: [PATCH 08/11] temporal: Assign the TypedDict with default values and keep the original look of the variabled --- python/grass/temporal/datetime_math.py | 77 +++++++++++++------------- 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index 587a976011c..1ad3d90d7fd 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -634,90 +634,87 @@ def compute_datetime_delta(start: datetime, end: datetime) -> datetime_delta: :return: A dictionary with year, month, day, hour, minute, second and max_days as keys() """ # noqa: E501 - _year: int = 0 - _month: int = 110 - _day: int = 0 - _hour: int = 0 - _minute: int = 0 - _second: int = 0 + # TODO: set default values here, and ensure processing below covers all situations, + # not leaking these default values + comp = datetime_delta( + year=0, + month=110, + day=0, + hour=0, + minute=0, + second=0, + max_days=(end - start).days, + ) - day_diff: int = (end - start).days + day_diff = comp["max_days"] # Date # Count full years - _year: int = end.year - start.year + comp["year"] = end.year - start.year # Count full months if start.month == 1 and end.month == 1: - _month = 0 + comp["month"] = 0 elif start.day == 1 and end.day == 1: d = end.month - start.month if d < 0: - d += 12 * _year + d += 12 * comp["year"] elif d == 0: - d = 12 * _year - _month = d + d = 12 * comp["year"] + comp["month"] = d # Count full days - _day = 0 if start.day == 1 and end.day == 1 else day_diff + comp["day"] = 0 if start.day == 1 and end.day == 1 else day_diff # Time # Hours if start.hour == 0 and end.hour == 0: - _hour = 0 + comp["hour"] = 0 else: d = end.hour - start.hour if d < 0: d += 24 + 24 * day_diff else: d += 24 * day_diff - _hour = d + comp["hour"] = d # Minutes if start.minute == 0 and end.minute == 0: - _minute = 0 + comp["minute"] = 0 else: d = end.minute - start.minute if d != 0: - if _hour: - d += 60 * _hour + if comp["hour"]: + d += 60 * comp["hour"] else: d += 24 * 60 * day_diff elif d == 0: - d = 60 * _hour if _hour else 24 * 60 * day_diff + d = 60 * comp["hour"] if comp["hour"] else 24 * 60 * day_diff - _minute = d + comp["minute"] = d # Seconds if start.second == 0 and end.second == 0: - _second = 0 + comp["second"] = 0 else: d = end.second - start.second if d != 0: - if _minute: - d += 60 * _minute - elif _hour: - d += 3600 * _hour + if comp["minute"]: + d += 60 * comp["minute"] + elif comp["hour"]: + d += 3600 * comp["hour"] else: d += 24 * 60 * 60 * day_diff elif d == 0: - if _minute: - d = 60 * _minute - elif _hour: - d = 3600 * _hour + if comp["minute"]: + d = 60 * comp["minute"] + elif comp["hour"]: + d = 3600 * comp["hour"] else: d = 24 * 60 * 60 * day_diff - _second = d - - return datetime_delta( - year=_year, - month=_month, - day=_day, - hour=_hour, - minute=_minute, - second=_second, - max_days=day_diff, - ) + comp["second"] = d + + return comp def check_datetime_string(time_string: str, use_dateutil: bool = True): From d435df6fc695c1fe66a4cc211241d0cd9cc8d121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Tue, 31 Dec 2024 11:41:09 -0500 Subject: [PATCH 09/11] Update datetime_math.py --- python/grass/temporal/datetime_math.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index 1ad3d90d7fd..03774c3067f 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -638,7 +638,7 @@ def compute_datetime_delta(start: datetime, end: datetime) -> datetime_delta: # not leaking these default values comp = datetime_delta( year=0, - month=110, + month=0, day=0, hour=0, minute=0, From fe1c1be14636d10a798a500e14f809d1575f0378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:52:28 +0000 Subject: [PATCH 10/11] Remove duplicated, but wrong, doc example --- python/grass/temporal/datetime_math.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index 03774c3067f..5e037bfd5d1 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -627,11 +627,6 @@ def compute_datetime_delta(start: datetime, end: datetime) -> datetime_delta: >>> compute_datetime_delta(start, end) {'hour': 0, 'month': 12, 'second': 0, 'max_days': 366, 'year': 1, 'day': 0, 'minute': 527070} - >>> start = datetime(2011, 12, 1, 0, 0, 0) - >>> end = datetime(2012, 6, 1, 0, 0, 0) - >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 12, 'second': 31622405, 'max_days': 366, 'year': 1, 'day': 0, 'minute': 0} - :return: A dictionary with year, month, day, hour, minute, second and max_days as keys() """ # noqa: E501 # TODO: set default values here, and ensure processing below covers all situations, From d347e94b8ff21b4f09cedf0b64011b7bf9f6a315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Fri, 3 Jan 2025 03:35:50 +0000 Subject: [PATCH 11/11] tests: Update doctest with current dict order --- python/grass/temporal/datetime_math.py | 46 +++++++++++++------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index 5e037bfd5d1..b96cd8c3e2a 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -515,117 +515,117 @@ def compute_datetime_delta(start: datetime, end: datetime) -> datetime_delta: >>> start = datetime(2001, 1, 1, 0, 0, 0) >>> end = datetime(2001, 1, 1, 0, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 0, 'second': 0, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 0} + {'year': 0, 'month': 0, 'day': 0, 'hour': 0, 'minute': 0, 'second': 0, 'max_days': 0} >>> start = datetime(2001, 1, 1, 0, 0, 14) >>> end = datetime(2001, 1, 1, 0, 0, 44) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 0, 'second': 30, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 0} + {'year': 0, 'month': 0, 'day': 0, 'hour': 0, 'minute': 0, 'second': 30, 'max_days': 0} >>> start = datetime(2001, 1, 1, 0, 0, 44) >>> end = datetime(2001, 1, 1, 0, 1, 14) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 0, 'second': 30, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 1} + {'year': 0, 'month': 0, 'day': 0, 'hour': 0, 'minute': 1, 'second': 30, 'max_days': 0} >>> start = datetime(2001, 1, 1, 0, 0, 30) >>> end = datetime(2001, 1, 1, 0, 5, 30) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 0, 'second': 300, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 5} + {'year': 0, 'month': 0, 'day': 0, 'hour': 0, 'minute': 5, 'second': 300, 'max_days': 0} >>> start = datetime(2001, 1, 1, 0, 0, 0) >>> end = datetime(2001, 1, 1, 0, 1, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 0, 'second': 0, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 1} + {'year': 0, 'month': 0, 'day': 0, 'hour': 0, 'minute': 1, 'second': 0, 'max_days': 0} >>> start = datetime(2011, 10, 31, 0, 45, 0) >>> end = datetime(2011, 10, 31, 1, 45, 0) >>> compute_datetime_delta(start, end) - {'hour': 1, 'second': 0, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 60} + {'year': 0, 'month': 0, 'day': 0, 'hour': 1, 'minute': 60, 'second': 0, 'max_days': 0} >>> start = datetime(2011, 10, 31, 0, 45, 0) >>> end = datetime(2011, 10, 31, 1, 15, 0) >>> compute_datetime_delta(start, end) - {'hour': 1, 'second': 0, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 30} + {'year': 0, 'month': 0, 'day': 0, 'hour': 1, 'minute': 30, 'second': 0, 'max_days': 0} >>> start = datetime(2011, 10, 31, 0, 45, 0) >>> end = datetime(2011, 10, 31, 12, 15, 0) >>> compute_datetime_delta(start, end) - {'hour': 12, 'second': 0, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 690} + {'year': 0, 'month': 0, 'day': 0, 'hour': 12, 'minute': 690, 'second': 0, 'max_days': 0} >>> start = datetime(2011, 10, 31, 0, 0, 0) >>> end = datetime(2011, 10, 31, 1, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 1, 'second': 0, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 0} + {'year': 0, 'month': 0, 'day': 0, 'hour': 1, 'minute': 0, 'second': 0, 'max_days': 0} >>> start = datetime(2011, 10, 31, 0, 0, 0) >>> end = datetime(2011, 11, 1, 1, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 25, 'second': 0, 'max_days': 1, 'year': 0, 'day': 1, 'minute': 0} + {'year': 0, 'month': 0, 'day': 1, 'hour': 25, 'minute': 0, 'second': 0, 'max_days': 1} >>> start = datetime(2011, 10, 31, 12, 0, 0) >>> end = datetime(2011, 11, 1, 6, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 18, 'second': 0, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 0} + {'year': 0, 'month': 0, 'day': 0, 'hour': 18, 'minute': 0, 'second': 0, 'max_days': 0} >>> start = datetime(2011, 11, 1, 0, 0, 0) >>> end = datetime(2011, 12, 1, 1, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 721, 'month': 1, 'second': 0, 'max_days': 30, 'year': 0, 'day': 0, 'minute': 0} + {'year': 0, 'month': 1, 'day': 0, 'hour': 721, 'minute': 0, 'second': 0, 'max_days': 30} >>> start = datetime(2011, 11, 1, 0, 0, 0) >>> end = datetime(2011, 11, 5, 0, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'second': 0, 'max_days': 4, 'year': 0, 'day': 4, 'minute': 0} + {'year': 0, 'month': 0, 'day': 4, 'hour': 0, 'minute': 0, 'second': 0, 'max_days': 4} >>> start = datetime(2011, 10, 6, 0, 0, 0) >>> end = datetime(2011, 11, 5, 0, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'second': 0, 'max_days': 30, 'year': 0, 'day': 30, 'minute': 0} + {'year': 0, 'month': 0, 'day': 30, 'hour': 0, 'minute': 0, 'second': 0, 'max_days': 30} >>> start = datetime(2011, 12, 2, 0, 0, 0) >>> end = datetime(2012, 1, 1, 0, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'second': 0, 'max_days': 30, 'year': 1, 'day': 30, 'minute': 0} + {'year': 1, 'month': 0, 'day': 30, 'hour': 0, 'minute': 0, 'second': 0, 'max_days': 30} >>> start = datetime(2011, 1, 1, 0, 0, 0) >>> end = datetime(2011, 2, 1, 0, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 1, 'second': 0, 'max_days': 31, 'year': 0, 'day': 0, 'minute': 0} + {'year': 0, 'month': 1, 'day': 0, 'hour': 0, 'minute': 0, 'second': 0, 'max_days': 31} >>> start = datetime(2011, 12, 1, 0, 0, 0) >>> end = datetime(2012, 1, 1, 0, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 1, 'second': 0, 'max_days': 31, 'year': 1, 'day': 0, 'minute': 0} + {'year': 1, 'month': 1, 'day': 0, 'hour': 0, 'minute': 0, 'second': 0, 'max_days': 31} >>> start = datetime(2011, 12, 1, 0, 0, 0) >>> end = datetime(2012, 6, 1, 0, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 6, 'second': 0, 'max_days': 183, 'year': 1, 'day': 0, 'minute': 0} + {'year': 1, 'month': 6, 'day': 0, 'hour': 0, 'minute': 0, 'second': 0, 'max_days': 183} >>> start = datetime(2011, 6, 1, 0, 0, 0) >>> end = datetime(2021, 6, 1, 0, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 120, 'second': 0, 'max_days': 3653, 'year': 10, 'day': 0, 'minute': 0} + {'year': 10, 'month': 120, 'day': 0, 'hour': 0, 'minute': 0, 'second': 0, 'max_days': 3653} >>> start = datetime(2011, 6, 1, 0, 0, 0) >>> end = datetime(2012, 6, 1, 12, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 8796, 'month': 12, 'second': 0, 'max_days': 366, 'year': 1, 'day': 0, 'minute': 0} + {'year': 1, 'month': 12, 'day': 0, 'hour': 8796, 'minute': 0, 'second': 0, 'max_days': 366} >>> start = datetime(2011, 6, 1, 0, 0, 0) >>> end = datetime(2012, 6, 1, 12, 30, 0) >>> compute_datetime_delta(start, end) - {'hour': 8796, 'month': 12, 'second': 0, 'max_days': 366, 'year': 1, 'day': 0, 'minute': 527790} + {'year': 1, 'month': 12, 'day': 0, 'hour': 8796, 'minute': 527790, 'second': 0, 'max_days': 366} >>> start = datetime(2011, 6, 1, 0, 0, 0) >>> end = datetime(2012, 6, 1, 12, 0, 5) >>> compute_datetime_delta(start, end) - {'hour': 8796, 'month': 12, 'second': 31665605, 'max_days': 366, 'year': 1, 'day': 0, 'minute': 0} + {'year': 1, 'month': 12, 'day': 0, 'hour': 8796, 'minute': 0, 'second': 31665605, 'max_days': 366} >>> start = datetime(2011, 6, 1, 0, 0, 0) >>> end = datetime(2012, 6, 1, 0, 30, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 12, 'second': 0, 'max_days': 366, 'year': 1, 'day': 0, 'minute': 527070} + {'year': 1, 'month': 12, 'day': 0, 'hour': 0, 'minute': 527070, 'second': 0, 'max_days': 366} :return: A dictionary with year, month, day, hour, minute, second and max_days as keys() """ # noqa: E501