Skip to content

fix(alerts): treat naive detected_at datetimes as UTC before timezone conversion - #2341

Open
zerafachris wants to merge 2 commits into
elementary-data:masterfrom
zerafachris:fix/detected-at-naive-utc-timezone
Open

fix(alerts): treat naive detected_at datetimes as UTC before timezone conversion#2341
zerafachris wants to merge 2 commits into
elementary-data:masterfrom
zerafachris:fix/detected-at-naive-utc-timezone

Conversation

@zerafachris

@zerafachris zerafachris commented Aug 31, 2026

Copy link
Copy Markdown

Problem

Fixes #2304.

AlertModel.__init__ calls detected_at.astimezone(...) on a naive datetime value. Python's astimezone() interprets a naive datetime as the local wall-clock time, not UTC.

Since the alerts table stores timestamps in UTC, running edr monitor on a host whose timezone is not UTC produces alerts where the "Time:" field shows the UTC value labeled with the local timezone name:

# Container with TZ=Asia/Tokyo; test actually ran at 18:08:22 JST (= 09:08:22 UTC)
Time: 2026-07-22 09:08:22 JST   ← UTC value with JST label (wrong)

This misled users into thinking alerts were hours delayed.

Fix

Before calling astimezone(), attach UTC tzinfo to any naive datetime so the conversion is always relative to UTC:

detected_at_utc_aware = (
    detected_at.replace(tzinfo=dt_timezone.utc)
    if detected_at.tzinfo is None
    else detected_at
)
self.detected_at_utc = detected_at_utc_aware
self.detected_at = detected_at_utc_aware.astimezone(...)

detected_at_utc is also updated to hold the timezone-aware value for consistency (previously it held the raw naive input).

Test

Added tests/unit/alerts/test_alert_detected_at_timezone.py with three cases:

  • Naive UTC → JST: 09:08:22 UTC must become 18:08:22 JST
  • Already-aware UTC → JST: no double-conversion
  • None → fields stay None, detected_at_str == "N/A"

Summary by CodeRabbit

  • Bug Fixes

    • Corrected alert timestamp handling so timezone-free timestamps are consistently interpreted as UTC.
    • Preserved correct behavior for timestamps that already include timezone information.
    • Prevented alert filtering errors for alerts with a “reused” status.
    • Alerts marked as reused are now excluded by default status filters.
  • Improvements

    • Added support for the new “reused” alert status.

zerafachris and others added 2 commits August 31, 2026 16:35
dbt State (v1.11+) introduces a "reused" run status for models that
are skipped because their state has not changed. This caused a
ValueError when elementary tried to construct alert objects from those
run results, since "reused" was not a member of the Status enum.

Adding REUSED = "reused" lets alert objects be created normally; the
default status filter (FAIL/ERROR/RUNTIME_ERROR/WARN) correctly
excludes "reused" alerts from notifications, matching dbt's intent
that a reused model is not an actionable event.

Fixes elementary-data#2311.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… conversion

Python's datetime.astimezone() interprets a naive datetime as *local wall-clock
time*, not UTC.  Since the alerts table stores timestamps in UTC, running
edr on a host whose timezone is not UTC produced alerts where the "Time:" field
showed the UTC value labeled with the local timezone name (e.g. 09:08 UTC
displayed as "09:08 JST").

Fix: attach UTC tzinfo to the naive datetime before calling astimezone(), so
the conversion is always relative to UTC regardless of the process timezone.
Also update detected_at_utc to hold the tz-aware value for consistency.

Fixes elementary-data#2304

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

👋 @zerafachris
Thank you for raising your pull request.
Please make sure to add tests and document all user-facing changes.
You can do this by editing the docs files in this pull request.

@coderabbitai

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 95f9688f-8d48-4a8d-96a8-f5d9e44576f3

📥 Commits

Reviewing files that changed from the base of the PR and between a2000b0 and 50655e5.

📒 Files selected for processing (4)
  • elementary/monitor/alerts/alert.py
  • elementary/monitor/data_monitoring/schema.py
  • tests/unit/alerts/test_alert_detected_at_timezone.py
  • tests/unit/monitor/api/alerts/test_alert_filters.py

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

AlertModel now treats naive detected timestamps as UTC. The monitoring status schema now includes reused. Regression tests cover timestamp conversion, missing timestamps, and reused-status filtering.

Changes

Alert timestamp normalization

Layer / File(s) Summary
Normalize alert timestamps
elementary/monitor/alerts/alert.py, tests/unit/alerts/test_alert_detected_at_timezone.py
Naive detected_at values receive UTC timezone information before conversion. Tests cover naive, aware, and missing timestamps.

Reused alert status

Layer / File(s) Summary
Handle reused status in filtering
elementary/monitor/data_monitoring/schema.py, tests/unit/monitor/api/alerts/test_alert_filters.py
Status.REUSED maps to "reused". Filtering no longer raises ValueError for reused alerts and excludes them under the default status filter.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 50655

The change consistently treats naive alert timestamps as UTC before timezone conversion, preventing incorrect displayed times. No actionable merge-blocking risk remains beyond normal checks and review.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The added Status.REUSED enum member and its regression test are unrelated to the requirements of linked issue #2304. The timestamp changes are in scope. Move the Status.REUSED changes to a separate pull request or link an issue that explicitly requires support for reused model statuses.
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: treating naive alert timestamps as UTC before timezone conversion.
Linked Issues check ✅ Passed The timestamp changes and regression tests satisfy issue #2304. Naive values are treated as UTC, aware values remain correct, and null values remain supported.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

Warning

⚠️ This pull request shows signs of AI-generated slop (description_diff_mismatch). It has been flagged by CodeRabbit slop detection and should be reviewed carefully.

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.

Alert "Time" shows the UTC value labeled with the local timezone when the host timezone is not UTC

1 participant