Skip to content

Deprecate naive datetime in StreamResponse.last_modified setter#13047

Draft
amitmishra11 wants to merge 1 commit into
aio-libs:masterfrom
amitmishra11:fix/warn-naive-datetime-last-modified
Draft

Deprecate naive datetime in StreamResponse.last_modified setter#13047
amitmishra11 wants to merge 1 commit into
aio-libs:masterfrom
amitmishra11:fix/warn-naive-datetime-last-modified

Conversation

@amitmishra11

Copy link
Copy Markdown

What do these changes do?

Emit a DeprecationWarning when a naive datetime.datetime is passed
to StreamResponse.last_modified. Previously, naive datetimes were
silently accepted, but utctimetuple() on a naive datetime returns the
local time fields unchanged, which then gets labelled as GMT in the
Last-Modified HTTP header. This means that calling
resp.last_modified = datetime.now() (which returns local time) would
produce an incorrect header value -- the local time would be stored
verbatim as if it were UTC.

The fix adds a clear deprecation warning directing users to timezone-aware
datetimes (e.g. datetime.now(datetime.timezone.utc)).

Are there changes in behavior for the user?

Yes: passing a naive datetime now emits a DeprecationWarning. The
actual value stored in the header is unchanged from the previous
behaviour. A future release can raise a TypeError or ValueError for
naive datetimes once the deprecation period has elapsed.

Is it a substantial burden for the maintainers to support this?

No. It is a one-line guard plus a warning in one setter, with a single
new test. The warning text is self-explanatory and points directly to
the fix.

Related issue number

Fixes #5304

Checklist

  • I think the code is well written
  • Unit tests for the changes exist
  • Documentation reflects the changes
  • Add a new news fragment into the CHANGES/ folder
Test output
============================= test session starts =============================
collected 10 items

tests/test_web_response.py::test_last_modified_initial PASSED
tests/test_web_response.py::test_last_modified_string PASSED
tests/test_web_response.py::test_last_modified_timestamp PASSED
tests/test_web_response.py::test_last_modified_datetime PASSED
tests/test_web_response.py::test_last_modified_datetime_naive_warns PASSED
tests/test_web_response.py::test_last_modified_reset PASSED
tests/test_web_response.py::test_last_modified_invalid_type PASSED
tests/test_web_response.py::test_last_modified_string_invalid[xxyyzz] PASSED
tests/test_web_response.py::test_last_modified_string_invalid[...] PASSED
tests/test_web_response.py::test_last_modified_string_invalid[...] PASSED
===================== 10 passed in 0.29s ==============================

Drafted with Claude Sonnet 4.6; reviewed by amitmishra11.

Passing a naive datetime to StreamResponse.last_modified is ambiguous
because datetime.utctimetuple() on a naive datetime returns the local
fields unchanged, which then gets labelled as GMT in the header.
This means datetime.now() (local time) would produce an incorrect
Last-Modified header.

Emit a DeprecationWarning so users are guided to use timezone-aware
datetimes such as datetime.now(datetime.timezone.utc).

Fixes aio-libs#5304
@psf-chronographer psf-chronographer Bot added the bot:chronographer:provided There is a change note present in this PR label Jul 3, 2026
@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.96%. Comparing base (9d183e3) to head (6d81eca).
⚠️ Report is 2 commits behind head on master.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #13047   +/-   ##
=======================================
  Coverage   98.96%   98.96%           
=======================================
  Files         131      131           
  Lines       48156    48163    +7     
  Branches     2499     2500    +1     
=======================================
+ Hits        47656    47663    +7     
  Misses        376      376           
  Partials      124      124           
Flag Coverage Δ
Autobahn 22.21% <14.28%> (-0.01%) ⬇️
CI-GHA 98.90% <100.00%> (-0.01%) ⬇️
OS-Linux 98.67% <100.00%> (+<0.01%) ⬆️
OS-Windows 97.04% <100.00%> (+<0.01%) ⬆️
OS-macOS 97.95% <100.00%> (-0.01%) ⬇️
Py-3.10 98.14% <100.00%> (-0.01%) ⬇️
Py-3.11 98.41% <100.00%> (+<0.01%) ⬆️
Py-3.12 98.50% <100.00%> (+<0.01%) ⬆️
Py-3.13 98.47% <100.00%> (+<0.01%) ⬆️
Py-3.14 98.49% <100.00%> (+<0.01%) ⬆️
Py-3.14t 97.58% <100.00%> (-0.01%) ⬇️
Py-pypy-3.11 97.45% <100.00%> (-0.01%) ⬇️
VM-macos 97.95% <100.00%> (-0.01%) ⬇️
VM-ubuntu 98.67% <100.00%> (+<0.01%) ⬆️
VM-windows 97.04% <100.00%> (+<0.01%) ⬆️
cython-coverage 38.12% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@codspeed-hq

codspeed-hq Bot commented Jul 3, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 83 untouched benchmarks
⏩ 83 skipped benchmarks1


Comparing amitmishra11:fix/warn-naive-datetime-last-modified (6d81eca) with master (9d183e3)2

Open in CodSpeed

Footnotes

  1. 83 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on master (e3774b4) during the generation of this report, so 9d183e3 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:chronographer:provided There is a change note present in this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Naive datetime is interpreted as UTC when set last_modified

1 participant