I can still reproduce this issue with mailcow Logs Viewer 2.7.0 and TZ=Europe/Berlin.
The fix introduced in 2.7.0 does correctly set the application PostgreSQL session to UTC, but the Dashboard → Recent Activity timestamps are still two hours ahead in my setup during CEST.
Environment
mailcow Logs Viewer: 2.7.0
TZ: Europe/Berlin
Host local time: CEST / UTC+2
Container and Python time are correct:
Host local: 2026-08-19 15:13 CEST
Host UTC: 2026-08-19 13:13 UTC
datetime.now(): 2026-08-19 15:13
datetime.utcnow(): 2026-08-19 13:13
The SQLAlchemy connection used by the application is also correctly pinned to UTC:
timezone : UTC
now() : 2026-08-19 13:13:28+00:00
localtimestamp : 2026-08-19 13:13:28
So the SET TIME ZONE 'UTC' fix from #19 is definitely active.
Reproducible example
For one message, PostgreSQL contained:
first_seen : 2026-08-19 16:35:11
last_seen : 2026-08-19 14:36:30.167601
created_at : 2026-08-19 12:36:30.167867
updated_at : 2026-08-19 12:36:30.167868
At that time, the Dashboard displayed:
16:36:30
The correct local time was approximately:
14:36:30 CEST
Netfilter timestamps are displayed correctly. The problem only affects the Dashboard Recent Activity messages.
Current code
The current 2.7.0 code, and also the current main branch, still use last_seen in backend/app/routers/stats.py:
recent = db.query(MessageCorrelation).order_by(
MessageCorrelation.last_seen.desc()
).limit(limit).all()
and:
"time": format_datetime_utc(msg.last_seen),
This means that a value such as:
14:36:30
is serialized as UTC and the browser then converts it to Europe/Berlin:
14:36:30Z
-> +02:00
16:36:30 CEST
Working workaround
For testing, I changed only the Recent Activity endpoint from last_seen to created_at:
recent = db.query(MessageCorrelation).order_by(
MessageCorrelation.created_at.desc()
).limit(limit).all()
and:
"time": format_datetime_utc(msg.created_at),
After restarting the container, this removes the +2 hour offset in the Dashboard.
For the example above:
created_at = 12:36 UTC
browser Europe/Berlin = 14:36 CEST
which matches the actual time.
I understand that created_at is probably not the ideal long-term timestamp because it represents when the Logs Viewer processed/created the correlation rather than necessarily the exact mail event time.
There was also a discussion in the mailcow community about using the Rspamd timestamp as a more reliable source-of-truth timestamp for messages:
https://community.mailcow.email/d/5704-mailcow-logs-viewer-a-faster-cool-way-to-monitor-your-emails?page=19
Could you please re-check #19 specifically for the /stats/recent-activity endpoint?
It looks like the PostgreSQL session timezone fix itself is working, but the Dashboard still uses a last_seen value which can result in the local timezone being applied a second time.
The local created_at change is only a workaround; an upstream solution using a consistently normalized event timestamp would obviously be preferable.