@@ -344,14 +344,69 @@ matches the default for many applications, including Django when
344344``TIME_ZONE `` is left at its ``UTC `` default. If the application uses a
345345different timezone, set ``TZ `` to that same timezone instead.
346346
347- Note that there is no Apache log format directive which will solve this by
348- forcing UTC. Both ``ErrorLogFormat `` and ``CustomLog `` / ``LogFormat ``
349- format their ``%t `` timestamps from the process's local time, derived from
350- ``TZ ``; even the compact ISO 8601 forms such as ``%{cu}t `` use local time,
351- not UTC. The ``%{cuz}t `` form (Apache 2.4.58 and later) at least appends
352- the numeric timezone offset, so lines stamped in different timezones can
353- be told apart rather than silently misread, but it does not make them
354- consistent.
347+ Where you are not able to change how Apache is started, a log format
348+ directive offers a partial mitigation, though not a cure. It is worth
349+ being clear about its limits, because reaching for the log configuration
350+ is the natural first instinct. No ``ErrorLogFormat `` or ``CustomLog `` /
351+ ``LogFormat `` option can force these timestamps to a fixed zone such as
352+ UTC; ``%t `` is always formatted from the emitting process's local time,
353+ derived from ``TZ ``. What a log format *can * do is make the timezone of
354+ each line explicit, so that lines stamped by processes in different
355+ timezones can be told apart rather than silently misread.
356+
357+ In an ``ErrorLogFormat `` the ``%t `` field accepts a small set of single
358+ letter options inside the braces:
359+
360+ * ``%t `` produces the default ``ctime `` style, ``Fri Jun 05 08:42:44 2026 ``.
361+ * ``%{u}t `` is the same but with microseconds. This is what the Apache
362+ default ``ErrorLogFormat `` uses.
363+ * the ``c `` option switches to the compact ISO 8601 form,
364+ ``2026-06-05 08:42:36 ``.
365+ * the ``z `` option (Apache 2.4.58 and later) appends the numeric timezone
366+ offset, ``+1000 ``.
367+
368+ The options combine, so ``%{cu}t `` is compact ISO 8601 with microseconds
369+ but still no offset, and ``%{cuz}t `` is that with the offset added on the
370+ end.
371+
372+ To keep the familiar default layout and simply add the timezone offset,
373+ take the Apache default ``ErrorLogFormat `` and change its ``%{u}t `` field
374+ to ``%{uz}t ``::
375+
376+ ErrorLogFormat "[%{uz}t] [%-m:%l] [pid %P:tid %T] %7F: %E: [client\ %a] %M% ,\ referer\ %{Referer}i"
377+
378+ A line then looks like the following, identical to the default apart from
379+ the trailing ``+1000 ``::
380+
381+ [Fri Jun 05 08:42:29.986168 2026 +1000] [mpm_event:notice] [pid 37192:tid 140704575428864] AH00489: ...
382+
383+ If you prefer the compact ISO 8601 timestamp, use ``%{cuz}t `` instead::
384+
385+ ErrorLogFormat "[%{cuz}t] [%-m:%l] [pid %P:tid %T] %7F: %E: [client\ %a] %M% ,\ referer\ %{Referer}i"
386+
387+ [2026-06-05 08:42:36.978307 +1000] [mpm_event:notice] [pid 37243:tid 140704575428864] AH00489: ...
388+
389+ A word of caution about the syntax. The ``c ``, ``u `` and ``z `` letters are
390+ options, not ``strftime `` conversions, and must appear directly inside the
391+ braces with no leading ``% ``. If you write ``%{%cuz}t `` instead of
392+ ``%{cuz}t ``, the leading ``% `` switches the field into ``strftime `` mode,
393+ where ``%c `` expands to the C library's locale date and time and the
394+ remaining ``uz `` is copied through literally, producing nonsense such as
395+ ``Fri Jun 5 08:42:25 2026uz ``.
396+
397+ This distinction also matters across platforms. The ``c ``, ``u `` and ``z ``
398+ options are formatted by Apache itself and behave identically on Linux,
399+ macOS and Windows. The ``strftime `` forms, such as ``%{%d/%b/%Y %T}t ``,
400+ are passed to the platform C library's ``strftime(3) ``, whose set of
401+ supported conversions varies; Windows in particular omits or alters
402+ several of them, with ``%z `` for example yielding a timezone name rather
403+ than a numeric offset. Preferring ``%{cuz}t `` over a hand written
404+ ``strftime `` string therefore also gives consistent output everywhere.
405+
406+ Adding the offset lets the two timezones be distinguished, but it does not
407+ make them consistent. The only way to have every process agree remains to
408+ start the Apache parent process with the same ``TZ `` the application uses,
409+ as described above.
355410
356411Be aware also that this only addresses divergence between processes. If a
357412single daemon process hosts multiple sub interpreters that set different
0 commit comments