From 16123e268bafd93d362c72fe9fb702103a26c942 Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Tue, 18 Oct 2022 15:06:24 +0100 Subject: [PATCH 01/24] Set timezone for creation and modification dates of comments --- plone/app/discussion/browser/comments.py | 5 ++-- plone/app/discussion/comment.py | 3 ++- .../discussion/profiles/default/metadata.xml | 2 +- plone/app/discussion/upgrades.py | 23 +++++++++++++++++++ plone/app/discussion/upgrades.zcml | 11 +++++++++ 5 files changed, 40 insertions(+), 4 deletions(-) diff --git a/plone/app/discussion/browser/comments.py b/plone/app/discussion/browser/comments.py index 387cead..2056aa2 100644 --- a/plone/app/discussion/browser/comments.py +++ b/plone/app/discussion/browser/comments.py @@ -2,6 +2,7 @@ from AccessControl import getSecurityManager from AccessControl import Unauthorized from Acquisition import aq_inner from datetime import datetime +from datetime import timezone from DateTime import DateTime from plone.app.discussion import _ from plone.app.discussion.browser.validator import CaptchaValidator @@ -192,8 +193,8 @@ class CommentForm(extensible.ExtensibleForm, form.Form): setattr(comment, attribute, data[attribute]) # Set dates - comment.creation_date = datetime.utcnow() - comment.modification_date = datetime.utcnow() + comment.creation_date = datetime.now(timezone.utc) + comment.modification_date = datetime.now(timezone.utc) # Get author name and email comment.author_name, comment.author_email = self.get_author(data) diff --git a/plone/app/discussion/comment.py b/plone/app/discussion/comment.py index 438e726..8e9be09 100644 --- a/plone/app/discussion/comment.py +++ b/plone/app/discussion/comment.py @@ -6,6 +6,7 @@ from Acquisition import aq_base from Acquisition import aq_parent from Acquisition import Implicit from datetime import datetime +from datetime import timezone from OFS.owner import Owned from OFS.role import RoleManager from OFS.Traversable import Traversable @@ -119,7 +120,7 @@ class Comment( # IConversation.addComment(). def __init__(self): - self.creation_date = self.modification_date = datetime.utcnow() + self.creation_date = self.modification_date = datetime.now(timezone.utc) self.mime_type = "text/plain" user = getSecurityManager().getUser() diff --git a/plone/app/discussion/profiles/default/metadata.xml b/plone/app/discussion/profiles/default/metadata.xml index 141a440..3b4e730 100644 --- a/plone/app/discussion/profiles/default/metadata.xml +++ b/plone/app/discussion/profiles/default/metadata.xml @@ -1,5 +1,5 @@ - 2000 + 2001 profile-plone.resource:default profile-plone.app.registry:default diff --git a/plone/app/discussion/upgrades.py b/plone/app/discussion/upgrades.py index 40cdc8f..fbad45c 100644 --- a/plone/app/discussion/upgrades.py +++ b/plone/app/discussion/upgrades.py @@ -2,6 +2,8 @@ from plone.app.discussion.interfaces import IDiscussionSettings from plone.registry.interfaces import IRegistry from Products.CMFCore.utils import getToolByName from zope.component import getUtility +from plone import api +from datetime import timezone import logging @@ -77,3 +79,24 @@ def add_js_to_plone_legacy(context): def extend_review_workflow(context): """Apply changes made to review workflow.""" upgrade_comment_workflows_retain_current_workflow(context) + + +def set_timezone_on_dates(context): + """Ensure timezone data is stored against all creation/modified dates""" + pc = api.portal.get_tool('portal_catalog') + creations = 0 + modifieds = 0 + logger.info('Setting timezone information on comment dates') + comments = pc.search({'Type': 'Comment'}) + for cbrain in comments: + comment = cbrain.getObject() + if not comment.creation_date.tzinfo: + creations += 1 + comment.creation_date = \ + comment.creation_date.astimezone(timezone.utc) + if not comment.modification_date.tzinfo: + modifieds += 1 + comment.modification_date = \ + comment.modification_date.astimezone(timezone.utc) + logger.info('Updated %i creation dates and %i modification dates' % + (creations, modifieds)) diff --git a/plone/app/discussion/upgrades.zcml b/plone/app/discussion/upgrades.zcml index 20b288e..7f8ffe7 100644 --- a/plone/app/discussion/upgrades.zcml +++ b/plone/app/discussion/upgrades.zcml @@ -88,4 +88,15 @@ handler=".upgrades.upgrade_comment_workflows" /> + + + + From 42e52386228813594a1f3069e0278c1b90f3d932 Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Tue, 18 Oct 2022 15:17:17 +0100 Subject: [PATCH 02/24] Update tests --- plone/app/discussion/tests/test_catalog.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/plone/app/discussion/tests/test_catalog.py b/plone/app/discussion/tests/test_catalog.py index 395f6c2..03af8e9 100644 --- a/plone/app/discussion/tests/test_catalog.py +++ b/plone/app/discussion/tests/test_catalog.py @@ -1,6 +1,7 @@ """Test the plone.app.discussion catalog indexes """ from datetime import datetime +from datetime import timezone from plone.app.discussion.interfaces import IConversation from plone.app.discussion.testing import ( # noqa PLONE_APP_DISCUSSION_INTEGRATION_TESTING, @@ -67,8 +68,10 @@ class ConversationCatalogTest(unittest.TestCase): comment1.text = "Comment text" comment1.creator = "jim" comment1.author_username = "Jim" - comment1.creation_date = datetime(2006, 9, 17, 14, 18, 12) - comment1.modification_date = datetime(2006, 9, 17, 14, 18, 12) + comment1.creation_date = \ + datetime(2006, 9, 17, 14, 18, 12).astimezone(timezone.utc) + comment1.modification_date = \ + datetime(2006, 9, 17, 14, 18, 12).astimezone(timezone.utc) new_comment1_id = conversation.addComment(comment1) self.comment_id = new_comment1_id @@ -115,15 +118,17 @@ class ConversationCatalogTest(unittest.TestCase): self.assertTrue("last_comment_date" in self.doc1_brain) self.assertEqual( self.doc1_brain.last_comment_date, - datetime(2006, 9, 17, 14, 18, 12), + datetime(2006, 9, 17, 14, 18, 12).astimezone(timezone.utc), ) # Add another comment and check if last comment date is updated. comment2 = createObject("plone.Comment") comment2.title = "Comment 2" comment2.text = "Comment text" - comment2.creation_date = datetime(2009, 9, 17, 14, 18, 12) - comment2.modification_date = datetime(2009, 9, 17, 14, 18, 12) + comment2.creation_date = \ + datetime(2009, 9, 17, 14, 18, 12).astimezone(timezone.utc) + comment2.modification_date = \ + datetime(2009, 9, 17, 14, 18, 12).astimezone(timezone.utc) new_comment2_id = self.conversation.addComment(comment2) comment2 = self.portal.doc1.restrictedTraverse( @@ -141,7 +146,7 @@ class ConversationCatalogTest(unittest.TestCase): doc1_brain = brains[0] self.assertEqual( doc1_brain.last_comment_date, - datetime(2009, 9, 17, 14, 18, 12), + datetime(2009, 9, 17, 14, 18, 12).astimezone(timezone.utc), ) # Remove the comment again @@ -158,7 +163,7 @@ class ConversationCatalogTest(unittest.TestCase): doc1_brain = brains[0] self.assertEqual( doc1_brain.last_comment_date, - datetime(2006, 9, 17, 14, 18, 12), + datetime(2006, 9, 17, 14, 18, 12).astimezone(timezone.utc), ) # remove all comments From 9f34a7f10df60a6fd722dc24e50181694c93de79 Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Tue, 18 Oct 2022 15:17:30 +0100 Subject: [PATCH 03/24] Add changelog --- news/204.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/204.bugfix diff --git a/news/204.bugfix b/news/204.bugfix new file mode 100644 index 0000000..6fe3d20 --- /dev/null +++ b/news/204.bugfix @@ -0,0 +1 @@ + - Set timezones for creation and modification dates of comments [instification] \ No newline at end of file From f1b753d088db6628a1787b4c94e20b2297a147e4 Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Tue, 18 Oct 2022 15:33:08 +0100 Subject: [PATCH 04/24] Update tests --- plone/app/discussion/tests/test_comment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plone/app/discussion/tests/test_comment.py b/plone/app/discussion/tests/test_comment.py index a0a0669..f9a9ac0 100644 --- a/plone/app/discussion/tests/test_comment.py +++ b/plone/app/discussion/tests/test_comment.py @@ -63,7 +63,7 @@ class CommentTest(unittest.TestCase): "get hidden by that" ) comment1 = createObject("plone.Comment") - local_utc = datetime.datetime.utcnow() + local_utc = datetime.datetime.now().astimezone(datetime.timezone.utc) for date in (comment1.creation_date, comment1.modification_date): difference = abs(date - local_utc) difference = difference.seconds From d6bbff89c72505ebcab1c818cabbddd708aa92c4 Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Tue, 18 Oct 2022 15:40:30 +0100 Subject: [PATCH 05/24] update conversation tests --- .../app/discussion/tests/test_conversation.py | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/plone/app/discussion/tests/test_conversation.py b/plone/app/discussion/tests/test_conversation.py index febce28..69179c0 100644 --- a/plone/app/discussion/tests/test_conversation.py +++ b/plone/app/discussion/tests/test_conversation.py @@ -8,6 +8,7 @@ from Acquisition import aq_base from Acquisition import aq_parent from datetime import datetime from datetime import timedelta +from datetime import timezone from plone.app.testing import setRoles from plone.app.testing import TEST_USER_ID from plone.app.vocabularies.types import BAD_TYPES @@ -70,7 +71,9 @@ class ConversationTest(unittest.TestCase): self.assertEqual(len(tuple(conversation.getThreads())), 1) self.assertEqual(conversation.total_comments(), 1) self.assertTrue( - conversation.last_comment_date - datetime.utcnow() < timedelta(seconds=1), + conversation.last_comment_date + - datetime.now().astimezone(timezone.utc)() + < timedelta(seconds=1), ) def test_private_comment(self): @@ -488,27 +491,32 @@ class ConversationTest(unittest.TestCase): # swapped in comment1 = createObject("plone.Comment") comment1.text = "Comment text" - comment1.creation_date = datetime.utcnow() - timedelta(4) + comment1.creation_date =\ + datetime.now().astimezone(timezone.utc)() - timedelta(4) conversation.addComment(comment1) comment2 = createObject("plone.Comment") comment2.text = "Comment text" - comment2.creation_date = datetime.utcnow() - timedelta(2) + comment2.creation_date =\ + datetime.now().astimezone(timezone.utc)() - timedelta(2) new_comment2_id = conversation.addComment(comment2) comment3 = createObject("plone.Comment") comment3.text = "Comment text" - comment3.creation_date = datetime.utcnow() - timedelta(1) + comment3.creation_date =\ + datetime.now().astimezone(timezone.utc)() - timedelta(1) new_comment3_id = conversation.addComment(comment3) # check if the latest comment is exactly one day old self.assertTrue( conversation.last_comment_date - < datetime.utcnow() - timedelta(hours=23, minutes=59, seconds=59), + < datetime.now().astimezone(timezone.utc)() + - timedelta(hours=23, minutes=59, seconds=59), ) self.assertTrue( conversation.last_comment_date - > datetime.utcnow() - timedelta(days=1, seconds=1), + > datetime.now().astimezone(timezone.utc)() + - timedelta(days=1, seconds=1), ) # remove the latest comment @@ -518,11 +526,13 @@ class ConversationTest(unittest.TestCase): # the latest comment should be exactly two days old self.assertTrue( conversation.last_comment_date - < datetime.utcnow() - timedelta(days=1, hours=23, minutes=59, seconds=59), + < datetime.now().astimezone(timezone.utc)() + - timedelta(days=1, hours=23, minutes=59, seconds=59), ) self.assertTrue( conversation.last_comment_date - > datetime.utcnow() - timedelta(days=2, seconds=1), + > datetime.now().astimezone(timezone.utc)() + - timedelta(days=2, seconds=1), ) # remove the latest comment again @@ -532,11 +542,13 @@ class ConversationTest(unittest.TestCase): # the latest comment should be exactly four days old self.assertTrue( conversation.last_comment_date - < datetime.utcnow() - timedelta(days=3, hours=23, minutes=59, seconds=59), + < datetime.now().astimezone(timezone.utc)() + - timedelta(days=3, hours=23, minutes=59, seconds=59), ) self.assertTrue( conversation.last_comment_date - > datetime.utcnow() - timedelta(days=4, seconds=2), + > datetime.now().astimezone(timezone.utc)() + - timedelta(days=4, seconds=2), ) def test_get_comments_full(self): From 45ef9a51c48f023384815c1d5002925181578514 Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Tue, 18 Oct 2022 15:56:05 +0100 Subject: [PATCH 06/24] Fix syntax errors --- .../app/discussion/tests/test_conversation.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plone/app/discussion/tests/test_conversation.py b/plone/app/discussion/tests/test_conversation.py index 69179c0..92b72ce 100644 --- a/plone/app/discussion/tests/test_conversation.py +++ b/plone/app/discussion/tests/test_conversation.py @@ -72,7 +72,7 @@ class ConversationTest(unittest.TestCase): self.assertEqual(conversation.total_comments(), 1) self.assertTrue( conversation.last_comment_date - - datetime.now().astimezone(timezone.utc)() + - datetime.now().astimezone(timezone.utc) < timedelta(seconds=1), ) @@ -492,30 +492,30 @@ class ConversationTest(unittest.TestCase): comment1 = createObject("plone.Comment") comment1.text = "Comment text" comment1.creation_date =\ - datetime.now().astimezone(timezone.utc)() - timedelta(4) + datetime.now().astimezone(timezone.utc) - timedelta(4) conversation.addComment(comment1) comment2 = createObject("plone.Comment") comment2.text = "Comment text" comment2.creation_date =\ - datetime.now().astimezone(timezone.utc)() - timedelta(2) + datetime.now().astimezone(timezone.utc) - timedelta(2) new_comment2_id = conversation.addComment(comment2) comment3 = createObject("plone.Comment") comment3.text = "Comment text" comment3.creation_date =\ - datetime.now().astimezone(timezone.utc)() - timedelta(1) + datetime.now().astimezone(timezone.utc) - timedelta(1) new_comment3_id = conversation.addComment(comment3) # check if the latest comment is exactly one day old self.assertTrue( conversation.last_comment_date - < datetime.now().astimezone(timezone.utc)() + < datetime.now().astimezone(timezone.utc) - timedelta(hours=23, minutes=59, seconds=59), ) self.assertTrue( conversation.last_comment_date - > datetime.now().astimezone(timezone.utc)() + > datetime.now().astimezone(timezone.utc) - timedelta(days=1, seconds=1), ) @@ -526,12 +526,12 @@ class ConversationTest(unittest.TestCase): # the latest comment should be exactly two days old self.assertTrue( conversation.last_comment_date - < datetime.now().astimezone(timezone.utc)() + < datetime.now().astimezone(timezone.utc) - timedelta(days=1, hours=23, minutes=59, seconds=59), ) self.assertTrue( conversation.last_comment_date - > datetime.now().astimezone(timezone.utc)() + > datetime.now().astimezone(timezone.utc) - timedelta(days=2, seconds=1), ) @@ -542,12 +542,12 @@ class ConversationTest(unittest.TestCase): # the latest comment should be exactly four days old self.assertTrue( conversation.last_comment_date - < datetime.now().astimezone(timezone.utc)() + < datetime.now().astimezone(timezone.utc) - timedelta(days=3, hours=23, minutes=59, seconds=59), ) self.assertTrue( conversation.last_comment_date - > datetime.now().astimezone(timezone.utc)() + > datetime.now().astimezone(timezone.utc) - timedelta(days=4, seconds=2), ) From fd1cfa8cadb5a2b5fffc6d490b33fd9488e1a250 Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Fri, 21 Oct 2022 14:16:58 +0100 Subject: [PATCH 07/24] Use local timezone when setting dates --- plone/app/discussion/browser/comments.py | 5 +++-- plone/app/discussion/comment.py | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/plone/app/discussion/browser/comments.py b/plone/app/discussion/browser/comments.py index 2056aa2..0c56651 100644 --- a/plone/app/discussion/browser/comments.py +++ b/plone/app/discussion/browser/comments.py @@ -11,6 +11,7 @@ from plone.app.discussion.interfaces import IComment from plone.app.discussion.interfaces import IConversation from plone.app.discussion.interfaces import IDiscussionSettings from plone.app.discussion.interfaces import IReplies +from plone.app.event.base import localized_now from plone.app.layout.viewlets.common import ViewletBase from plone.base.utils import safe_text from plone.registry.interfaces import IRegistry @@ -193,8 +194,8 @@ class CommentForm(extensible.ExtensibleForm, form.Form): setattr(comment, attribute, data[attribute]) # Set dates - comment.creation_date = datetime.now(timezone.utc) - comment.modification_date = datetime.now(timezone.utc) + comment.creation_date = localized_now() + comment.modification_date = localized_now() # Get author name and email comment.author_name, comment.author_email = self.get_author(data) diff --git a/plone/app/discussion/comment.py b/plone/app/discussion/comment.py index 8e9be09..3a02628 100644 --- a/plone/app/discussion/comment.py +++ b/plone/app/discussion/comment.py @@ -21,6 +21,7 @@ from plone.app.discussion.events import ReplyRemovedEvent from plone.app.discussion.interfaces import IComment from plone.app.discussion.interfaces import IConversation from plone.app.discussion.interfaces import IDiscussionSettings +from plone.app.event.base import localized_now from plone.base.interfaces.controlpanel import IMailSchema from plone.base.utils import safe_text from plone.registry.interfaces import IRegistry @@ -120,7 +121,7 @@ class Comment( # IConversation.addComment(). def __init__(self): - self.creation_date = self.modification_date = datetime.now(timezone.utc) + self.creation_date = self.modification_date = localized_now() self.mime_type = "text/plain" user = getSecurityManager().getUser() From 103bd894d988b9ce35681ae2030758cb566b105e Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Fri, 21 Oct 2022 14:17:34 +0100 Subject: [PATCH 08/24] Use correct timezone when indexing comments --- plone/app/discussion/catalog.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plone/app/discussion/catalog.py b/plone/app/discussion/catalog.py index 6413d11..bc7bdeb 100644 --- a/plone/app/discussion/catalog.py +++ b/plone/app/discussion/catalog.py @@ -109,7 +109,7 @@ def effective(object): object.creation_date.hour, object.creation_date.minute, object.creation_date.second, - "GMT", + object.creation_date.tzname(), ) @@ -123,7 +123,7 @@ def created(object): object.creation_date.hour, object.creation_date.minute, object.creation_date.second, - "GMT", + object.creation_date.tzname(), ) @@ -137,7 +137,7 @@ def modified(object): object.modification_date.hour, object.modification_date.minute, object.modification_date.second, - "GMT", + object.modification_date.tzname(), ) From 2506286c43ebba59de8b9e94c3dd9dce5311970c Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Fri, 21 Oct 2022 14:18:09 +0100 Subject: [PATCH 09/24] Create custom __getattribute__ method to return timezone aware dates from older Comment objects --- plone/app/discussion/comment.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/plone/app/discussion/comment.py b/plone/app/discussion/comment.py index 3a02628..62d7811 100644 --- a/plone/app/discussion/comment.py +++ b/plone/app/discussion/comment.py @@ -135,6 +135,17 @@ class Comment( user.getId(): ["Owner"], } + def __getattribute__(self, attr): + # In older versions of the add-on dates were set timezone naive. + # In tz aware versions, the value is stored as self._creation_date + if attr in ["creation_date", "modification_date"]: + old_date = super(Comment, self).__getattribute__(attr) + if old_date.tzinfo is None: + # Naive dates were always stored utc + return old_date.astimezone(timezone.utc) + return old_date + return super(Comment, self).__getattribute__(attr) + @property def __name__(self): return self.comment_id and str(self.comment_id) or None From c70abe2b6342bcd40a50b97a0e8de9a9e3b4b456 Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Fri, 21 Oct 2022 14:19:27 +0100 Subject: [PATCH 10/24] Create a test for the custom getter to ensure timezones are being added --- .../app/discussion/tests/test_conversation.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/plone/app/discussion/tests/test_conversation.py b/plone/app/discussion/tests/test_conversation.py index 92b72ce..b1484df 100644 --- a/plone/app/discussion/tests/test_conversation.py +++ b/plone/app/discussion/tests/test_conversation.py @@ -76,6 +76,36 @@ class ConversationTest(unittest.TestCase): < timedelta(seconds=1), ) + def test_timezone_naive_comment(self): + # Create a conversation. In this case we doesn't assign it to an + # object, as we just want to check the Conversation object API. + conversation = IConversation(self.portal.doc1) + + # Add a comment. Note: in real life, we always create comments via the + # factory to allow different factories to be swapped in + comment = createObject("plone.Comment") + comment.text = "Comment text" + + new_id = conversation.addComment(comment) + + # Check that comments have timezones + self.assertTrue(comment.creation_date.tzinfo) + self.assertTrue(comment.modification_date.tzinfo) + + # Remove the timezone from the comment dates + comment.creation_date = comment.creation_date.replace(tzinfo=None) + comment.modification_date = comment.modification_date.replace(tzinfo=None) + + # Check that the date is still correct + self.assertTrue( + conversation.last_comment_date + - datetime.now().astimezone(timezone.utc) + < timedelta(seconds=1), + ) + # Check that comments still have timezones + self.assertTrue(comment.creation_date.tzinfo) + self.assertTrue(comment.modification_date.tzinfo) + def test_private_comment(self): conversation = IConversation(self.portal.doc1) From f0cd076fd749d6ce0ad7f582d7886487144186f2 Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Fri, 21 Oct 2022 14:20:32 +0100 Subject: [PATCH 11/24] Update test_indexers to account for local timezones. Test across daylight savings bounds. --- plone/app/discussion/tests/test_indexers.py | 50 ++++++++++++++++----- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/plone/app/discussion/tests/test_indexers.py b/plone/app/discussion/tests/test_indexers.py index b1b30f1..1257c7b 100644 --- a/plone/app/discussion/tests/test_indexers.py +++ b/plone/app/discussion/tests/test_indexers.py @@ -4,11 +4,18 @@ from .. import catalog from ..interfaces import IConversation from ..testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa from datetime import datetime +from datetime import timezone +from dateutil import tz from DateTime import DateTime +from plone.app.event.base import localized_now +from plone.app.event.base import default_timezone from plone.app.testing import setRoles from plone.app.testing import TEST_USER_ID from plone.indexer.delegate import DelegatingIndexerFactory +from plone.registry.interfaces import IRegistry from zope.component import createObject +from zope.component import getUtility +import os import unittest @@ -36,6 +43,13 @@ class ConversationIndexersTest(unittest.TestCase): workflow = self.portal.portal_workflow workflow.doActionFor(self.portal.doc1, "publish") + # Change the timezone to europe to test timezones properly + os.environ['TZ'] = 'UTC' + reg_key = "plone.portal_timezone" + registry = getUtility(IRegistry) + registry[reg_key] = "Europe/Berlin" + self.portal_timezone = tz.gettz(default_timezone()) + # Create a conversation. conversation = IConversation(self.portal.doc1) @@ -43,6 +57,8 @@ class ConversationIndexersTest(unittest.TestCase): comment1.text = "Comment Text" comment1.creator = "jim" comment1.author_username = "Jim" + # Purposefully exclude timezone to test the conversation getter + # (see plone.app.discussion.comment.Comment object) comment1.creation_date = datetime(2006, 9, 17, 14, 18, 12) comment1.modification_date = datetime(2006, 9, 17, 14, 18, 12) self.new_id1 = conversation.addComment(comment1) @@ -51,16 +67,16 @@ class ConversationIndexersTest(unittest.TestCase): comment2.text = "Comment Text" comment2.creator = "emma" comment2.author_username = "Emma" - comment2.creation_date = datetime(2007, 12, 13, 4, 18, 12) - comment2.modification_date = datetime(2007, 12, 13, 4, 18, 12) + comment2.creation_date = datetime(2007, 12, 13, 4, 18, 12).astimezone(self.portal_timezone) + comment2.modification_date = datetime(2007, 12, 13, 4, 18, 12).astimezone(self.portal_timezone) self.new_id2 = conversation.addComment(comment2) comment3 = createObject("plone.Comment") comment3.text = "Comment Text" comment3.creator = "lukas" comment3.author_username = "Lukas" - comment3.creation_date = datetime(2009, 4, 12, 11, 12, 12) - comment3.modification_date = datetime(2009, 4, 12, 11, 12, 12) + comment3.creation_date = datetime(2009, 4, 12, 11, 12, 12).astimezone(self.portal_timezone) + comment3.modification_date = datetime(2009, 4, 12, 11, 12, 12).astimezone(self.portal_timezone) self.new_id3 = conversation.addComment(comment3) self.conversation = conversation @@ -88,12 +104,12 @@ class ConversationIndexersTest(unittest.TestCase): ) self.assertEqual( catalog.last_comment_date(self.portal.doc1)(), - datetime(2009, 4, 12, 11, 12, 12), + datetime(2009, 4, 12, 11, 12, 12).astimezone(self.portal_timezone), ) del self.conversation[self.new_id3] self.assertEqual( catalog.last_comment_date(self.portal.doc1)(), - datetime(2007, 12, 13, 4, 18, 12), + datetime(2007, 12, 13, 4, 18, 12).astimezone(self.portal_timezone), ) del self.conversation[self.new_id2] del self.conversation[self.new_id1] @@ -122,12 +138,24 @@ class CommentIndexersTest(unittest.TestCase): # Add a comment. Note: in real life, we always create comments via the # factory to allow different factories to be swapped in + # Change the timezone to europe to test timezones properly + os.environ['TZ'] = 'UTC' + reg_key = "plone.portal_timezone" + registry = getUtility(IRegistry) + registry[reg_key] = "Europe/Berlin" + portal_timezone = tz.gettz(default_timezone()) + + comment = createObject("plone.Comment") comment.text = "Lorem ipsum dolor sit amet." comment.creator = "jim" comment.author_name = "Jim" - comment.creation_date = datetime(2006, 9, 17, 14, 18, 12) - comment.modification_date = datetime(2008, 3, 12, 7, 32, 52) + + # Create date in CEST (ie not daylight savings = UTC+2) + comment.creation_date = datetime(2006, 9, 17, 14, 18, 12).astimezone(portal_timezone) + + # Create date in CET (ie daylight savings = UTC+1) + comment.modification_date = datetime(2008, 3, 12, 7, 32, 52).astimezone(portal_timezone) self.comment_id = conversation.addComment(comment) self.comment = comment.__of__(conversation) @@ -161,15 +189,15 @@ class CommentIndexersTest(unittest.TestCase): # Test if created, modified, effective etc. are set correctly self.assertEqual( catalog.created(self.comment)(), - DateTime(2006, 9, 17, 14, 18, 12, "GMT"), + DateTime(2006, 9, 17, 16, 18, 12, "GMT+2"), ) self.assertEqual( catalog.effective(self.comment)(), - DateTime(2006, 9, 17, 14, 18, 12, "GMT"), + DateTime(2006, 9, 17, 16, 18, 12, "GMT+2"), ) self.assertEqual( catalog.modified(self.comment)(), - DateTime(2008, 3, 12, 7, 32, 52, "GMT"), + DateTime(2008, 3, 12, 8, 32, 52, "GMT+1"), ) def test_searchable_text(self): From 99447966279f947412512222c31922f073711846 Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Fri, 21 Oct 2022 16:11:19 +0100 Subject: [PATCH 12/24] Fix timezone tests --- .../app/discussion/tests/test_conversation.py | 24 ++++++++++--------- plone/app/discussion/tests/test_indexers.py | 15 +++++++----- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/plone/app/discussion/tests/test_conversation.py b/plone/app/discussion/tests/test_conversation.py index b1484df..610ac4b 100644 --- a/plone/app/discussion/tests/test_conversation.py +++ b/plone/app/discussion/tests/test_conversation.py @@ -9,6 +9,8 @@ from Acquisition import aq_parent from datetime import datetime from datetime import timedelta from datetime import timezone +from dateutil import tz +from plone.app.event.base import default_timezone from plone.app.testing import setRoles from plone.app.testing import TEST_USER_ID from plone.app.vocabularies.types import BAD_TYPES @@ -72,7 +74,7 @@ class ConversationTest(unittest.TestCase): self.assertEqual(conversation.total_comments(), 1) self.assertTrue( conversation.last_comment_date - - datetime.now().astimezone(timezone.utc) + - datetime.now().astimezone(tz.gettz(default_timezone())) < timedelta(seconds=1), ) @@ -99,7 +101,7 @@ class ConversationTest(unittest.TestCase): # Check that the date is still correct self.assertTrue( conversation.last_comment_date - - datetime.now().astimezone(timezone.utc) + - datetime.now().astimezone(tz.gettz(default_timezone())) < timedelta(seconds=1), ) # Check that comments still have timezones @@ -522,30 +524,30 @@ class ConversationTest(unittest.TestCase): comment1 = createObject("plone.Comment") comment1.text = "Comment text" comment1.creation_date =\ - datetime.now().astimezone(timezone.utc) - timedelta(4) + datetime.now().astimezone(tz.gettz(default_timezone())) - timedelta(4) conversation.addComment(comment1) comment2 = createObject("plone.Comment") comment2.text = "Comment text" comment2.creation_date =\ - datetime.now().astimezone(timezone.utc) - timedelta(2) + datetime.now().astimezone(tz.gettz(default_timezone())) - timedelta(2) new_comment2_id = conversation.addComment(comment2) comment3 = createObject("plone.Comment") comment3.text = "Comment text" comment3.creation_date =\ - datetime.now().astimezone(timezone.utc) - timedelta(1) + datetime.now().astimezone(tz.gettz(default_timezone())) - timedelta(1) new_comment3_id = conversation.addComment(comment3) # check if the latest comment is exactly one day old self.assertTrue( conversation.last_comment_date - < datetime.now().astimezone(timezone.utc) + < datetime.now().astimezone(tz.gettz(default_timezone())) - timedelta(hours=23, minutes=59, seconds=59), ) self.assertTrue( conversation.last_comment_date - > datetime.now().astimezone(timezone.utc) + > datetime.now().astimezone(tz.gettz(default_timezone())) - timedelta(days=1, seconds=1), ) @@ -556,12 +558,12 @@ class ConversationTest(unittest.TestCase): # the latest comment should be exactly two days old self.assertTrue( conversation.last_comment_date - < datetime.now().astimezone(timezone.utc) + < datetime.now().astimezone(tz.gettz(default_timezone())) - timedelta(days=1, hours=23, minutes=59, seconds=59), ) self.assertTrue( conversation.last_comment_date - > datetime.now().astimezone(timezone.utc) + > datetime.now().astimezone(tz.gettz(default_timezone())) - timedelta(days=2, seconds=1), ) @@ -572,12 +574,12 @@ class ConversationTest(unittest.TestCase): # the latest comment should be exactly four days old self.assertTrue( conversation.last_comment_date - < datetime.now().astimezone(timezone.utc) + < datetime.now().astimezone(tz.gettz(default_timezone())) - timedelta(days=3, hours=23, minutes=59, seconds=59), ) self.assertTrue( conversation.last_comment_date - > datetime.now().astimezone(timezone.utc) + > datetime.now().astimezone(tz.gettz(default_timezone())) - timedelta(days=4, seconds=2), ) diff --git a/plone/app/discussion/tests/test_indexers.py b/plone/app/discussion/tests/test_indexers.py index 1257c7b..034543d 100644 --- a/plone/app/discussion/tests/test_indexers.py +++ b/plone/app/discussion/tests/test_indexers.py @@ -15,8 +15,9 @@ from plone.indexer.delegate import DelegatingIndexerFactory from plone.registry.interfaces import IRegistry from zope.component import createObject from zope.component import getUtility -import os +import time +import os import unittest @@ -44,7 +45,8 @@ class ConversationIndexersTest(unittest.TestCase): workflow.doActionFor(self.portal.doc1, "publish") # Change the timezone to europe to test timezones properly - os.environ['TZ'] = 'UTC' + os.environ['TZ'] = 'Europe/Berlin' + time.tzset() reg_key = "plone.portal_timezone" registry = getUtility(IRegistry) registry[reg_key] = "Europe/Berlin" @@ -139,7 +141,8 @@ class CommentIndexersTest(unittest.TestCase): # factory to allow different factories to be swapped in # Change the timezone to europe to test timezones properly - os.environ['TZ'] = 'UTC' + os.environ['TZ'] = 'Europe/Berlin' + time.tzset() reg_key = "plone.portal_timezone" registry = getUtility(IRegistry) registry[reg_key] = "Europe/Berlin" @@ -189,15 +192,15 @@ class CommentIndexersTest(unittest.TestCase): # Test if created, modified, effective etc. are set correctly self.assertEqual( catalog.created(self.comment)(), - DateTime(2006, 9, 17, 16, 18, 12, "GMT+2"), + DateTime(2006, 9, 17, 14, 18, 12, "GMT+2"), ) self.assertEqual( catalog.effective(self.comment)(), - DateTime(2006, 9, 17, 16, 18, 12, "GMT+2"), + DateTime(2006, 9, 17, 14, 18, 12, "GMT+2"), ) self.assertEqual( catalog.modified(self.comment)(), - DateTime(2008, 3, 12, 8, 32, 52, "GMT+1"), + DateTime(2008, 3, 12, 7, 32, 52, "GMT+1"), ) def test_searchable_text(self): From b28d6d1f91a964d880089003e184147bff5b51b6 Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Fri, 21 Oct 2022 17:12:08 +0100 Subject: [PATCH 13/24] Don't set the server timezone, seems to break other tests --- plone/app/discussion/tests/test_indexers.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/plone/app/discussion/tests/test_indexers.py b/plone/app/discussion/tests/test_indexers.py index 034543d..4e11c0e 100644 --- a/plone/app/discussion/tests/test_indexers.py +++ b/plone/app/discussion/tests/test_indexers.py @@ -45,8 +45,6 @@ class ConversationIndexersTest(unittest.TestCase): workflow.doActionFor(self.portal.doc1, "publish") # Change the timezone to europe to test timezones properly - os.environ['TZ'] = 'Europe/Berlin' - time.tzset() reg_key = "plone.portal_timezone" registry = getUtility(IRegistry) registry[reg_key] = "Europe/Berlin" @@ -140,14 +138,10 @@ class CommentIndexersTest(unittest.TestCase): # Add a comment. Note: in real life, we always create comments via the # factory to allow different factories to be swapped in - # Change the timezone to europe to test timezones properly - os.environ['TZ'] = 'Europe/Berlin' - time.tzset() + # Get the default timezone from the portal reg_key = "plone.portal_timezone" registry = getUtility(IRegistry) registry[reg_key] = "Europe/Berlin" - portal_timezone = tz.gettz(default_timezone()) - comment = createObject("plone.Comment") comment.text = "Lorem ipsum dolor sit amet." @@ -155,10 +149,10 @@ class CommentIndexersTest(unittest.TestCase): comment.author_name = "Jim" # Create date in CEST (ie not daylight savings = UTC+2) - comment.creation_date = datetime(2006, 9, 17, 14, 18, 12).astimezone(portal_timezone) + comment.creation_date = datetime(2006, 9, 17, 14, 18, 12).replace(tzinfo=tz.gettz("Europe/Berlin")) # Create date in CET (ie daylight savings = UTC+1) - comment.modification_date = datetime(2008, 3, 12, 7, 32, 52).astimezone(portal_timezone) + comment.modification_date = datetime(2008, 3, 12, 7, 32, 52).replace(tzinfo=tz.gettz("Europe/Berlin")) self.comment_id = conversation.addComment(comment) self.comment = comment.__of__(conversation) From 194f2d9f76ac8cc73274296eec1a95cd0049d791 Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Sat, 22 Oct 2022 10:48:10 +0100 Subject: [PATCH 14/24] Use configured portal timezone in tests --- .../app/discussion/tests/test_conversation.py | 30 ++++++++++++------- plone/app/discussion/tests/test_indexers.py | 2 +- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/plone/app/discussion/tests/test_conversation.py b/plone/app/discussion/tests/test_conversation.py index 610ac4b..a36f88b 100644 --- a/plone/app/discussion/tests/test_conversation.py +++ b/plone/app/discussion/tests/test_conversation.py @@ -20,6 +20,7 @@ from Products.CMFCore.utils import getToolByName from zope import interface from zope.annotation.interfaces import IAnnotations from zope.component import createObject +from zope.component import getUtility from zope.component import queryUtility import unittest @@ -34,6 +35,11 @@ class ConversationTest(unittest.TestCase): setRoles(self.portal, TEST_USER_ID, ["Manager"]) interface.alsoProvides(self.portal.REQUEST, IDiscussionLayer) + # Set the portal timezone to something non-utc + reg_key = "plone.portal_timezone" + registry = getUtility(IRegistry) + registry[reg_key] = "Europe/Berlin" + self.typetool = self.portal.portal_types self.portal_discussion = getToolByName( self.portal, @@ -90,23 +96,25 @@ class ConversationTest(unittest.TestCase): new_id = conversation.addComment(comment) - # Check that comments have timezones - self.assertTrue(comment.creation_date.tzinfo) - self.assertTrue(comment.modification_date.tzinfo) + # Check that comments have the correct portal timezones + self.assertTrue(comment.creation_date.tzinfo, + tz.gettz("Europe/Berlin")) + self.assertTrue(comment.modification_date.tzinfo, + tz.gettz("Europe/Berlin")) # Remove the timezone from the comment dates - comment.creation_date = comment.creation_date.replace(tzinfo=None) - comment.modification_date = comment.modification_date.replace(tzinfo=None) - - # Check that the date is still correct + comment.creation_date = datetime.utcnow() + comment.modification_date = datetime.utcnow() + + # Check that the timezone naive date is converted to UTC + # See https://github.com/plone/plone.app.discussion/pull/204 self.assertTrue( conversation.last_comment_date - - datetime.now().astimezone(tz.gettz(default_timezone())) + - datetime.now().astimezone(timezone.utc) < timedelta(seconds=1), ) - # Check that comments still have timezones - self.assertTrue(comment.creation_date.tzinfo) - self.assertTrue(comment.modification_date.tzinfo) + self.assertTrue(comment.creation_date.tzinfo, timezone.utc) + self.assertTrue(comment.modification_date.tzinfo, timezone.utc) def test_private_comment(self): conversation = IConversation(self.portal.doc1) diff --git a/plone/app/discussion/tests/test_indexers.py b/plone/app/discussion/tests/test_indexers.py index 4e11c0e..79c81a0 100644 --- a/plone/app/discussion/tests/test_indexers.py +++ b/plone/app/discussion/tests/test_indexers.py @@ -138,7 +138,7 @@ class CommentIndexersTest(unittest.TestCase): # Add a comment. Note: in real life, we always create comments via the # factory to allow different factories to be swapped in - # Get the default timezone from the portal + # Set the portal timezone to something non-utc reg_key = "plone.portal_timezone" registry = getUtility(IRegistry) registry[reg_key] = "Europe/Berlin" From 35b11a0120a267e875e5c0e733b823a10ad090c4 Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Sat, 22 Oct 2022 11:16:14 +0100 Subject: [PATCH 15/24] Prevent test failures when converting to timezone --- plone/app/discussion/tests/test_conversation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plone/app/discussion/tests/test_conversation.py b/plone/app/discussion/tests/test_conversation.py index a36f88b..54a6395 100644 --- a/plone/app/discussion/tests/test_conversation.py +++ b/plone/app/discussion/tests/test_conversation.py @@ -110,7 +110,7 @@ class ConversationTest(unittest.TestCase): # See https://github.com/plone/plone.app.discussion/pull/204 self.assertTrue( conversation.last_comment_date - - datetime.now().astimezone(timezone.utc) + - datetime.utcnow().replace(tzinfo=timezone.utc) < timedelta(seconds=1), ) self.assertTrue(comment.creation_date.tzinfo, timezone.utc) From 2df8ff4ea75567187a2c27769ce0f51b541ddddb Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Sat, 22 Oct 2022 12:13:20 +0100 Subject: [PATCH 16/24] Try reindexing comment and increasing delta (in case of slow test causing failure) --- plone/app/discussion/tests/test_conversation.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plone/app/discussion/tests/test_conversation.py b/plone/app/discussion/tests/test_conversation.py index 54a6395..b177c74 100644 --- a/plone/app/discussion/tests/test_conversation.py +++ b/plone/app/discussion/tests/test_conversation.py @@ -105,13 +105,15 @@ class ConversationTest(unittest.TestCase): # Remove the timezone from the comment dates comment.creation_date = datetime.utcnow() comment.modification_date = datetime.utcnow() + + comment.reindexObject() # Check that the timezone naive date is converted to UTC # See https://github.com/plone/plone.app.discussion/pull/204 self.assertTrue( conversation.last_comment_date - datetime.utcnow().replace(tzinfo=timezone.utc) - < timedelta(seconds=1), + < timedelta(seconds=10), ) self.assertTrue(comment.creation_date.tzinfo, timezone.utc) self.assertTrue(comment.modification_date.tzinfo, timezone.utc) From 0d643964a22755547450742831175eedb4d288e2 Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Sun, 23 Oct 2022 09:24:59 +0100 Subject: [PATCH 17/24] Don't convert time when adding timezone in getter. Refactor time comparison in tests to correct direction (avoid comparing against negative deltas) --- plone/app/discussion/comment.py | 2 +- plone/app/discussion/tests/test_conversation.py | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/plone/app/discussion/comment.py b/plone/app/discussion/comment.py index 62d7811..dc22187 100644 --- a/plone/app/discussion/comment.py +++ b/plone/app/discussion/comment.py @@ -142,7 +142,7 @@ class Comment( old_date = super(Comment, self).__getattribute__(attr) if old_date.tzinfo is None: # Naive dates were always stored utc - return old_date.astimezone(timezone.utc) + return old_date.replace(tzinfo=timezone.utc) return old_date return super(Comment, self).__getattribute__(attr) diff --git a/plone/app/discussion/tests/test_conversation.py b/plone/app/discussion/tests/test_conversation.py index b177c74..569b5f3 100644 --- a/plone/app/discussion/tests/test_conversation.py +++ b/plone/app/discussion/tests/test_conversation.py @@ -79,9 +79,9 @@ class ConversationTest(unittest.TestCase): self.assertEqual(len(tuple(conversation.getThreads())), 1) self.assertEqual(conversation.total_comments(), 1) self.assertTrue( - conversation.last_comment_date - - datetime.now().astimezone(tz.gettz(default_timezone())) - < timedelta(seconds=1), + datetime.now().astimezone(tz.gettz(default_timezone())) + - conversation.last_comment_date + >= timedelta(seconds=0) <= timedelta(seconds=1), ) def test_timezone_naive_comment(self): @@ -105,15 +105,13 @@ class ConversationTest(unittest.TestCase): # Remove the timezone from the comment dates comment.creation_date = datetime.utcnow() comment.modification_date = datetime.utcnow() - - comment.reindexObject() # Check that the timezone naive date is converted to UTC # See https://github.com/plone/plone.app.discussion/pull/204 self.assertTrue( - conversation.last_comment_date - - datetime.utcnow().replace(tzinfo=timezone.utc) - < timedelta(seconds=10), + datetime.utcnow().replace(tzinfo=timezone.utc) + - conversation.last_comment_date + >= timedelta(seconds=0) <= timedelta(seconds=1), ) self.assertTrue(comment.creation_date.tzinfo, timezone.utc) self.assertTrue(comment.modification_date.tzinfo, timezone.utc) From 2380bdacb3b0f4a1d758247a2717bfb0d9ef81b7 Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Sun, 23 Oct 2022 09:35:27 +0100 Subject: [PATCH 18/24] black/flake8 --- plone/app/discussion/browser/comments.py | 2 - plone/app/discussion/comment.py | 3 +- .../app/discussion/tests/test_conversation.py | 39 ++++++++++--------- plone/app/discussion/tests/test_indexers.py | 8 +--- 4 files changed, 24 insertions(+), 28 deletions(-) diff --git a/plone/app/discussion/browser/comments.py b/plone/app/discussion/browser/comments.py index 0c56651..02ca122 100644 --- a/plone/app/discussion/browser/comments.py +++ b/plone/app/discussion/browser/comments.py @@ -1,8 +1,6 @@ from AccessControl import getSecurityManager from AccessControl import Unauthorized from Acquisition import aq_inner -from datetime import datetime -from datetime import timezone from DateTime import DateTime from plone.app.discussion import _ from plone.app.discussion.browser.validator import CaptchaValidator diff --git a/plone/app/discussion/comment.py b/plone/app/discussion/comment.py index dc22187..c461048 100644 --- a/plone/app/discussion/comment.py +++ b/plone/app/discussion/comment.py @@ -5,11 +5,10 @@ from AccessControl.SecurityManagement import getSecurityManager from Acquisition import aq_base from Acquisition import aq_parent from Acquisition import Implicit -from datetime import datetime -from datetime import timezone from OFS.owner import Owned from OFS.role import RoleManager from OFS.Traversable import Traversable +from datetime import timezone from persistent import Persistent from plone.app.discussion import _ from plone.app.discussion.events import CommentAddedEvent diff --git a/plone/app/discussion/tests/test_conversation.py b/plone/app/discussion/tests/test_conversation.py index 569b5f3..ba70de9 100644 --- a/plone/app/discussion/tests/test_conversation.py +++ b/plone/app/discussion/tests/test_conversation.py @@ -80,8 +80,9 @@ class ConversationTest(unittest.TestCase): self.assertEqual(conversation.total_comments(), 1) self.assertTrue( datetime.now().astimezone(tz.gettz(default_timezone())) - - conversation.last_comment_date - >= timedelta(seconds=0) <= timedelta(seconds=1), + - conversation.last_comment_date + >= timedelta(seconds=0) + <= timedelta(seconds=1), ) def test_timezone_naive_comment(self): @@ -94,24 +95,23 @@ class ConversationTest(unittest.TestCase): comment = createObject("plone.Comment") comment.text = "Comment text" - new_id = conversation.addComment(comment) + conversation.addComment(comment) # Check that comments have the correct portal timezones - self.assertTrue(comment.creation_date.tzinfo, - tz.gettz("Europe/Berlin")) - self.assertTrue(comment.modification_date.tzinfo, - tz.gettz("Europe/Berlin")) - + self.assertTrue(comment.creation_date.tzinfo, tz.gettz("Europe/Berlin")) + self.assertTrue(comment.modification_date.tzinfo, tz.gettz("Europe/Berlin")) + # Remove the timezone from the comment dates comment.creation_date = datetime.utcnow() comment.modification_date = datetime.utcnow() - + # Check that the timezone naive date is converted to UTC # See https://github.com/plone/plone.app.discussion/pull/204 self.assertTrue( - datetime.utcnow().replace(tzinfo=timezone.utc) - - conversation.last_comment_date - >= timedelta(seconds=0) <= timedelta(seconds=1), + datetime.utcnow().replace(tzinfo=timezone.utc) + - conversation.last_comment_date + >= timedelta(seconds=0) + <= timedelta(seconds=1), ) self.assertTrue(comment.creation_date.tzinfo, timezone.utc) self.assertTrue(comment.modification_date.tzinfo, timezone.utc) @@ -531,20 +531,23 @@ class ConversationTest(unittest.TestCase): # swapped in comment1 = createObject("plone.Comment") comment1.text = "Comment text" - comment1.creation_date =\ - datetime.now().astimezone(tz.gettz(default_timezone())) - timedelta(4) + comment1.creation_date = datetime.now().astimezone( + tz.gettz(default_timezone()) + ) - timedelta(4) conversation.addComment(comment1) comment2 = createObject("plone.Comment") comment2.text = "Comment text" - comment2.creation_date =\ - datetime.now().astimezone(tz.gettz(default_timezone())) - timedelta(2) + comment2.creation_date = datetime.now().astimezone( + tz.gettz(default_timezone()) + ) - timedelta(2) new_comment2_id = conversation.addComment(comment2) comment3 = createObject("plone.Comment") comment3.text = "Comment text" - comment3.creation_date =\ - datetime.now().astimezone(tz.gettz(default_timezone())) - timedelta(1) + comment3.creation_date = datetime.now().astimezone( + tz.gettz(default_timezone()) + ) - timedelta(1) new_comment3_id = conversation.addComment(comment3) # check if the latest comment is exactly one day old diff --git a/plone/app/discussion/tests/test_indexers.py b/plone/app/discussion/tests/test_indexers.py index 79c81a0..5944b85 100644 --- a/plone/app/discussion/tests/test_indexers.py +++ b/plone/app/discussion/tests/test_indexers.py @@ -4,10 +4,8 @@ from .. import catalog from ..interfaces import IConversation from ..testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa from datetime import datetime -from datetime import timezone from dateutil import tz from DateTime import DateTime -from plone.app.event.base import localized_now from plone.app.event.base import default_timezone from plone.app.testing import setRoles from plone.app.testing import TEST_USER_ID @@ -16,8 +14,6 @@ from plone.registry.interfaces import IRegistry from zope.component import createObject from zope.component import getUtility -import time -import os import unittest @@ -147,10 +143,10 @@ class CommentIndexersTest(unittest.TestCase): comment.text = "Lorem ipsum dolor sit amet." comment.creator = "jim" comment.author_name = "Jim" - + # Create date in CEST (ie not daylight savings = UTC+2) comment.creation_date = datetime(2006, 9, 17, 14, 18, 12).replace(tzinfo=tz.gettz("Europe/Berlin")) - + # Create date in CET (ie daylight savings = UTC+1) comment.modification_date = datetime(2008, 3, 12, 7, 32, 52).replace(tzinfo=tz.gettz("Europe/Berlin")) From a069f49719adde298ce394131ae963450d00a6ea Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Mon, 24 Oct 2022 21:09:49 +0100 Subject: [PATCH 19/24] Change timezone to America/Los_Angeles to expose DateTime exceptions --- plone/app/discussion/tests/test_conversation.py | 6 +++--- plone/app/discussion/tests/test_indexers.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plone/app/discussion/tests/test_conversation.py b/plone/app/discussion/tests/test_conversation.py index ba70de9..b14888e 100644 --- a/plone/app/discussion/tests/test_conversation.py +++ b/plone/app/discussion/tests/test_conversation.py @@ -38,7 +38,7 @@ class ConversationTest(unittest.TestCase): # Set the portal timezone to something non-utc reg_key = "plone.portal_timezone" registry = getUtility(IRegistry) - registry[reg_key] = "Europe/Berlin" + registry[reg_key] = "America/Los_Angeles" self.typetool = self.portal.portal_types self.portal_discussion = getToolByName( @@ -98,8 +98,8 @@ class ConversationTest(unittest.TestCase): conversation.addComment(comment) # Check that comments have the correct portal timezones - self.assertTrue(comment.creation_date.tzinfo, tz.gettz("Europe/Berlin")) - self.assertTrue(comment.modification_date.tzinfo, tz.gettz("Europe/Berlin")) + self.assertTrue(comment.creation_date.tzinfo, tz.gettz("America/Los_Angeles")) + self.assertTrue(comment.modification_date.tzinfo, tz.gettz("America/Los_Angeles")) # Remove the timezone from the comment dates comment.creation_date = datetime.utcnow() diff --git a/plone/app/discussion/tests/test_indexers.py b/plone/app/discussion/tests/test_indexers.py index 5944b85..d654e45 100644 --- a/plone/app/discussion/tests/test_indexers.py +++ b/plone/app/discussion/tests/test_indexers.py @@ -40,10 +40,10 @@ class ConversationIndexersTest(unittest.TestCase): workflow = self.portal.portal_workflow workflow.doActionFor(self.portal.doc1, "publish") - # Change the timezone to europe to test timezones properly + # Change the timezone to PDT to test timezones properly reg_key = "plone.portal_timezone" registry = getUtility(IRegistry) - registry[reg_key] = "Europe/Berlin" + registry[reg_key] = "America/Los_Angeles" self.portal_timezone = tz.gettz(default_timezone()) # Create a conversation. From ab92def1a6a6791a7bad0c5c9e685846d055ec1f Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Mon, 24 Oct 2022 21:10:12 +0100 Subject: [PATCH 20/24] Use plone.app.event.base.DT to convert datetime -> DateTime --- plone/app/discussion/catalog.py | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/plone/app/discussion/catalog.py b/plone/app/discussion/catalog.py index bc7bdeb..59a83a6 100644 --- a/plone/app/discussion/catalog.py +++ b/plone/app/discussion/catalog.py @@ -6,6 +6,7 @@ Also provide event handlers to actually catalog the comments. from DateTime import DateTime from plone.app.discussion.interfaces import IComment from plone.app.discussion.interfaces import IConversation +from plone.app.event.base import DT from plone.base.utils import safe_text from plone.indexer import indexer from plone.uuid.interfaces import IUUID @@ -102,43 +103,19 @@ def in_response_to(object): @indexer(IComment) def effective(object): # the catalog index needs Zope DateTime instead of Python datetime - return DateTime( - object.creation_date.year, - object.creation_date.month, - object.creation_date.day, - object.creation_date.hour, - object.creation_date.minute, - object.creation_date.second, - object.creation_date.tzname(), - ) + return DT( object.creation_date ) @indexer(IComment) def created(object): # the catalog index needs Zope DateTime instead of Python datetime - return DateTime( - object.creation_date.year, - object.creation_date.month, - object.creation_date.day, - object.creation_date.hour, - object.creation_date.minute, - object.creation_date.second, - object.creation_date.tzname(), - ) + return DT(object.modification_date) @indexer(IComment) def modified(object): # the catalog index needs Zope DateTime instead of Python datetime - return DateTime( - object.modification_date.year, - object.modification_date.month, - object.modification_date.day, - object.modification_date.hour, - object.modification_date.minute, - object.modification_date.second, - object.modification_date.tzname(), - ) + return DT(object.modification_date) # Override the conversation indexers for comments From a95e67bcaed2dfb36b6340fad1ea816fee1090ac Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Mon, 24 Oct 2022 21:10:26 +0100 Subject: [PATCH 21/24] Use py3 super syntax --- plone/app/discussion/comment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plone/app/discussion/comment.py b/plone/app/discussion/comment.py index c461048..0186a6e 100644 --- a/plone/app/discussion/comment.py +++ b/plone/app/discussion/comment.py @@ -143,7 +143,7 @@ class Comment( # Naive dates were always stored utc return old_date.replace(tzinfo=timezone.utc) return old_date - return super(Comment, self).__getattribute__(attr) + return super().__getattribute__(attr) @property def __name__(self): From a3818a4743472f18585fd98c76c1dd2de84c8ced Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Mon, 24 Oct 2022 21:10:39 +0100 Subject: [PATCH 22/24] Update changelog --- news/204.bugfix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/news/204.bugfix b/news/204.bugfix index 6fe3d20..c6f022d 100644 --- a/news/204.bugfix +++ b/news/204.bugfix @@ -1 +1 @@ - - Set timezones for creation and modification dates of comments [instification] \ No newline at end of file + Set timezones for creation and modification dates of comments [instification] \ No newline at end of file From c5f17dc5e86d8d90aafdf72a03e739a448745752 Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Mon, 24 Oct 2022 21:44:57 +0100 Subject: [PATCH 23/24] Bump version for linked plone.restapi test_statictime version check --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 31aa776..e5dfd16 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import find_packages from setuptools import setup -version = "4.0.0b4.dev0" +version = "4.0.0b4.dev1" install_requires = [ "setuptools", From aa38be3ac2b220f5d1abb5e5f3202653c820909f Mon Sep 17 00:00:00 2001 From: Jon Pentland Date: Mon, 24 Oct 2022 22:43:35 +0100 Subject: [PATCH 24/24] Use correct date when indexing. Update comment indexes to use America/Los_Angeles --- plone/app/discussion/catalog.py | 2 +- plone/app/discussion/tests/test_indexers.py | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/plone/app/discussion/catalog.py b/plone/app/discussion/catalog.py index 59a83a6..5acb28b 100644 --- a/plone/app/discussion/catalog.py +++ b/plone/app/discussion/catalog.py @@ -109,7 +109,7 @@ def effective(object): @indexer(IComment) def created(object): # the catalog index needs Zope DateTime instead of Python datetime - return DT(object.modification_date) + return DT(object.creation_date) @indexer(IComment) diff --git a/plone/app/discussion/tests/test_indexers.py b/plone/app/discussion/tests/test_indexers.py index d654e45..a6f577f 100644 --- a/plone/app/discussion/tests/test_indexers.py +++ b/plone/app/discussion/tests/test_indexers.py @@ -137,18 +137,18 @@ class CommentIndexersTest(unittest.TestCase): # Set the portal timezone to something non-utc reg_key = "plone.portal_timezone" registry = getUtility(IRegistry) - registry[reg_key] = "Europe/Berlin" + registry[reg_key] = "America/Los_Angeles" comment = createObject("plone.Comment") comment.text = "Lorem ipsum dolor sit amet." comment.creator = "jim" comment.author_name = "Jim" - # Create date in CEST (ie not daylight savings = UTC+2) - comment.creation_date = datetime(2006, 9, 17, 14, 18, 12).replace(tzinfo=tz.gettz("Europe/Berlin")) + # Create date in PDT (ie daylight savings) + comment.creation_date = datetime(2006, 9, 17, 14, 18, 12).replace(tzinfo=tz.gettz("America/Los_Angeles")) - # Create date in CET (ie daylight savings = UTC+1) - comment.modification_date = datetime(2008, 3, 12, 7, 32, 52).replace(tzinfo=tz.gettz("Europe/Berlin")) + # Create date in PST (ie not daylight savings) + comment.modification_date = datetime(2008, 2, 12, 7, 32, 52).replace(tzinfo=tz.gettz("America/Los_Angeles")) self.comment_id = conversation.addComment(comment) self.comment = comment.__of__(conversation) @@ -182,15 +182,15 @@ class CommentIndexersTest(unittest.TestCase): # Test if created, modified, effective etc. are set correctly self.assertEqual( catalog.created(self.comment)(), - DateTime(2006, 9, 17, 14, 18, 12, "GMT+2"), + DateTime(2006, 9, 17, 14, 18, 12, "America/Los_Angeles"), ) self.assertEqual( catalog.effective(self.comment)(), - DateTime(2006, 9, 17, 14, 18, 12, "GMT+2"), + DateTime(2006, 9, 17, 14, 18, 12, "America/Los_Angeles"), ) self.assertEqual( catalog.modified(self.comment)(), - DateTime(2008, 3, 12, 7, 32, 52, "GMT+1"), + DateTime(2008, 2, 12, 7, 32, 52, "America/Los_Angeles"), ) def test_searchable_text(self):