You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Describe the bug
Because of an interaction between the way that the Sphinx html builder generates a last_updated datetime, and the assumptions made by the underlying babel library on formatting the time, the "last updated time" used in Sphinx templates is assumed to be a UTC time value, when that's not the case.
To Reproduce
Use a generic Sphinx document with a template that includes "last_updated" variable in the output.
In conf.py set html_last_updated_fmt = "%Y-%m-%d (%H:%M %Z)" ; this will prompt the last updated date-time format used to print out the timezone info as part of the formatted date-time string.
Build the docs: the timezone in the output will get reported as UTC, but the actual time will use whatever date-time/timezone your local system clock is set to.
Expected behavior
Either the build code should properly determine what the local platform's timezone is in order to capture that information in the datetime passed on to the babel formatter, or the builder code should always create a datetime that captures the system clock time in UTC so that babel correctly reports the timezone when it claims the timezone is UTC.
Environment info
OS: MacOS 10.14.5
Python version: 3.7.3
Sphinx version: 2.1.2
Sphinx extensions: HTML builder used
Extra tools: n/a
Additional context
Starting about line 482 in /sphinx/builders/html is this call to the i18n.format_date function (note that this only passes in a format string, and a locale/language, not a date-time value itself):
...
# format the "last updated on" string, only once is enough since it# typically doesn't include the time of daylufmt=self.config.html_last_updated_fmtiflufmtisnotNone:
self.last_updated=format_date(lufmtor_('%b %d, %Y'),
language=self.config.language)
else:
self.last_updated=None
...
Starting about line 280 in /sphinx/util/i18n.py is the definition for the format_date function:
...
defformat_date(format, date=None, language=None):
# type: (str, datetime, str) -> strifdateisNone:
# If time is not specified, try to use $SOURCE_DATE_EPOCH variable# See https://wiki.debian.org/ReproducibleBuilds/TimestampsProposalsource_date_epoch=os.getenv('SOURCE_DATE_EPOCH')
ifsource_date_epochisnotNone:
date=datetime.utcfromtimestamp(float(source_date_epoch))
else:
date=datetime.now()
...
If the build environment does not have SOURCE_DATE_EPOCH set, then the date value that's eventually passed on to the babel formatter is a naive datetime object with the call to datetime.now().
The babel formatter makes the decision that if it gets a datetime that's timezone-naive to interpret the value as a UTC datetime, and thus the expansion/formatting of %Z reports the time as UTC even though it won't be, unless your system clock is set to UTC.
I believe that one way to fix this would be to change the assignment to date like this:
This would ensure that the datetime used for the build time is always a UTC time value (and the UTC version of the 'now' system time). This would ensure that at least "last updated" would always report the UTC value of when the build happened. However, it would also make localization of that time value to a timezone probably challenging for Sphinx users.
datetime.now() can accept a tzinfo so that the datetime created is not a timezone naive value, and then the babel formatter will properly render whatever the timezone actually is, but this raises the question about where that tzinfo would actually come from. Some options might be:
Determine it from the local system somehow?
Have it be a configured value in conf.py?
I think the more practical answer is to just use datetime.utcnow() to get the UTC time value, since if the local system had SOURCE_DATE_EPOCH set, the time would be a UTC time.
I will submit a PR that makes this change.
The text was updated successfully, but these errors were encountered:
Describe the bug
Because of an interaction between the way that the Sphinx html builder generates a last_updated datetime, and the assumptions made by the underlying
babel
library on formatting the time, the "last updated time" used in Sphinx templates is assumed to be a UTC time value, when that's not the case.To Reproduce
html_last_updated_fmt = "%Y-%m-%d (%H:%M %Z)"
; this will prompt the last updated date-time format used to print out the timezone info as part of the formatted date-time string.Expected behavior
Either the build code should properly determine what the local platform's timezone is in order to capture that information in the
datetime
passed on to the babel formatter, or the builder code should always create adatetime
that captures the system clock time in UTC so that babel correctly reports the timezone when it claims the timezone is UTC.Environment info
Additional context
/sphinx/builders/html
is this call to the i18n.format_date function (note that this only passes in a format string, and a locale/language, not a date-time value itself):/sphinx/util/i18n.py
is the definition for theformat_date
function:If the build environment does not have
SOURCE_DATE_EPOCH
set, then the date value that's eventually passed on to the babel formatter is a naive datetime object with the call todatetime.now()
.%Z
reports the time as UTC even though it won't be, unless your system clock is set to UTC.I believe that one way to fix this would be to change the assignment to date like this:
This would ensure that the datetime used for the build time is always a UTC time value (and the UTC version of the 'now' system time). This would ensure that at least "last updated" would always report the UTC value of when the build happened. However, it would also make localization of that time value to a timezone probably challenging for Sphinx users.
datetime.now()
can accept atzinfo
so that the datetime created is not a timezone naive value, and then the babel formatter will properly render whatever the timezone actually is, but this raises the question about where thattzinfo
would actually come from. Some options might be:I think the more practical answer is to just use
datetime.utcnow()
to get the UTC time value, since if the local system hadSOURCE_DATE_EPOCH
set, the time would be a UTC time.I will submit a PR that makes this change.
The text was updated successfully, but these errors were encountered: