Skip to content

Bug: Cookie Expires labels a non-UTC aware datetime as GMT without converting it #8671

Open

Description

@LemonCondor

Expected Behaviour

A Cookie whose expires is a timezone-aware datetime should send that instant in GMT, as RFC 6265 requires for Expires. datetime(2026, 1, 1, 12, tzinfo=timezone(timedelta(hours=2))) is 10:00 UTC, so the header should read Expires=Thu, 01 Jan 2026 10:00:00 GMT.

Current Behaviour

Cookie._format_date formats the datetime with strftime and a hard-coded GMT suffix, without converting it to UTC first. The local wall-clock time is labelled GMT, so the cookie expires earlier or later by the UTC offset: Expires=Thu, 01 Jan 2026 12:00:00 GMT.

Code snippet

from datetime import datetime, timedelta, timezone
from aws_lambda_powertools.shared.cookies import Cookie

cookie = Cookie("a", "b", expires=datetime(2026, 1, 1, 12, tzinfo=timezone(timedelta(hours=2))))
print(str(cookie))  # a=b; Secure; Expires=Thu, 01 Jan 2026 12:00:00 GMT

Possible Solution

Convert an aware datetime to UTC before formatting (expires.astimezone(timezone.utc)); naive datetimes keep today's behaviour. I have this fix with a regression test in tests/unit/test_cookie_class.py (fails on develop, passes with the change) and can open a PR once this is triaged.

Steps to Reproduce

Run the snippet above on develop (5785377).

Powertools for AWS Lambda (Python) version

latest (3.37.0, develop)

AWS Lambda function runtime

3.13

Packaging format used

PyPi

Debugging logs

N/A

Activity

  1. SumiRann1 commented on Oct 29, 2026

    @SumiRann1
    Contributor

    Hii @LemonCondor, I want to work on this!

  2. leandrodamascena commented on Oct 29, 2026

    @leandrodamascena
    Contributor

    Thanks @LemonCondor, confirmed. _format_date stamps GMT on whatever wall-clock time it gets, so an aware datetime outside UTC is off by its offset. Converting aware values with astimezone(timezone.utc) and leaving naive ones alone is the right fix. It's yours, open the PR with the test. This goes into 3.40.0.

  3. added this to the 3.40.0 milestone on Oct 29, 2026
  4. anishmehta24 commented on Oct 30, 2026

    @anishmehta24

    One more thing in _format_date, in case it fits the same fix: %a and %b follow the process LC_TIME, so with a German locale the header reads Expires=Fr, 30 Okt 2026 07:28:00 GMT, which browsers cannot parse. email.utils.format_datetime(value, usegmt=True) after the UTC conversion gives the English names regardless of locale. Same root cause as #8706 for feature flags.

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions