From b5ad9d5754aaf2bd52de6cb1ccef3858cac44d1a Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Tue, 4 Oct 2022 11:28:33 +0200 Subject: [PATCH] Add option to set default_timezone #133 --- docs/schema.json | 26 +++++++++++-- mkdocs.yml | 1 + mkdocs_rss_plugin/plugin.py | 5 +++ mkdocs_rss_plugin/timezoner_pre39.py | 44 +++++++++++++++++++++ mkdocs_rss_plugin/timezoner_py39.py | 51 ++++++++++++++++++++++++ mkdocs_rss_plugin/util.py | 58 +++++++++++++++++++--------- 6 files changed, 163 insertions(+), 22 deletions(-) create mode 100644 mkdocs_rss_plugin/timezoner_pre39.py create mode 100644 mkdocs_rss_plugin/timezoner_py39.py diff --git a/docs/schema.json b/docs/schema.json index bb955126..67e6174c 100644 --- a/docs/schema.json +++ b/docs/schema.json @@ -4,7 +4,9 @@ "oneOf": [ { "markdownDescription": "https://guts.github.io/mkdocs-rss-plugin/", - "enum": ["rss"] + "enum": [ + "rss" + ] }, { "type": "object", @@ -45,13 +47,29 @@ "default": null, "properties": { "as_creation": { - "type": ["boolean", "string"] + "type": [ + "boolean", + "string" + ] }, "as_update": { - "type": ["boolean", "string"] + "type": [ + "boolean", + "string" + ] }, "datetime_format": { - "type": ["null", "string"] + "type": [ + "null", + "string" + ] + }, + "default_timezone": { + "type": [ + "null", + "string" + ], + "default": "UTC" } } }, diff --git a/mkdocs.yml b/mkdocs.yml index 414a1d5c..1e883d4f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -21,6 +21,7 @@ plugins: as_creation: "date" as_update: false datetime_format: "%Y-%m-%d %H:%M" + default_timezone: "Europe/Paris" image: https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/Feed-icon.svg/128px-Feed-icon.svg.png match_path: ".*" pretty_print: false diff --git a/mkdocs_rss_plugin/plugin.py b/mkdocs_rss_plugin/plugin.py index 4cfb60fe..ccbd07a0 100644 --- a/mkdocs_rss_plugin/plugin.py +++ b/mkdocs_rss_plugin/plugin.py @@ -64,6 +64,7 @@ def __init__(self): # dates source self.src_date_created = self.src_date_updated = "git" self.meta_datetime_format = None + self.meta_default_timezone = "UTC" # pages storage self.pages_to_filter = [] # prepare output feeds @@ -129,6 +130,9 @@ def on_config(self, config: config_options.Config) -> dict: self.meta_datetime_format = self.config.get("date_from_meta").get( "datetime_format", "%Y-%m-%d %H:%M" ) + self.meta_default_timezone = self.config.get("date_from_meta").get( + "default_timezone", "UTC" + ) logger.debug( "[rss-plugin] Dates will be retrieved from page meta (yaml " "frontmatter). The git log will be used as fallback." @@ -196,6 +200,7 @@ def on_page_content( source_date_creation=self.src_date_created, source_date_update=self.src_date_updated, meta_datetime_format=self.meta_datetime_format, + meta_default_timezone=self.meta_default_timezone, ) # handle custom URL parameters diff --git a/mkdocs_rss_plugin/timezoner_pre39.py b/mkdocs_rss_plugin/timezoner_pre39.py new file mode 100644 index 00000000..516fee23 --- /dev/null +++ b/mkdocs_rss_plugin/timezoner_pre39.py @@ -0,0 +1,44 @@ +#! python3 # noqa: E265 + + +""" + Manage timezones for pages date(time)s using zoneinfo module, added in Python 3.9. + +""" + +# ############################################################################ +# ########## Libraries ############# +# ################################## + +# standard library +import logging +from datetime import datetime +from functools import lru_cache + +# ############################################################################ +# ########## Globals ############# +# ################################ + + +logger = logging.getLogger("mkdocs.mkdocs_rss_plugin") + + +# ############################################################################ +# ########## Functions ########### +# ################################ + + +@lru_cache(typed=True) +def set_datetime_zoneinfo( + input_datetime: datetime, config_timezone: str = None +) -> datetime: + """_summary_ + + :param input_datetime: _description_ + :type input_datetime: datetime + :param config_timezone: _description_ + :type config_timezone: str + :return: _description_ + :rtype: datetime + """ + pass diff --git a/mkdocs_rss_plugin/timezoner_py39.py b/mkdocs_rss_plugin/timezoner_py39.py new file mode 100644 index 00000000..034ecb7b --- /dev/null +++ b/mkdocs_rss_plugin/timezoner_py39.py @@ -0,0 +1,51 @@ +#! python3 # noqa: E265 + + +""" + Manage timezones for pages date(time)s using zoneinfo module, added in Python 3.9. + +""" + +# ############################################################################ +# ########## Libraries ############# +# ################################## + +# standard library +import logging +from datetime import datetime, timezone +from zoneinfo import ZoneInfo + +# ############################################################################ +# ########## Globals ############# +# ################################ + + +logger = logging.getLogger("mkdocs.mkdocs_rss_plugin") + + +# ############################################################################ +# ########## Functions ########### +# ################################ + + +def set_datetime_zoneinfo( + input_datetime: datetime, config_timezone: str = "UTC" +) -> datetime: + """Apply timezone to a naive datetime. + + :param input_datetime: offset-naive datetime + :type input_datetime: datetime + :param config_timezone: name of timezone as registered in IANA database, + defaults to "UTC". Example : Europe/Paris. + :type config_timezone: str, optional + + :return: offset-aware datetime + :rtype: datetime + """ + if input_datetime.tzinfo: + return input_datetime + elif not config_timezone: + return input_datetime.replace(tzinfo=timezone.utc) + else: + config_tz = ZoneInfo(config_timezone) + return input_datetime.replace(tzinfo=config_tz) diff --git a/mkdocs_rss_plugin/util.py b/mkdocs_rss_plugin/util.py index eea9c74d..132f5f87 100644 --- a/mkdocs_rss_plugin/util.py +++ b/mkdocs_rss_plugin/util.py @@ -7,8 +7,9 @@ # standard library import logging import ssl -from datetime import date, datetime -from email.utils import formatdate +import sys +from datetime import date, datetime, timedelta, timezone +from email.utils import format_datetime, formatdate from mimetypes import guess_type from pathlib import Path from typing import Iterable, Tuple @@ -21,12 +22,18 @@ from git import GitCommandError, GitCommandNotFound, InvalidGitRepositoryError, Repo from mkdocs.config.config_options import Config from mkdocs.structure.pages import Page -from mkdocs.utils import get_build_timestamp +from mkdocs.utils import get_build_datetime # package from mkdocs_rss_plugin import __about__ from mkdocs_rss_plugin.git_manager.ci import CiHandler +# conditional imports +if sys.version_info < (3, 9): + from mkdocs_rss_plugin.timezoner_pre39 import set_datetime_zoneinfo +else: + from mkdocs_rss_plugin.timezoner_py39 import set_datetime_zoneinfo + # ############################################################################ # ########## Globals ############# # ################################ @@ -36,11 +43,6 @@ "User-Agent": "{}/{}".format(__about__.__title__, __about__.__version__), } - -# ############################################################################ -# ########## Globals ############### -# ################################## - logger = logging.getLogger("mkdocs.mkdocs_rss_plugin") @@ -98,6 +100,7 @@ def get_file_dates( source_date_creation: str = "git", source_date_update: str = "git", meta_datetime_format: str = "%Y-%m-%d %H:%M", + meta_default_timezone: str = "UTC", ) -> Tuple[int, int]: """Extract creation and update dates from page metadata (yaml frontmatter) or \ git log for given file. @@ -124,6 +127,7 @@ def get_file_dates( dt_created = self.get_date_from_meta( date_metatag_value=in_page.meta.get(source_date_creation), meta_datetime_format=meta_datetime_format, + meta_datetime_timezone=meta_default_timezone, ) if isinstance(dt_created, str): logger.error(dt_created) @@ -133,6 +137,7 @@ def get_file_dates( dt_updated = self.get_date_from_meta( date_metatag_value=in_page.meta.get(source_date_update), meta_datetime_format=meta_datetime_format, + meta_datetime_timezone=meta_default_timezone, ) if isinstance(dt_updated, str): logger.error(dt_updated) @@ -170,14 +175,23 @@ def get_file_dates( " Trace: %s" % err ) self.git_is_valid = 0 + # convert timestamps into datetimes + if isinstance(dt_created, (str, float, int)) and dt_created: + dt_created = set_datetime_zoneinfo( + datetime.fromtimestamp(float(dt_created)), meta_default_timezone + ) + if isinstance(dt_updated, (str, float, int)) and dt_updated: + dt_updated = set_datetime_zoneinfo( + datetime.fromtimestamp(float(dt_updated)), meta_default_timezone + ) else: pass # return results if all([dt_created, dt_updated]): return ( - int(dt_created), - int(dt_updated), + dt_created, + dt_updated, ) else: logging.warning( @@ -185,8 +199,8 @@ def get_file_dates( % in_page.file.abs_src_path ) return ( - get_build_timestamp(), - get_build_timestamp(), + get_build_datetime(), + get_build_datetime(), ) def get_authors_from_meta(self, in_page: Page) -> Tuple[str] or None: @@ -257,8 +271,11 @@ def get_categories_from_meta( return sorted(output_categories) def get_date_from_meta( - self, date_metatag_value: str, meta_datetime_format: str - ) -> float: + self, + date_metatag_value: str, + meta_datetime_format: str, + meta_datetime_timezone: str, + ) -> datetime: """Get date from page.meta handling str with associated datetime format and \ date already transformed by MkDocs. @@ -266,9 +283,11 @@ def get_date_from_meta( :type date_metatag_value: str :param meta_datetime_format: expected format of datetime :type meta_datetime_format: str + :param meta_datetime_timezone: timezone to use + :type meta_datetime_timezone: str - :return: datetime as timestamp - :rtype: float + :return: datetime + :rtype: datetime """ out_date = None try: @@ -285,7 +304,10 @@ def get_date_from_meta( err ) - return out_date.timestamp() + if not out_date.tzinfo: + out_date = set_datetime_zoneinfo(out_date, meta_datetime_timezone) + + return out_date def get_description_or_abstract(self, in_page: Page, chars_count: int = 160) -> str: """Returns description from page meta. If it doesn't exist, use the \ @@ -516,7 +538,7 @@ def filter_pages(pages: list, attribute: str, length: int) -> list: "guid": page.guid, "image": page.image, "link": page.url_full, - "pubDate": formatdate(getattr(page, attribute)), + "pubDate": format_datetime(dt=getattr(page, attribute)), "title": page.title, } )