From ddb2edfd32058024df405cbb2678b99839a4dfda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20F=C3=A4hrmann?= Date: Thu, 21 Mar 2024 19:19:33 +0100 Subject: [PATCH] [formatter] fix local DST datetime offsets for ':O' 'O' would get the *current* local UTC offset and apply it to all 'datetime' objects it gets applied to. This would result in a wrong offset if the current offset includes DST and the target 'datetime' does not or vice-versa. 'O' now determines the correct local UTC offset while respecting DST for each individual 'datetime'. --- gallery_dl/formatter.py | 12 ++++++------ test/test_formatter.py | 28 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/gallery_dl/formatter.py b/gallery_dl/formatter.py index 6098fc61..b83cf21c 100644 --- a/gallery_dl/formatter.py +++ b/gallery_dl/formatter.py @@ -375,18 +375,18 @@ def _parse_offset(format_spec, default): fmt = _build_format_func(format_spec, default) if not offset or offset == "local": - is_dst = time.daylight and time.localtime().tm_isdst > 0 - offset = -(time.altzone if is_dst else time.timezone) + def off(dt): + local = time.localtime(util.datetime_to_timestamp(dt)) + return fmt(dt + datetime.timedelta(0, local.tm_gmtoff)) else: hours, _, minutes = offset.partition(":") offset = 3600 * int(hours) if minutes: offset += 60 * (int(minutes) if offset > 0 else -int(minutes)) + offset = datetime.timedelta(0, offset) - offset = datetime.timedelta(seconds=offset) - - def off(obj): - return fmt(obj + offset) + def off(obj): + return fmt(obj + offset) return off diff --git a/test/test_formatter.py b/test/test_formatter.py index 5ed94ec4..26adbdee 100644 --- a/test/test_formatter.py +++ b/test/test_formatter.py @@ -31,8 +31,9 @@ class TestFormatter(unittest.TestCase): "h": "

foo

& bar

", "u": "'< / >'", "t": 1262304000, - "dt": datetime.datetime(2010, 1, 1), "ds": "2010-01-01T01:00:00+0100", + "dt": datetime.datetime(2010, 1, 1), + "dt_dst": datetime.datetime(2010, 6, 1), "name": "Name", "title1": "Title", "title2": "", @@ -236,19 +237,18 @@ class TestFormatter(unittest.TestCase): self._run_test("{ds:D%Y-%m-%dT%H:%M:%S%z/O1}", "2010-01-01 01:00:00") self._run_test("{t!d:O2}", "2010-01-01 02:00:00") - orig_daylight = time.daylight - orig_timezone = time.timezone - orig_altzone = time.altzone - try: - time.daylight = False - time.timezone = -3600 - self._run_test("{dt:O}", "2010-01-01 01:00:00") - time.timezone = 7200 - self._run_test("{dt:Olocal}", "2009-12-31 22:00:00") - finally: - time.daylight = orig_daylight - time.timezone = orig_timezone - time.altzone = orig_altzone + def test_offset_local(self): + ts = self.kwdict["dt"].replace(tzinfo=datetime.UTC).timestamp() + offset = time.localtime(ts).tm_gmtoff + dt = self.kwdict["dt"] + datetime.timedelta(seconds=offset) + self._run_test("{dt:O}", str(dt)) + self._run_test("{dt:Olocal}", str(dt)) + + ts = self.kwdict["dt_dst"].replace(tzinfo=datetime.UTC).timestamp() + offset = time.localtime(ts).tm_gmtoff + dt = self.kwdict["dt_dst"] + datetime.timedelta(seconds=offset) + self._run_test("{dt_dst:O}", str(dt)) + self._run_test("{dt_dst:Olocal}", str(dt)) def test_sort(self): self._run_test("{l:S}" , "['a', 'b', 'c']")