ModifiedDate Extension for Sphinx¶
Updated: May 07, 2024
Idea Behind the Extension¶
When writing article in Hugo [1] you have to provide some date in date
field so that Hugo
can show it in the statically generated website. Every time you update one article, you have to manually update this field.
I was looking for some way to generate this date automatically in Sphinx [2]. There is a way to add current date using |today| [3] substitution. I was looking for similar kind of substitution which is capable of automatically fetching the current file’s modified date. But unfortunately, I’m not able to find any extension for Sphinx
which satisfy my need. So, I decided why not write one?
ModifiedDate Extension¶
So, I looked into the source code of Sphinx
which handles |today|
substitution [4], after understanding that code, I started implementing my own extension to Sphinx
and came up with my own substitution called |modifieddate|
. Here is my extension,
"""modifieddate.
This extension provides `|modifieddate|` substitution
.. moduleauthor:: Mohan R <mohan43u@gmail.com>
"""
import os
import datetime
from docutils import nodes
from docutils.transforms import Transform
from sphinx.util.i18n import format_date
from sphinx.locale import _
class ModifiedDateTransform(Transform):
"""ModifiedDateTransform class."""
default_priority = 210
def apply(self, **kwargs):
"""Transform modifieddate substitution."""
for ref in self.document.traverse(nodes.substitution_reference):
name = ref['refname']
if name == 'modifieddate':
source = self.document['source']
modifieddate = None
try:
modifieddate = os.stat(source).st_mtime
except Exception as exception:
raise Exception('failed to get modifieddate for %s' % (source)) from exception
if modifieddate is not None:
config = self.document.settings.env.config
modifieddate_fmt = config.today_fmt or _('%b %d, %Y')
timezone = datetime.timezone.utc
modifieddate = datetime.datetime.fromtimestamp(modifieddate,
timezone)
text = format_date(modifieddate_fmt, date=modifieddate,
language=config.language)
ref.replace_self(nodes.Text(text))
def setup(app):
"""extension."""
app.add_transform(ModifiedDateTransform)
return {
'version': '0.0.1',
'parallel_read_safe': True,
'parallel_write_safe': True
}
Automating Updated Date¶
Adding |modifieddate|
substitution is not enough to automate. Every time I update one article, the current modified date in the disk will change, but I need to preserve the timestamp when I create that article. So I wrote one small script which will look at the prefix of each article filename, If the filename contains a timestamp, then it compares the current modified date for that file in the disk with that timestamp from filename, if they are not equal, then the script will update the modified date to the timestamp from the filename. In this way, I make sure the modified date is always preserved to the timestamp when the article was written. Since the modified date in the disk and the timestamp from the filename got synced, |modifieddate|
substitution always return the timestamp from filename as intended.
If there is no timestamp in the filename, then my script will add the current time to the filename, this will make sure the filename of the article contains the original timestamp when it was created.
the date you see at the top of this article after “Updated:” field comes from |modifieddate| substitution
To be continued..