From 879d4b300fbe84d5047416767228d76dc581e68f Mon Sep 17 00:00:00 2001 From: jensens Date: Fri, 13 Jan 2023 12:10:01 +0100 Subject: [PATCH] [fc] Repository: plone.event Branch: refs/heads/master Date: 2023-01-13T10:30:30+01:00 Author: Gogo Bernhard (gogobd) Commit: https://github.com/plone/plone.event/commit/5ffb4469175d5fa404adf1e716abefad119b8ae3 Fixing #13 Files changed: A news/13.bugfix M plone/event/utils.py Repository: plone.event Branch: refs/heads/master Date: 2023-01-13T12:10:01+01:00 Author: Jens W. Klein (jensens) Commit: https://github.com/plone/plone.event/commit/a6a0923a657d376df74d15ad8099e961c3bb18b6 Merge pull request #15 from plone/issue-#13 Fixing #13 Files changed: A news/13.bugfix M plone/event/utils.py --- last_commit.txt | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/last_commit.txt b/last_commit.txt index c56736f95a..f0d53bdfb2 100644 --- a/last_commit.txt +++ b/last_commit.txt @@ -1,15 +1,34 @@ -Repository: plone.volto +Repository: plone.event -Branch: refs/heads/main -Date: 2023-01-12T09:03:00+01:00 -Author: Timo Stollenwerk (tisto) -Commit: https://github.com/plone/plone.volto/commit/7e16d711ec92185d1315826e113b36a9995511b5 +Branch: refs/heads/master +Date: 2023-01-13T10:30:30+01:00 +Author: Gogo Bernhard (gogobd) +Commit: https://github.com/plone/plone.event/commit/5ffb4469175d5fa404adf1e716abefad119b8ae3 -Update architectural diagram "Plone" -> "Plone core" +Fixing #13 Files changed: -M README.rst +A news/13.bugfix +M plone/event/utils.py -b'diff --git a/README.rst b/README.rst\nindex 5930344..06ab223 100644\n--- a/README.rst\n+++ b/README.rst\n@@ -77,7 +77,7 @@ Architectural Diagram of Plone 6::\n \xe2\x94\x82\xe2\x94\x82 plone.volto \xe2\x94\x82\xe2\x94\x82\n \xe2\x94\x82\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x98\xe2\x94\x82\n \xe2\x94\x82\xe2\x94\x8c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x90 \xe2\x94\x8c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x90\xe2\x94\x82\n- \xe2\x94\x82\xe2\x94\x82 Plone \xe2\x94\x82 \xe2\x94\x82 Add-Ons \xe2\x94\x82\xe2\x94\x82\n+ \xe2\x94\x82\xe2\x94\x82 Plone Core \xe2\x94\x82 \xe2\x94\x82 Add-Ons \xe2\x94\x82\xe2\x94\x82\n \xe2\x94\x82\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x98 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x98\xe2\x94\x82\n \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x98\n \n' +b'diff --git a/news/13.bugfix b/news/13.bugfix\nnew file mode 100644\nindex 0000000..5bbd512\n--- /dev/null\n+++ b/news/13.bugfix\n@@ -0,0 +1 @@\n+Fixing issue #13\n\\ No newline at end of file\ndiff --git a/plone/event/utils.py b/plone/event/utils.py\nindex e7681f8..0763205 100644\n--- a/plone/event/utils.py\n+++ b/plone/event/utils.py\n@@ -9,16 +9,16 @@\n import time\n \n \n-DSTADJUST = \'adjust\'\n-DSTKEEP = \'keep\'\n-DSTAUTO = \'auto\'\n+DSTADJUST = "adjust"\n+DSTKEEP = "keep"\n+DSTAUTO = "auto"\n MAX32 = int(2**31 - 1)\n \n-logger = logging.getLogger(\'plone.event\')\n+logger = logging.getLogger("plone.event")\n \n \n def validated_timezone(timezone, fallback=None):\n- """ Validate a given timezone identifier. If a fallback is given, return it\n+ """Validate a given timezone identifier. If a fallback is given, return it\n when the given timezone is not a valid pytz zone. Else raise an\n ValueError exception.\n \n@@ -59,8 +59,8 @@ def validated_timezone(timezone, fallback=None):\n except Exception:\n if fallback:\n logger.warn(\n- \'The timezone {0} is not a valid timezone from the \'\n- \'Olson database or pytz. Falling back to {1}.\'.format(\n+ "The timezone {0} is not a valid timezone from the "\n+ "Olson database or pytz. Falling back to {1}.".format(\n timezone,\n fallback,\n )\n@@ -68,61 +68,61 @@ def validated_timezone(timezone, fallback=None):\n return fallback\n else:\n raise ValueError(\n- \'The timezone {0} is not a valid timezone from \'\n- \'the Olson database or pytz.\'.format(timezone)\n+ "The timezone {0} is not a valid timezone from "\n+ "the Olson database or pytz.".format(timezone)\n )\n \n \n-def default_timezone(fallback=\'UTC\'):\n- """ Retrieve the timezone from the server.\n- Default Fallback: UTC\n+def default_timezone(fallback="UTC"):\n+ """Retrieve the timezone from the server.\n+ Default Fallback: UTC\n \n- :param fallback: A fallback timezone identifier.\n- :type fallback: string\n+ :param fallback: A fallback timezone identifier.\n+ :type fallback: string\n \n- :returns: A timezone identifier.\n- :rtype: string\n+ :returns: A timezone identifier.\n+ :rtype: string\n \n- >>> from plone.event.utils import default_timezone\n- >>> import os\n- >>> import time\n- >>> timetz = time.tzname\n- >>> ostz = \'TZ\' in os.environ.keys() and os.environ[\'TZ\'] or None\n+ >>> from plone.event.utils import default_timezone\n+ >>> import os\n+ >>> import time\n+ >>> timetz = time.tzname\n+ >>> ostz = \'TZ\' in os.environ.keys() and os.environ[\'TZ\'] or None\n \n- >>> os.environ[\'TZ\'] = "Europe/Vienna"\n- >>> default_timezone()\n- \'Europe/Vienna\'\n+ >>> os.environ[\'TZ\'] = "Europe/Vienna"\n+ >>> default_timezone()\n+ \'Europe/Vienna\'\n \n- Timezone from time module\n- >>> os.environ[\'TZ\'] = ""\n- >>> time.tzname = (\'CET\', \'CEST\')\n- >>> default_timezone()\n- \'CET\'\n+ Timezone from time module\n+ >>> os.environ[\'TZ\'] = ""\n+ >>> time.tzname = (\'CET\', \'CEST\')\n+ >>> default_timezone()\n+ \'CET\'\n \n- Invalid timezone\n- >>> os.environ[\'TZ\'] = "PST"\n- >>> default_timezone()\n- \'UTC\'\n+ Invalid timezone\n+ >>> os.environ[\'TZ\'] = "PST"\n+ >>> default_timezone()\n+ \'UTC\'\n \n- Invalid timezone with defined fallback\n- >>> os.environ[\'TZ\'] = ""\n- >>> time.tzname = None\n- >>> default_timezone(fallback=\'CET\')\n- \'CET\'\n+ Invalid timezone with defined fallback\n+ >>> os.environ[\'TZ\'] = ""\n+ >>> time.tzname = None\n+ >>> default_timezone(fallback=\'CET\')\n+ \'CET\'\n \n- Restore the system timezone\n- >>> time.tzname = timetz\n- >>> if ostz:\n- ... os.environ[\'TZ\'] = ostz\n- ... else:\n- ... del os.environ[\'TZ\']\n+ Restore the system timezone\n+ >>> time.tzname = timetz\n+ >>> if ostz:\n+ ... os.environ[\'TZ\'] = ostz\n+ ... else:\n+ ... del os.environ[\'TZ\']\n \n """\n \n timezone = None\n- if \'TZ\' in os.environ.keys():\n+ if "TZ" in os.environ.keys():\n # Timezone from OS env var\n- timezone = os.environ[\'TZ\']\n+ timezone = os.environ["TZ"]\n if not timezone:\n # Timezone from python time\n zones = time.tzname\n@@ -131,15 +131,14 @@ def default_timezone(fallback=\'UTC\'):\n else:\n # Default fallback = UTC\n logger.warn(\n- \'Operating system\\\'s timezone cannot be found. \'\n- \'Falling back to UTC.\'\n+ "Operating system\'s timezone cannot be found. " "Falling back to UTC."\n )\n return validated_timezone(timezone, fallback)\n \n \n # Display helpers\n def is_same_time(start, end, exact=False):\n- """ Test if event starts and ends at same time.\n+ """Test if event starts and ends at same time.\n \n :param start: The start datetime.\n :type start: Python datetime or Zope DateTime\n@@ -177,13 +176,15 @@ def is_same_time(start, end, exact=False):\n if exact:\n return start == end\n else:\n- return start.hour == end.hour and\\\n- start.minute == end.minute and\\\n- start.second == end.second\n+ return (\n+ start.hour == end.hour\n+ and start.minute == end.minute\n+ and start.second == end.second\n+ )\n \n \n def is_same_day(start, end):\n- """ Test if event starts and ends at same day.\n+ """Test if event starts and ends at same day.\n \n >>> from plone.event.utils import is_same_day, utc\n >>> from datetime import datetime, timedelta\n@@ -218,18 +219,18 @@ def is_same_day(start, end):\n \n # Timezone helpers\n def utctz():\n- """ Return the UTVC zone as a pytz.UTC instance.\n+ """Return the UTVC zone as a pytz.UTC instance.\n \n >>> from plone.event.utils import utctz\n >>> utctz()\n \n \n """\n- return pytz.timezone(\'UTC\')\n+ return pytz.timezone("UTC")\n \n \n def utc(dt):\n- """ Convert Python datetime to UTC.\n+ """Convert Python datetime to UTC.\n \n >>> from datetime import datetime\n >>> from plone.event.utils import utc\n@@ -250,6 +251,8 @@ def utc(dt):\n if dt is None:\n return None\n dt = pydt(dt)\n+ if dt is None:\n+ return None\n return dt.astimezone(utctz())\n \n \n@@ -275,12 +278,12 @@ def utcoffset_normalize(date, delta=None, dstmode=DSTAUTO):\n used - otherwise DSTADJUST. This behavior is the default.\n """\n try:\n- assert (bool(date.tzinfo))\n+ assert bool(date.tzinfo)\n except Exception:\n- raise TypeError(\'Cannot normalize timezone naive dates\')\n- assert (dstmode in [DSTADJUST, DSTKEEP, DSTAUTO])\n+ raise TypeError("Cannot normalize timezone naive dates")\n+ assert dstmode in [DSTADJUST, DSTKEEP, DSTAUTO]\n if delta:\n- assert (isinstance(delta, timedelta)) # Easier in Java\n+ assert isinstance(delta, timedelta) # Easier in Java\n delta = delta.seconds + delta.days * 24 * 3600 # total delta in secs\n if dstmode == DSTAUTO and delta < 24 * 60 * 60:\n dstmode = DSTKEEP\n@@ -301,7 +304,7 @@ def utcoffset_normalize(date, delta=None, dstmode=DSTAUTO):\n \n \n def tzdel(dt):\n- """ Create timezone naive datetime from a timezone aware one by removing\n+ """Create timezone naive datetime from a timezone aware one by removing\n the timezone component.\n \n >>> from plone.event.utils import tzdel, utctz\n@@ -392,7 +395,7 @@ def date_to_datetime(value):\n elif is_datetime(value):\n return value\n else:\n- raise ValueError(\'Value must be a date or datetime object.\')\n+ raise ValueError("Value must be a date or datetime object.")\n \n \n def pydt(dt, missing_zone=None, exact=False):\n@@ -454,13 +457,13 @@ def pydt(dt, missing_zone=None, exact=False):\n dt = datetime(dt.year, dt.month, dt.day)\n \n if isinstance(dt, datetime):\n- tznaive = not bool(getattr(dt, \'tzinfo\', False))\n+ tznaive = not bool(getattr(dt, "tzinfo", False))\n if tznaive:\n ret = missing_zone.localize(dt)\n else:\n ret = utcoffset_normalize(dt, dstmode=DSTADJUST)\n \n- if \'DateTime\' in str(dt.__class__):\n+ if "DateTime" in str(dt.__class__):\n # Zope DateTime\n # TODO: do we need to support subclasses of DateTime too? the check\n # above would fail.\n@@ -536,7 +539,7 @@ def guesstz(DT):\n \n # Date as integer representation helpers\n def dt2int(dt):\n- """ Calculates an integer from a datetime, resolution is one minute.\n+ """Calculates an integer from a datetime, resolution is one minute.\n The datetime is always converted to the UTC zone.\n \n >>> from plone.event import utils\n@@ -549,21 +552,22 @@ def dt2int(dt):\n return 0\n # TODO: if dt has not timezone information, guess and set it\n dt = utc(dt)\n- value = (((dt.year * 12 + dt.month) * 31 + dt.day) * 24 + dt.hour\n- ) * 60 + dt.minute\n+ value = (((dt.year * 12 + dt.month) * 31 + dt.day) * 24 + dt.hour) * 60 + dt.minute\n \n # TODO: unit test me\n if value > MAX32:\n # value must be integer fitting in the 32bit range\n raise OverflowError(\n """{0} is not within the range of indexable dates,<<\n- exceeding 32bit range.""".format(dt)\n+ exceeding 32bit range.""".format(\n+ dt\n+ )\n )\n return value\n \n \n def int2dt(dtint):\n- """ Returns a datetime object from an integer representation with\n+ """Returns a datetime object from an integer representation with\n resolution of one minute. The datetime returned is in the UTC zone.\n \n >>> from plone.event.utils import int2dt\n@@ -578,7 +582,7 @@ def int2dt(dtint):\n \n """\n if not isinstance(dtint, int):\n- raise ValueError(\'int2dt expects integer values as arguments.\')\n+ raise ValueError("int2dt expects integer values as arguments.")\n minutes = dtint % 60\n hours = dtint // 60 % 24\n days = dtint // 60 // 24 % 31\n@@ -588,7 +592,7 @@ def int2dt(dtint):\n \n \n def dt_to_zone(dt, tzstring):\n- """ Return a datetime instance converted to the timezone given by the\n+ """Return a datetime instance converted to the timezone given by the\n string.\n \n """\n@@ -596,8 +600,8 @@ def dt_to_zone(dt, tzstring):\n \n \n # RFC2445 export helpers\n-def rfc2445dt(dt, mode=\'utc\', date=True, time=True):\n- """ Convert a datetime or DateTime object into an RFC2445 compatible\n+def rfc2445dt(dt, mode="utc", date=True, time=True):\n+ """Convert a datetime or DateTime object into an RFC2445 compatible\n datetime string.\n \n @param dt: datetime or DateTime object to convert.\n@@ -657,14 +661,14 @@ def rfc2445dt(dt, mode=\'utc\', date=True, time=True):\n # TODO: rfc2445dt might not be necessary. drop me then.\n \n dt = pydt(dt)\n- if mode == \'utc\':\n+ if mode == "utc":\n dt = utc(dt)\n- date = \'{0}{1}{2}{3}\'.format(\n- date and dt.strftime(\'%Y%m%d\') or \'\',\n- date and time and \'T\' or \'\',\n- time and dt.strftime(\'%H%M%S\') or \'\',\n- mode == \'utc\' and \'Z\' or \'\',\n+ date = "{0}{1}{2}{3}".format(\n+ date and dt.strftime("%Y%m%d") or "",\n+ date and time and "T" or "",\n+ time and dt.strftime("%H%M%S") or "",\n+ mode == "utc" and "Z" or "",\n )\n- if mode == \'local\':\n+ if mode == "local":\n return date, dt.tzinfo.zone\n return date\n' + +Repository: plone.event + + +Branch: refs/heads/master +Date: 2023-01-13T12:10:01+01:00 +Author: Jens W. Klein (jensens) +Commit: https://github.com/plone/plone.event/commit/a6a0923a657d376df74d15ad8099e961c3bb18b6 + +Merge pull request #15 from plone/issue-#13 + +Fixing #13 + +Files changed: +A news/13.bugfix +M plone/event/utils.py + +b'diff --git a/news/13.bugfix b/news/13.bugfix\nnew file mode 100644\nindex 0000000..5bbd512\n--- /dev/null\n+++ b/news/13.bugfix\n@@ -0,0 +1 @@\n+Fixing issue #13\n\\ No newline at end of file\ndiff --git a/plone/event/utils.py b/plone/event/utils.py\nindex e7681f8..0763205 100644\n--- a/plone/event/utils.py\n+++ b/plone/event/utils.py\n@@ -9,16 +9,16 @@\n import time\n \n \n-DSTADJUST = \'adjust\'\n-DSTKEEP = \'keep\'\n-DSTAUTO = \'auto\'\n+DSTADJUST = "adjust"\n+DSTKEEP = "keep"\n+DSTAUTO = "auto"\n MAX32 = int(2**31 - 1)\n \n-logger = logging.getLogger(\'plone.event\')\n+logger = logging.getLogger("plone.event")\n \n \n def validated_timezone(timezone, fallback=None):\n- """ Validate a given timezone identifier. If a fallback is given, return it\n+ """Validate a given timezone identifier. If a fallback is given, return it\n when the given timezone is not a valid pytz zone. Else raise an\n ValueError exception.\n \n@@ -59,8 +59,8 @@ def validated_timezone(timezone, fallback=None):\n except Exception:\n if fallback:\n logger.warn(\n- \'The timezone {0} is not a valid timezone from the \'\n- \'Olson database or pytz. Falling back to {1}.\'.format(\n+ "The timezone {0} is not a valid timezone from the "\n+ "Olson database or pytz. Falling back to {1}.".format(\n timezone,\n fallback,\n )\n@@ -68,61 +68,61 @@ def validated_timezone(timezone, fallback=None):\n return fallback\n else:\n raise ValueError(\n- \'The timezone {0} is not a valid timezone from \'\n- \'the Olson database or pytz.\'.format(timezone)\n+ "The timezone {0} is not a valid timezone from "\n+ "the Olson database or pytz.".format(timezone)\n )\n \n \n-def default_timezone(fallback=\'UTC\'):\n- """ Retrieve the timezone from the server.\n- Default Fallback: UTC\n+def default_timezone(fallback="UTC"):\n+ """Retrieve the timezone from the server.\n+ Default Fallback: UTC\n \n- :param fallback: A fallback timezone identifier.\n- :type fallback: string\n+ :param fallback: A fallback timezone identifier.\n+ :type fallback: string\n \n- :returns: A timezone identifier.\n- :rtype: string\n+ :returns: A timezone identifier.\n+ :rtype: string\n \n- >>> from plone.event.utils import default_timezone\n- >>> import os\n- >>> import time\n- >>> timetz = time.tzname\n- >>> ostz = \'TZ\' in os.environ.keys() and os.environ[\'TZ\'] or None\n+ >>> from plone.event.utils import default_timezone\n+ >>> import os\n+ >>> import time\n+ >>> timetz = time.tzname\n+ >>> ostz = \'TZ\' in os.environ.keys() and os.environ[\'TZ\'] or None\n \n- >>> os.environ[\'TZ\'] = "Europe/Vienna"\n- >>> default_timezone()\n- \'Europe/Vienna\'\n+ >>> os.environ[\'TZ\'] = "Europe/Vienna"\n+ >>> default_timezone()\n+ \'Europe/Vienna\'\n \n- Timezone from time module\n- >>> os.environ[\'TZ\'] = ""\n- >>> time.tzname = (\'CET\', \'CEST\')\n- >>> default_timezone()\n- \'CET\'\n+ Timezone from time module\n+ >>> os.environ[\'TZ\'] = ""\n+ >>> time.tzname = (\'CET\', \'CEST\')\n+ >>> default_timezone()\n+ \'CET\'\n \n- Invalid timezone\n- >>> os.environ[\'TZ\'] = "PST"\n- >>> default_timezone()\n- \'UTC\'\n+ Invalid timezone\n+ >>> os.environ[\'TZ\'] = "PST"\n+ >>> default_timezone()\n+ \'UTC\'\n \n- Invalid timezone with defined fallback\n- >>> os.environ[\'TZ\'] = ""\n- >>> time.tzname = None\n- >>> default_timezone(fallback=\'CET\')\n- \'CET\'\n+ Invalid timezone with defined fallback\n+ >>> os.environ[\'TZ\'] = ""\n+ >>> time.tzname = None\n+ >>> default_timezone(fallback=\'CET\')\n+ \'CET\'\n \n- Restore the system timezone\n- >>> time.tzname = timetz\n- >>> if ostz:\n- ... os.environ[\'TZ\'] = ostz\n- ... else:\n- ... del os.environ[\'TZ\']\n+ Restore the system timezone\n+ >>> time.tzname = timetz\n+ >>> if ostz:\n+ ... os.environ[\'TZ\'] = ostz\n+ ... else:\n+ ... del os.environ[\'TZ\']\n \n """\n \n timezone = None\n- if \'TZ\' in os.environ.keys():\n+ if "TZ" in os.environ.keys():\n # Timezone from OS env var\n- timezone = os.environ[\'TZ\']\n+ timezone = os.environ["TZ"]\n if not timezone:\n # Timezone from python time\n zones = time.tzname\n@@ -131,15 +131,14 @@ def default_timezone(fallback=\'UTC\'):\n else:\n # Default fallback = UTC\n logger.warn(\n- \'Operating system\\\'s timezone cannot be found. \'\n- \'Falling back to UTC.\'\n+ "Operating system\'s timezone cannot be found. " "Falling back to UTC."\n )\n return validated_timezone(timezone, fallback)\n \n \n # Display helpers\n def is_same_time(start, end, exact=False):\n- """ Test if event starts and ends at same time.\n+ """Test if event starts and ends at same time.\n \n :param start: The start datetime.\n :type start: Python datetime or Zope DateTime\n@@ -177,13 +176,15 @@ def is_same_time(start, end, exact=False):\n if exact:\n return start == end\n else:\n- return start.hour == end.hour and\\\n- start.minute == end.minute and\\\n- start.second == end.second\n+ return (\n+ start.hour == end.hour\n+ and start.minute == end.minute\n+ and start.second == end.second\n+ )\n \n \n def is_same_day(start, end):\n- """ Test if event starts and ends at same day.\n+ """Test if event starts and ends at same day.\n \n >>> from plone.event.utils import is_same_day, utc\n >>> from datetime import datetime, timedelta\n@@ -218,18 +219,18 @@ def is_same_day(start, end):\n \n # Timezone helpers\n def utctz():\n- """ Return the UTVC zone as a pytz.UTC instance.\n+ """Return the UTVC zone as a pytz.UTC instance.\n \n >>> from plone.event.utils import utctz\n >>> utctz()\n \n \n """\n- return pytz.timezone(\'UTC\')\n+ return pytz.timezone("UTC")\n \n \n def utc(dt):\n- """ Convert Python datetime to UTC.\n+ """Convert Python datetime to UTC.\n \n >>> from datetime import datetime\n >>> from plone.event.utils import utc\n@@ -250,6 +251,8 @@ def utc(dt):\n if dt is None:\n return None\n dt = pydt(dt)\n+ if dt is None:\n+ return None\n return dt.astimezone(utctz())\n \n \n@@ -275,12 +278,12 @@ def utcoffset_normalize(date, delta=None, dstmode=DSTAUTO):\n used - otherwise DSTADJUST. This behavior is the default.\n """\n try:\n- assert (bool(date.tzinfo))\n+ assert bool(date.tzinfo)\n except Exception:\n- raise TypeError(\'Cannot normalize timezone naive dates\')\n- assert (dstmode in [DSTADJUST, DSTKEEP, DSTAUTO])\n+ raise TypeError("Cannot normalize timezone naive dates")\n+ assert dstmode in [DSTADJUST, DSTKEEP, DSTAUTO]\n if delta:\n- assert (isinstance(delta, timedelta)) # Easier in Java\n+ assert isinstance(delta, timedelta) # Easier in Java\n delta = delta.seconds + delta.days * 24 * 3600 # total delta in secs\n if dstmode == DSTAUTO and delta < 24 * 60 * 60:\n dstmode = DSTKEEP\n@@ -301,7 +304,7 @@ def utcoffset_normalize(date, delta=None, dstmode=DSTAUTO):\n \n \n def tzdel(dt):\n- """ Create timezone naive datetime from a timezone aware one by removing\n+ """Create timezone naive datetime from a timezone aware one by removing\n the timezone component.\n \n >>> from plone.event.utils import tzdel, utctz\n@@ -392,7 +395,7 @@ def date_to_datetime(value):\n elif is_datetime(value):\n return value\n else:\n- raise ValueError(\'Value must be a date or datetime object.\')\n+ raise ValueError("Value must be a date or datetime object.")\n \n \n def pydt(dt, missing_zone=None, exact=False):\n@@ -454,13 +457,13 @@ def pydt(dt, missing_zone=None, exact=False):\n dt = datetime(dt.year, dt.month, dt.day)\n \n if isinstance(dt, datetime):\n- tznaive = not bool(getattr(dt, \'tzinfo\', False))\n+ tznaive = not bool(getattr(dt, "tzinfo", False))\n if tznaive:\n ret = missing_zone.localize(dt)\n else:\n ret = utcoffset_normalize(dt, dstmode=DSTADJUST)\n \n- if \'DateTime\' in str(dt.__class__):\n+ if "DateTime" in str(dt.__class__):\n # Zope DateTime\n # TODO: do we need to support subclasses of DateTime too? the check\n # above would fail.\n@@ -536,7 +539,7 @@ def guesstz(DT):\n \n # Date as integer representation helpers\n def dt2int(dt):\n- """ Calculates an integer from a datetime, resolution is one minute.\n+ """Calculates an integer from a datetime, resolution is one minute.\n The datetime is always converted to the UTC zone.\n \n >>> from plone.event import utils\n@@ -549,21 +552,22 @@ def dt2int(dt):\n return 0\n # TODO: if dt has not timezone information, guess and set it\n dt = utc(dt)\n- value = (((dt.year * 12 + dt.month) * 31 + dt.day) * 24 + dt.hour\n- ) * 60 + dt.minute\n+ value = (((dt.year * 12 + dt.month) * 31 + dt.day) * 24 + dt.hour) * 60 + dt.minute\n \n # TODO: unit test me\n if value > MAX32:\n # value must be integer fitting in the 32bit range\n raise OverflowError(\n """{0} is not within the range of indexable dates,<<\n- exceeding 32bit range.""".format(dt)\n+ exceeding 32bit range.""".format(\n+ dt\n+ )\n )\n return value\n \n \n def int2dt(dtint):\n- """ Returns a datetime object from an integer representation with\n+ """Returns a datetime object from an integer representation with\n resolution of one minute. The datetime returned is in the UTC zone.\n \n >>> from plone.event.utils import int2dt\n@@ -578,7 +582,7 @@ def int2dt(dtint):\n \n """\n if not isinstance(dtint, int):\n- raise ValueError(\'int2dt expects integer values as arguments.\')\n+ raise ValueError("int2dt expects integer values as arguments.")\n minutes = dtint % 60\n hours = dtint // 60 % 24\n days = dtint // 60 // 24 % 31\n@@ -588,7 +592,7 @@ def int2dt(dtint):\n \n \n def dt_to_zone(dt, tzstring):\n- """ Return a datetime instance converted to the timezone given by the\n+ """Return a datetime instance converted to the timezone given by the\n string.\n \n """\n@@ -596,8 +600,8 @@ def dt_to_zone(dt, tzstring):\n \n \n # RFC2445 export helpers\n-def rfc2445dt(dt, mode=\'utc\', date=True, time=True):\n- """ Convert a datetime or DateTime object into an RFC2445 compatible\n+def rfc2445dt(dt, mode="utc", date=True, time=True):\n+ """Convert a datetime or DateTime object into an RFC2445 compatible\n datetime string.\n \n @param dt: datetime or DateTime object to convert.\n@@ -657,14 +661,14 @@ def rfc2445dt(dt, mode=\'utc\', date=True, time=True):\n # TODO: rfc2445dt might not be necessary. drop me then.\n \n dt = pydt(dt)\n- if mode == \'utc\':\n+ if mode == "utc":\n dt = utc(dt)\n- date = \'{0}{1}{2}{3}\'.format(\n- date and dt.strftime(\'%Y%m%d\') or \'\',\n- date and time and \'T\' or \'\',\n- time and dt.strftime(\'%H%M%S\') or \'\',\n- mode == \'utc\' and \'Z\' or \'\',\n+ date = "{0}{1}{2}{3}".format(\n+ date and dt.strftime("%Y%m%d") or "",\n+ date and time and "T" or "",\n+ time and dt.strftime("%H%M%S") or "",\n+ mode == "utc" and "Z" or "",\n )\n- if mode == \'local\':\n+ if mode == "local":\n return date, dt.tzinfo.zone\n return date\n'