Skip to content

TimeStamper ignores utc flag for custom format string #712

@m-endra

Description

@m-endra

Hello,

There may be a regression in Structlog 25.2.0 as a result of #709. My unit tests found that it always displays time in local timezone, even if you set flag utc=True. For ISO format it works fine, but for custom format, it strips the UTC timezone and applies a local one.

Easiest way to reproduce is:

event_dict = {}
time_stamper_local = structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S %Z", utc=False)
print(time_stamper_local(None, None, event_dict))
time_stamper_utc = structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S %Z", utc=True)
print(time_stamper_utc(None, None, event_dict))

# prints
{'timestamp': '2025-03-14 13:33:03 CET'}
{'timestamp': '2025-03-14 13:33:03 CET'}



In structlog/src/structlog/processors.py
TimeStamper(fmt="%Y-%m-%d %H:%M:%S %Z", utc=True) effectively does:

datetime.datetime.now(tz=datetime.timezone.utc).astimezone()

Before applying astimezone() the object has UTC timezone, then it is replaced by local timezone. As per datetime documentation:

If called without arguments (or with tz=None) the system local time zone is assumed for the target time zone.




Not sure if this is intentional, but this behavior changed from Structlog 25.1.0. How about handling UTC separately, the same way it is done for ISO format?

diff --git a/structlog/.venv/lib/python3.10/site-packages/structlog/processors.py b/structlog/.venv/lib/python3.10/site-packages/structlog/processors.py
index 4ef7578..39e1532 100644
--- a/structlog/.venv/lib/python3.10/site-packages/structlog/processors.py
+++ b/structlog/.venv/lib/python3.10/site-packages/structlog/processors.py
@@ -553,12 +553,18 @@ def _make_stamper(
 
         return stamper_iso_local
 
-    def stamper_fmt(event_dict: EventDict) -> EventDict:
+    def stamper_fmt_local(event_dict: EventDict) -> EventDict:
         event_dict[key] = now().astimezone().strftime(fmt)
+        return event_dict
 
+    def stamper_fmt_utc(event_dict: EventDict) -> EventDict:
+        event_dict[key] = now().strftime(fmt)
         return event_dict
 
-    return stamper_fmt
+    if utc:
+        return stamper_fmt_utc
+
+    return stamper_fmt_local
 
 
 class MaybeTimeStamper:

Let me know if this makes sense or I misconfigured something,

Thanks,
Marcin Endraszka

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions