diff --git a/tests/test_datetime.py b/tests/test_datetime.py index 76cdf80..f31a7cf 100644 --- a/tests/test_datetime.py +++ b/tests/test_datetime.py @@ -775,7 +775,7 @@ def test_happy_strings(self): {"id": 6, "date": "2022-06-15"}, {"id": 7, "date": "foobar"}, ] - result = group_by_year_and_month({"items": input_data}, "date") + result = group_by_year_and_month(input_data, "date") expected = [ { "heading": "2021", @@ -828,7 +828,7 @@ def test_happy_datetime(self): {"id": 6, "date": datetime.date(2022, 6, 15)}, {"id": 7, "date": None}, ] - result = group_by_year_and_month({"items": input_data}, "date") + result = group_by_year_and_month(input_data, "date") expected = [ { "heading": "2021", @@ -882,7 +882,7 @@ def test_happy_strings_reverse(self): {"id": 6, "date": "2022-06-15"}, {"id": 7, "date": "foobar"}, ] - result = group_by_year_and_month({"items": input_data}, "date", reverse=True) + result = group_by_year_and_month(input_data, "date", reverse=True) expected = [ { "heading": "2022", @@ -935,7 +935,7 @@ def test_happy_datetime_reverse(self): {"id": 6, "date": datetime.date(2022, 6, 15)}, {"id": 7, "date": None}, ] - result = group_by_year_and_month({"items": input_data}, "date", reverse=True) + result = group_by_year_and_month(input_data, "date", reverse=True) expected = [ { "heading": "2022", diff --git a/tna_utilities/datetime.py b/tna_utilities/datetime.py index 6bbbcf8..8c0aa82 100644 --- a/tna_utilities/datetime.py +++ b/tna_utilities/datetime.py @@ -295,12 +295,24 @@ def pretty_datetime_range( # noqa: C901 return "" -def pretty_age(date, just_now_seconds: int = 5, lowercase_first: bool = False) -> str: +def pretty_age( + date: datetime.datetime, just_now_seconds: int = 5, lowercase_first: bool = False +) -> str: if not date: raise ValueError("Date must be provided") - date = date.replace(microsecond=0) - now = datetime.datetime.now().replace(microsecond=0) + # Normalise input date to be timezone-aware in UTC for consistent comparison + if isinstance(date, datetime.datetime): + if date.tzinfo is None: + date = date.replace(tzinfo=datetime.timezone.utc) + date = date.replace(microsecond=0) + else: + # For date objects, convert to a UTC datetime at midnight + date = datetime.datetime.combine( + date, datetime.time.min, tzinfo=datetime.timezone.utc + ) + + now = datetime.datetime.now(datetime.timezone.utc).replace(microsecond=0) future = now < date delta = date - now if future else now - date @@ -372,14 +384,14 @@ def is_today_in_date_range( def group_by_year_and_month( items: list[dict], date_key: str, reverse: bool = False -) -> dict: # noqa: C901 +) -> list[dict]: # noqa: C901 """ Groups a list of items by year and month based on a date key in each item. """ grouped = [] - for item in items.get("items", []): + for item in items: if request_date := item.get(date_key): if isinstance(request_date, (datetime.datetime, datetime.date)): request_datetime = request_date @@ -438,9 +450,37 @@ def group_by_year_and_month( for month_group in year_group["items"]: month_group["items"].sort( key=lambda x: ( - x.get(date_key) + ( + x.get(date_key) + if isinstance( + x.get(date_key), (datetime.date, datetime.datetime) + ) + else ( + get_date_from_string(x.get(date_key)) + if x.get(date_key) is not None + else ( + datetime.datetime.max + if reverse + else datetime.datetime.min + ) + ) + ) if isinstance(x.get(date_key), (datetime.date, datetime.datetime)) - else get_date_from_string(x.get(date_key)) + else ( + ( + get_date_from_string(x.get(date_key)) + if x.get(date_key) is not None + else ( + datetime.datetime.max + if reverse + else datetime.datetime.min + ) + ) + if isinstance(x.get(date_key), str) + else ( + datetime.datetime.max if reverse else datetime.datetime.min + ) + ) ), reverse=reverse, )