Skip to content

fix(backend): use timezone.now() for licensing and analytics timestamps - #1002

Open
omlahore wants to merge 1 commit into
phasehq:mainfrom
omlahore:fix/naive-datetime-licensing
Open

fix(backend): use timezone.now() for licensing and analytics timestamps#1002
omlahore wants to merge 1 commit into
phasehq:mainfrom
omlahore:fix/naive-datetime-licensing

Conversation

@omlahore

@omlahore omlahore commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #972, same class of bug in three more places. USE_TZ = True and TIME_ZONE = "UTC", and the backend already uses timezone.now() in 143 call sites against 7 naive ones. These are three of the seven.

1. ee/licensing/utils.py:144 — naive datetime into a DateTimeField

"issued_at": datetime.now(),

ActivatedPhaseLicense.issued_at is a DateTimeField and this reaches it through objects.create(**fields). Under USE_TZ Django warns and interprets the value as UTC, but it was produced in server-local time, so a non-UTC host stores an issued_at that is wrong by its offset. This is exactly what #972 fixed for token expiry.

activated_at on the same dict is changed for consistency only. That column is auto_now_add=True, so Django overwrites whatever is passed and the current value is not a live bug.

2. ee/licensing/verifier.py:33 — license expiry judged in server-local time

if datetime.now().date() > data["expires_at"]:

expires_at is parsed from the signed license as a plain date, so this compares it against the server's local date. Enterprise licences expire early on any host east of UTC.

At 2026-09-02 20:30 UTC, for a licence with expires_at = 2026-09-02:

server timezone datetime.now().date() timezone.now().date() expired?
UTC 2026-09-02 2026-09-02 no
Asia/Kolkata 2026-09-03 2026-09-02 yes
Pacific/Auckland 2026-09-03 2026-09-02 yes

The licence dies up to a day early depending on where the instance runs.

3. backend/schema.py:1408 — analytics window anchored to local time

end_date = datetime.now()  # current time

end_date and the start_date derived from it are used to bucket decrypt counts against timezone-aware rows, so the chart window is shifted by the host's UTC offset.

Deliberately not touched

  • api/views/kms.py:28 and backend/schema.py:1122 both do datetime.now().timestamp() * 1000. timestamp() interprets a naive value as local time, so the epoch value is correct and changing these would be churn.
  • ee/integrations/secrets/dynamic/aws/utils.py:210 uses datetime.utcnow() for AWS IAM tag values. Nothing reads those tags back, so it is a cosmetic and deprecation issue rather than a defect. Can send it separately if you want it.

Verification

pytest tests/ passes locally: 1503 passed. The suite does not need Postgres, so I can run it for backend changes from now on rather than leaving them unverified as I did on #972.

issued_at was written to a DateTimeField as a naive server-local datetime
under USE_TZ, the license expiry check compared a server-local date against
the license date, and the decrypt-count chart window started from a naive
now(). Matches the timezone.now() used in the other 143 call sites.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant