fix(store): use UTC for all date arithmetic against SQLite columns

Greptile flagged that _cli_query's --since parsing uses datetime.now()
(local time) while first_seen is stored via SQLite's datetime('now') (UTC).
The same bug exists in three other call sites that compare against either
first_seen or run_date (both UTC):

- get_daily_cost: "today" defaults to local date, returns wrong day's cost
  near UTC midnight
- get_stats: "7 days ago" cutoff for runs_7d / successful_7d
- get_trending: "N days ago" cutoff for finding activity ranking
- _cli_query: "N days ago" cutoff for --since flag (Greptile's flag)

All four now use datetime.now(timezone.utc). Same root cause and same fix
as the test_get_new_findings_filters_by_date repair in the previous commit.
This commit is contained in:
Trevin Chow
2026-05-16 21:40:25 -07:00
parent eb2d7a0f37
commit 5d4f9ef2c5
+7 -6
View File
@@ -14,7 +14,7 @@ import argparse
import json import json
import sqlite3 import sqlite3
import sys import sys
from datetime import datetime, timedelta from datetime import datetime, timedelta, timezone
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
@@ -519,7 +519,7 @@ def get_daily_cost(date: Optional[str] = None) -> float:
conn = _connect() conn = _connect()
try: try:
if not date: if not date:
date = datetime.now().strftime("%Y-%m-%d") date = datetime.now(timezone.utc).strftime("%Y-%m-%d")
row = conn.execute( row = conn.execute(
"""SELECT COALESCE(SUM(token_cost), 0) as total """SELECT COALESCE(SUM(token_cost), 0) as total
FROM research_runs FROM research_runs
@@ -575,7 +575,7 @@ def get_stats() -> Dict[str, Any]:
topic_count = conn.execute("SELECT COUNT(*) FROM topics WHERE enabled = 1").fetchone()[0] topic_count = conn.execute("SELECT COUNT(*) FROM topics WHERE enabled = 1").fetchone()[0]
finding_count = conn.execute("SELECT COUNT(*) FROM findings").fetchone()[0] finding_count = conn.execute("SELECT COUNT(*) FROM findings").fetchone()[0]
week_ago = (datetime.now() - timedelta(days=7)).strftime("%Y-%m-%d") week_ago = (datetime.now(timezone.utc) - timedelta(days=7)).strftime("%Y-%m-%d")
runs_7d = conn.execute( runs_7d = conn.execute(
"SELECT COUNT(*) FROM research_runs WHERE run_date >= ?", (week_ago,) "SELECT COUNT(*) FROM research_runs WHERE run_date >= ?", (week_ago,)
).fetchone()[0] ).fetchone()[0]
@@ -621,7 +621,7 @@ def get_trending(days: int = 7) -> List[Dict[str, Any]]:
"""Get topics ranked by recent finding activity.""" """Get topics ranked by recent finding activity."""
conn = _connect() conn = _connect()
try: try:
since = (datetime.now() - timedelta(days=days)).strftime("%Y-%m-%d") since = (datetime.now(timezone.utc) - timedelta(days=days)).strftime("%Y-%m-%d")
rows = conn.execute( rows = conn.execute(
"""SELECT t.name, t.id, """SELECT t.name, t.id,
COUNT(f.id) as new_findings, COUNT(f.id) as new_findings,
@@ -725,9 +725,10 @@ def _cli_query(args):
since = None since = None
if args.since: if args.since:
# Parse duration like "7d", "30d" # Parse duration like "7d", "30d". Use UTC to match SQLite's
# datetime('now') which writes first_seen in UTC.
days = int(args.since.rstrip("d")) days = int(args.since.rstrip("d"))
since = (datetime.now() - timedelta(days=days)).strftime("%Y-%m-%d") since = (datetime.now(timezone.utc) - timedelta(days=days)).strftime("%Y-%m-%d")
findings = get_new_findings(topic["id"], since) findings = get_new_findings(topic["id"], since)
print(json.dumps({"topic": topic["name"], "findings": findings, "count": len(findings)}, default=str)) print(json.dumps({"topic": topic["name"], "findings": findings, "count": len(findings)}, default=str))