From 17b291093847fc8efe2a62f427301d16e9fc3461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20F=C3=A4hrmann?= Date: Wed, 28 May 2025 20:10:18 +0200 Subject: [PATCH] [util] implement 'to_datetime()' --- gallery_dl/util.py | 28 +++++++++++++++++++++++++ test/test_util.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/gallery_dl/util.py b/gallery_dl/util.py index d760a8ea..4f24a66c 100644 --- a/gallery_dl/util.py +++ b/gallery_dl/util.py @@ -236,6 +236,34 @@ def to_string(value): return str(value) +def to_datetime(value): + """Convert 'value' to a datetime object""" + if not value: + return EPOCH + + if isinstance(value, datetime.datetime): + return value + + if isinstance(value, str): + try: + if value[-1] == "Z": + # compat for Python < 3.11 + value = value[:-1] + dt = datetime.datetime.fromisoformat(value) + if dt.tzinfo is None: + if dt.microsecond: + dt = dt.replace(microsecond=0) + else: + # convert to naive UTC + dt = dt.astimezone(datetime.timezone.utc).replace( + microsecond=0, tzinfo=None) + return dt + except Exception: + pass + + return text.parse_timestamp(value, EPOCH) + + def datetime_to_timestamp(dt): """Convert naive UTC datetime to Unix timestamp""" return (dt - EPOCH) / SECOND diff --git a/test/test_util.py b/test/test_util.py index 27f78ece..126c2b97 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -765,6 +765,58 @@ value = 123 self.assertEqual(f(["a", "b", "c"]), "a, b, c") self.assertEqual(f([1, 2, 3]), "1, 2, 3") + @unittest.skipIf(sys.hexversion < 0x3070000, + "datetime.fromisoformat") + def test_to_datetime(self, f=util.to_datetime): + + def _assert(value, expected): + result = f(value) + self.assertIsInstance(result, datetime.datetime) + self.assertEqual(result, expected, msg=repr(value)) + + dt = datetime.datetime(2010, 1, 1) + self.assertIs(f(dt), dt) + + _assert(dt , dt) + _assert(1262304000 , dt) + _assert(1262304000.0 , dt) + _assert(1262304000.123, dt) + _assert("1262304000" , dt) + + _assert("2010-01-01" , dt) + _assert("2010-01-01 00:00:00" , dt) + _assert("2010-01-01T00:00:00" , dt) + _assert("2010-01-01T00:00:00.123456" , dt) + _assert("2009-12-31T19:00:00-05:00" , dt) + _assert("2009-12-31T19:00:00.123456-05:00", dt) + _assert("2010-01-01T00:00:00Z" , dt) + _assert("2010-01-01T00:00:00.123456Z" , dt) + + _assert(0 , util.EPOCH) + _assert("" , util.EPOCH) + _assert("foo", util.EPOCH) + _assert(None , util.EPOCH) + _assert(() , util.EPOCH) + _assert([] , util.EPOCH) + _assert({} , util.EPOCH) + _assert((1, 2, 3), util.EPOCH) + + @unittest.skipIf(sys.hexversion < 0x30b0000, + "extended fromisoformat timezones") + def test_to_datetime_tz(self, f=util.to_datetime): + + def _assert(value, expected): + result = f(value) + self.assertIsInstance(result, datetime.datetime) + self.assertEqual(result, expected, msg=repr(value)) + + dt = datetime.datetime(2010, 1, 1) + + _assert("2009-12-31T19:00:00-05" , dt) + _assert("2009-12-31T19:00:00-0500" , dt) + _assert("2009-12-31T19:00:00.123456-05" , dt) + _assert("2009-12-31T19:00:00.123456-0500" , dt) + def test_datetime_to_timestamp(self, f=util.datetime_to_timestamp): self.assertEqual(f(util.EPOCH), 0.0) self.assertEqual(f(datetime.datetime(2010, 1, 1)), 1262304000.0)