diff --git a/cachecontrol/caches/redis_cache.py b/cachecontrol/caches/redis_cache.py index 98e9f3e..ee03764 100644 --- a/cachecontrol/caches/redis_cache.py +++ b/cachecontrol/caches/redis_cache.py @@ -4,7 +4,7 @@ from __future__ import division -from datetime import datetime +from datetime import datetime, timezone from typing import TYPE_CHECKING, Optional, Union from cachecontrol.cache import BaseCache @@ -26,7 +26,10 @@ def set( if not expires: self.conn.set(key, value) elif isinstance(expires, datetime): - delta = expires - datetime.utcnow() + now_utc = datetime.now(timezone.utc) + if expires.tzinfo is None: + now_utc = now_utc.replace(tzinfo=None) + delta = expires - now_utc self.conn.setex(key, int(delta.total_seconds()), value) else: self.conn.setex(key, expires, value) diff --git a/cachecontrol/heuristics.py b/cachecontrol/heuristics.py index fdee762..5188c66 100644 --- a/cachecontrol/heuristics.py +++ b/cachecontrol/heuristics.py @@ -4,7 +4,7 @@ import calendar import time -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from email.utils import formatdate, parsedate, parsedate_tz from typing import TYPE_CHECKING, Any, Dict, Mapping, Optional @@ -15,7 +15,7 @@ def expire_after(delta: timedelta, date: Optional[datetime] = None) -> datetime: - date = date or datetime.utcnow() + date = date or datetime.now(timezone.utc) return date + delta @@ -67,7 +67,7 @@ def update_headers(self, response: "HTTPResponse") -> Dict[str, str]: if "expires" not in response.headers: date = parsedate(response.headers["date"]) - expires = expire_after(timedelta(days=1), date=datetime(*date[:6])) + expires = expire_after(timedelta(days=1), date=datetime(*date[:6], tzinfo=timezone.utc)) # type: ignore[misc] headers["expires"] = datetime_to_header(expires) headers["cache-control"] = "public" return headers diff --git a/tests/test_expires_heuristics.py b/tests/test_expires_heuristics.py index 2dd1b5f..c0895ef 100644 --- a/tests/test_expires_heuristics.py +++ b/tests/test_expires_heuristics.py @@ -4,7 +4,7 @@ import calendar import time -from datetime import datetime +from datetime import datetime, timezone from email.utils import formatdate, parsedate from pprint import pprint from unittest.mock import Mock @@ -157,7 +157,9 @@ def test_last_modified_is_used(self): resp = DummyResponse(200, {"Date": self.now, "Last-Modified": self.week_ago}) modified = self.heuristic.update_headers(resp) assert ["expires"] == list(modified.keys()) - assert datetime(*parsedate(modified["expires"])[:6]) > datetime.now() + + expected = datetime(*parsedate(modified["expires"])[:6], tzinfo=timezone.utc) + assert expected > datetime.now(timezone.utc) def test_last_modified_is_not_used_when_cache_control_present(self): resp = DummyResponse( @@ -185,7 +187,8 @@ def test_last_modified_is_used_when_cache_control_public(self): ) modified = self.heuristic.update_headers(resp) assert ["expires"] == list(modified.keys()) - assert datetime(*parsedate(modified["expires"])[:6]) > datetime.now() + expected = datetime(*parsedate(modified["expires"])[:6], tzinfo=timezone.utc) + assert expected > datetime.now(timezone.utc) def test_warning_not_added_when_response_more_recent_than_24_hours(self): resp = DummyResponse(200, {"Date": self.now, "Last-Modified": self.week_ago}) diff --git a/tests/test_storage_redis.py b/tests/test_storage_redis.py index 6d6f07c..ac5b22c 100644 --- a/tests/test_storage_redis.py +++ b/tests/test_storage_redis.py @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: Apache-2.0 -from datetime import datetime +from datetime import datetime, timezone from unittest.mock import Mock from cachecontrol.caches import RedisCache @@ -17,6 +17,11 @@ def test_set_expiration_datetime(self): self.cache.set("foo", "bar", expires=datetime(2014, 2, 2)) assert self.conn.setex.called + def test_set_expiration_datetime_aware(self): + self.cache.set("foo", "bar", + expires=datetime(2014, 2, 2, tzinfo=timezone.utc)) + assert self.conn.setex.called + def test_set_expiration_int(self): self.cache.set("foo", "bar", expires=600) assert self.conn.setex.called