diff --git a/news/188.bugfix b/news/188.bugfix
new file mode 100644
index 00000000..44793782
--- /dev/null
+++ b/news/188.bugfix
@@ -0,0 +1 @@
+Fix merging multiple date operations. @davisagli
diff --git a/src/plone/app/querystring/profiles.zcml b/src/plone/app/querystring/profiles.zcml
index f0420589..9964d900 100644
--- a/src/plone/app/querystring/profiles.zcml
+++ b/src/plone/app/querystring/profiles.zcml
@@ -90,4 +90,12 @@
directory="profiles/upgrades/to_14"
/>
+
+
diff --git a/src/plone/app/querystring/profiles/default/metadata.xml b/src/plone/app/querystring/profiles/default/metadata.xml
index 3949c2a4..c8b58bcf 100644
--- a/src/plone/app/querystring/profiles/default/metadata.xml
+++ b/src/plone/app/querystring/profiles/default/metadata.xml
@@ -1,6 +1,6 @@
- 14
+ 15
profile-plone.app.registry:default
diff --git a/src/plone/app/querystring/profiles/default/registry.xml b/src/plone/app/querystring/profiles/default/registry.xml
index 24b38735..ab474d24 100644
--- a/src/plone/app/querystring/profiles/default/registry.xml
+++ b/src/plone/app/querystring/profiles/default/registry.xml
@@ -67,7 +67,7 @@
i18n:translate=""
>Before date
Please use YYYY/MM/DD.
- plone.app.querystring.queryparser._lessThan
+ plone.app.querystring.queryparser._dateLessThan
DateWidget
@@ -80,7 +80,7 @@
Please use YYYY/MM/DD.
- plone.app.querystring.queryparser._largerThan
+ plone.app.querystring.queryparser._dateLargerThan
DateWidget
@@ -93,7 +93,7 @@
Please use YYYY/MM/DD.
- plone.app.querystring.queryparser._between
+ plone.app.querystring.queryparser._dateBetween
DateRangeWidget
diff --git a/src/plone/app/querystring/profiles/upgrades/to_15/registry.xml b/src/plone/app/querystring/profiles/upgrades/to_15/registry.xml
new file mode 100644
index 00000000..b9727e7e
--- /dev/null
+++ b/src/plone/app/querystring/profiles/upgrades/to_15/registry.xml
@@ -0,0 +1,24 @@
+
+
+
+
+ plone.app.querystring.queryparser._dateLessThan
+
+
+
+ plone.app.querystring.queryparser._dateLargerThan
+
+
+
+ plone.app.querystring.queryparser._dateBetween
+
+
+
diff --git a/src/plone/app/querystring/queryparser.py b/src/plone/app/querystring/queryparser.py
index 1f0a0561..575de757 100644
--- a/src/plone/app/querystring/queryparser.py
+++ b/src/plone/app/querystring/queryparser.py
@@ -163,6 +163,19 @@ def _between(context, row):
return tmp
+def _dateBetween(context, row):
+ try:
+ start_date = DateTime(row.values[0])
+ except DateTimeError:
+ start_date = DateTime(0)
+ try:
+ end_date = DateTime(row.values[1])
+ except DateTimeError:
+ end_date = DateTime()
+ row = Row(index=row.index, operator=row.operator, values=(start_date, end_date))
+ return _between(context, row)
+
+
def _largerThan(context, row):
tmp = {
row.index: {
@@ -186,6 +199,19 @@ def _intLargerThan(context, row):
}
+def _dateLargerThan(context, row):
+ try:
+ value = DateTime(row.values)
+ except (DateTimeError, TypeError, AttributeError):
+ return {}
+ return {
+ row.index: {
+ "query": value,
+ "range": "min",
+ },
+ }
+
+
def _lessThan(context, row):
tmp = {
row.index: {
@@ -209,6 +235,19 @@ def _intLessThan(context, row):
}
+def _dateLessThan(context, row):
+ try:
+ value = DateTime(row.values)
+ except (DateTimeError, TypeError, AttributeError):
+ return {}
+ return {
+ row.index: {
+ "query": value,
+ "range": "max",
+ },
+ }
+
+
def _currentUser(context, row):
"""Current user lookup"""
mt = getToolByName(context, "portal_membership")
diff --git a/src/plone/app/querystring/tests/testQueryParser.py b/src/plone/app/querystring/tests/testQueryParser.py
index d42c08ff..e39af124 100644
--- a/src/plone/app/querystring/tests/testQueryParser.py
+++ b/src/plone/app/querystring/tests/testQueryParser.py
@@ -289,6 +289,30 @@ def test_merge_ranges(self):
parsed = queryparser.parseFormquery(MockSite(), data)
self.assertEqual(parsed, {"modified": {"query": [10, 20], "range": "minmax"}})
+ def test_merge_date_operations(self):
+ data = [
+ {
+ "i": "start",
+ "o": "plone.app.querystring.operation.date.afterRelativeDate",
+ "v": "0",
+ },
+ {
+ "i": "start",
+ "o": "plone.app.querystring.operation.date.largerThan",
+ "v": "2026/05/04",
+ },
+ ]
+ parsed = queryparser.parseFormquery(MockSite(), data)
+ self.assertEqual(
+ parsed,
+ {
+ "start": {
+ "query": [DateTime("2026/05/04"), DateTime().earliestTime()],
+ "range": "min",
+ }
+ },
+ )
+
class TestQueryGenerators(TestQueryParserBase):
def test__between(self):
diff --git a/src/plone/app/querystring/tests/testRegistryIntegration.py b/src/plone/app/querystring/tests/testRegistryIntegration.py
index 4a965e2b..46ca75d2 100644
--- a/src/plone/app/querystring/tests/testRegistryIntegration.py
+++ b/src/plone/app/querystring/tests/testRegistryIntegration.py
@@ -33,7 +33,7 @@ def test_date_lessthan(self):
self.assertEqual(registry[prefix + ".description"], "Please use YYYY/MM/DD.")
self.assertEqual(
registry[prefix + ".operation"],
- "plone.app.querystring.queryparser._lessThan",
+ "plone.app.querystring.queryparser._dateLessThan",
)
diff --git a/src/plone/app/querystring/upgrades.zcml b/src/plone/app/querystring/upgrades.zcml
index 07c2e230..d0445755 100644
--- a/src/plone/app/querystring/upgrades.zcml
+++ b/src/plone/app/querystring/upgrades.zcml
@@ -159,4 +159,15 @@
/>
+
+
+
+