[RUFF] Enable DTZ rule (datetime timezone)#1041
Conversation
There was a problem hiding this comment.
Pull request overview
This PR enables Ruff’s DTZ (flake8-datetimez) ruleset repository-wide and updates code to use explicit UTC when constructing datetimes, eliminating naive datetime usage.
Changes:
- Enable
DTZinruff.toml. - Replace naive datetime construction patterns (
now(),utcnow(),fromtimestamp(),strptime()) with explicit UTC equivalents across the repo. - Opportunistically modernize some logging calls (avoid f-strings in logger calls; add UTC timestamps to logs).
Reviewed changes
Copilot reviewed 57 out of 57 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| x/skype_history/skype_history.py | Make fromtimestamp UTC-aware. |
| x/gatelet/server/models.py | Use UTC-aware datetime.now() for validity checks. |
| x/gatelet/server/endpoints/test_challenge.py | Update tests to use UTC-aware timestamps. |
| x/gatelet/server/endpoints/challenge.py | Use UTC-aware timestamps for nonce/session creation. |
| x/gatelet/server/endpoints/admin.py | Use UTC-aware timestamps for admin sessions and key actions. |
| x/gatelet/server/conftest.py | Use UTC-aware timestamps in fixtures/stubs. |
| x/gatelet/server/auth/test_key_path_auth.py | Use UTC-aware timestamps in tests. |
| x/gatelet/server/auth/test_key_auth.py | Use UTC-aware timestamps in tests. |
| x/gatelet/server/auth/test_handlers.py | Use UTC-aware timestamps in tests. |
| x/gatelet/server/auth/handlers.py | Use UTC-aware now() in session/admin auth logic. |
| x/gatelet/server/app.py | Use UTC-aware now() for admin session redirect check. |
| x/ember_evals/scenarios/regression.py | Make date parsing/comparison UTC-based. |
| x/ember_evals/runner.py | Make run metadata timestamps UTC-based. |
| x/cotrl/llm_rl_experiment.py | Use UTC-aware timestamps for experiment logging and duration. |
| x/claude_linter_v2/test_multiline_predicates.py | Use UTC-aware timestamps in predicate tests. |
| x/claude_linter_v2/test_mcp_tools.py | Use UTC-aware timestamps in tool logging tests. |
| x/claude_linter_v2/session/violations.py | Use UTC-aware timestamps in violation records. |
| x/claude_linter_v2/session/state.py | Use UTC-aware timestamps for session state updates. |
| x/claude_linter_v2/session/manager.py | Use UTC-aware timestamps; improve logger formatting. |
| x/claude_linter_v2/hooks/handler.py | Use UTC-aware timestamps; adjust logger formatting and log-level parsing. |
| x/claude_linter_v2/cli.py | Use UTC-aware expiry parsing; improve logger formatting. |
| x/claude_linter/precommit_runner.py | Use UTC-aware timestamps in debug logs. |
| x/claude_linter/cli.py | Use UTC-aware timestamps for cleanup cutoff and hook logs. |
| x/agent_server/presets.py | Use UTC-aware fromtimestamp; improve logger formatting. |
| wt/testing/conftest.py | Use UTC-aware timestamps in test fixtures. |
| wt/shared/protocol.py | Use UTC-aware timestamps in protocol collectors. |
| wt/server/wt_server.py | Use UTC-aware timestamps for request timing; tweak shutdown task attribute. |
| wt/server/types.py | Use UTC-aware _now() helper. |
| wt/server/handlers/status_handler.py | Use UTC-aware last_updated_at. |
| wt/integration/test_integration_cli_output_format.py | Use UTC-aware timestamps in integration tests. |
| wt/e2e/test_github_pr_display_variants.py | Use UTC-aware merged timestamp in tests. |
| trilium/search_hack.py | Use UTC-aware timestamps in embeddings metadata. |
| tana/export/convert.py | Make strptime results UTC-aware. |
| sysrw/run_eval.py | Use UTC-aware timestamps for output directories. |
| skills/proxmox_vm/vm_interact.py | Use UTC-aware timestamps for screenshot naming. |
| ruff.toml | Enable DTZ rules. |
| props/core/gepa/gepa_adapter.py | Use UTC-aware timestamps; improve logger formatting. |
| props/cli/cmd_db.py | Use UTC-aware timestamps for backups and listing. |
| llm/ultra_long_cot/ultra_long_cot_o4.py | Use UTC-aware timestamps for session timing/log naming. |
| llm/mcp/habitify/utils/date_utils.py | Use UTC-aware defaults and UTC-aware strptime parsing. |
| llm/mcp/habitify/test_habitify_client.py | Use UTC-based “today” in tests. |
| llm/mcp/habitify/cli.py | Use UTC-aware display dates; improve exception logging. |
| llm/html/llm_html/test_token.py | Use UTC-aware timestamps for token generation tests. |
| llm/hook_logging.py | Use UTC-aware fromtimestamp when emitting JSON logs. |
| gmail_archiver/planners/usps.py | Make parsed dates UTC-aware. |
| gmail_archiver/planners/test_square.py | Update expected datetimes to be UTC-aware. |
| gmail_archiver/planners/test_anthropic.py | Update expected datetimes to be UTC-aware. |
| gmail_archiver/planners/square.py | Make parsed transaction datetime UTC-aware. |
| gmail_archiver/planners/anthem_reimbursement.py | Make parsed care date UTC-aware. |
| gmail_archiver/planners/aliexpress.py | Normalize file formatting; make delivered date UTC-aware. |
| finance/reconcile/splitwise_lib.py | Make parsed API datetime UTC-aware. |
| finance/reconcile/cli.py | Use UTC-aware timestamps for notes; make parsed dates UTC-aware. |
| devinfra/claude/hook_daemon/session_start/handler.py | Use UTC-aware session start timestamps. |
| debug/spice_lag/record.py | Use UTC-aware timestamps for recording labels. |
| debug/spice_lag/analyze.py | Make parsed clock times UTC-aware. |
| cluster/k8s/inventree/token-provisioner/provision.py | Use UTC-based “today” for expiry calculations. |
| agent_cli/cli.py | Use UTC-aware timestamps for transcript naming. |
| """Check if nonce is valid (not used and not expired).""" | ||
| return self.used_at is None and datetime.now() < self.expires_at | ||
| return self.used_at is None and datetime.now(tz=UTC) < self.expires_at |
There was a problem hiding this comment.
Same naive/aware mismatch risk here: self.expires_at comes from a DateTime (no timezone) column, but datetime.now(tz=UTC) is offset-aware. This will throw when evaluating is_valid unless the model/db schema uses timezone-aware datetimes or both sides are normalized.
| if not admin_session or admin_session.expires_at <= datetime.now(tz=UTC): | ||
| raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED) |
There was a problem hiding this comment.
admin_session.expires_at is stored in a SQLAlchemy DateTime column without timezone support, so it's likely offset-naive. Comparing it to datetime.now(tz=UTC) will raise TypeError (naive vs aware). Consider migrating the Gatelet datetime columns to DateTime(timezone=True) (and emitting UTC everywhere) or normalizing both sides to naive UTC before comparing.
x/claude_linter_v2/hooks/handler.py
Outdated
| try: | ||
| level_value = logging._nameToLevel.get(log_level.upper(), logging.INFO) | ||
| level_value = logging.getLevelName(log_level.upper()) | ||
| file_handler.setLevel(level_value) | ||
| except (AttributeError, KeyError): | ||
| file_handler.setLevel(logging.INFO) | ||
| logger.warning(f"Invalid log level '{log_level}', using INFO") | ||
| logger.warning("Invalid log level '%s', using INFO", log_level) |
There was a problem hiding this comment.
logging.getLevelName(log_level.upper()) returns a string (e.g. 'INFO' or 'Level INFO'), not the numeric level expected here, and setLevel(...) can raise ValueError for unknown strings (not caught by the current exception list). Use logging._nameToLevel.get(...), logging.getLevelNamesMapping().get(...), or pass log_level.upper() directly to setLevel and catch ValueError.
| """Check if session is currently valid.""" | ||
| return self.expires_at > datetime.now() | ||
| return self.expires_at > datetime.now(tz=UTC) |
There was a problem hiding this comment.
self.expires_at is stored in a SQLAlchemy DateTime column without timezone=True, so it's expected to be offset-naive. Comparing it to datetime.now(tz=UTC) will raise a naive/aware TypeError at runtime unless the DB layer is switched to timezone-aware datetimes (or values are normalized before comparison).
| labels=request.labels, | ||
| secrets=request.secrets, | ||
| started_at=datetime.datetime.utcnow().isoformat() + "Z", | ||
| started_at=datetime.datetime.now(tz=datetime.UTC).isoformat() + "Z", |
There was a problem hiding this comment.
datetime.datetime.now(tz=datetime.UTC).isoformat() already includes a UTC offset (e.g. +00:00). Appending 'Z' will produce an invalid timestamp like 2026-03-27T12:34:56+00:00Z. Prefer emitting RFC3339 UTC either via ...isoformat().replace('+00:00', 'Z') or formatting with strftime(...'%Y-%m-%dT%H:%M:%SZ').
|
|
||
| metadata.status = "ready" | ||
| metadata.ready_at = datetime.datetime.utcnow().isoformat() + "Z" | ||
| metadata.ready_at = datetime.datetime.now(tz=datetime.UTC).isoformat() + "Z" |
There was a problem hiding this comment.
Same timestamp formatting issue as started_at: isoformat() on an aware UTC datetime includes +00:00, so ... + 'Z' yields an invalid +00:00Z suffix. Please normalize to a single UTC designator (Z or +00:00) consistently.
| except Exception as exc: | ||
| metadata.status = "failed" | ||
| metadata.failed_at = datetime.datetime.utcnow().isoformat() + "Z" | ||
| metadata.failed_at = datetime.datetime.now(tz=datetime.UTC).isoformat() + "Z" |
There was a problem hiding this comment.
Same timestamp formatting issue as above: datetime.datetime.now(tz=datetime.UTC).isoformat() + 'Z' will render ...+00:00Z. Emit either RFC3339 Z or keep the +00:00 offset, but not both.
| expiration_time = self.created_at + validity_period | ||
| return self.revoked_at is None and datetime.now() < expiration_time | ||
| return self.revoked_at is None and datetime.now(tz=UTC) < expiration_time |
There was a problem hiding this comment.
This compares a timezone-aware datetime.now(tz=UTC) with self.created_at (and thus expiration_time) that come from SQLAlchemy DateTime columns defined without timezone=True (and with server_default=func.now() in the migration). Those values will be offset-naive, and Python will raise TypeError: can't compare offset-naive and offset-aware datetimes. Either migrate these columns to timezone-aware datetimes (preferred) or ensure both sides of the comparison are consistently naive/UTC.
d63b621 to
d743266
Compare
Adds `DTZ` (flake8-datetimez) to ruff.toml. Flags naive datetime construction without timezone. Fixes: datetime.now() → datetime.now(tz=UTC), fromtimestamp(t) → fromtimestamp(t, tz=UTC), strptime → .replace(tzinfo=UTC), date.today() → datetime.now(tz=UTC).date(). Uses `from datetime import UTC` (Python 3.11+). https://claude.ai/code/session_013WcexLQ8JPJG1j79uvMfCF
d743266 to
38a7129
Compare
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Summary
DTZ(flake8-datetimez) inruff.tomldatetime.now()→datetime.now(tz=UTC),fromtimestamp(t)→fromtimestamp(t, tz=UTC),strptime(...)→.replace(tzinfo=UTC)from datetime import UTC(Python 3.11+ module-level constant)https://claude.ai/code/session_013WcexLQ8JPJG1j79uvMfCF