From bcef8fa13b77674daa20641348e6f55b5c313e35 Mon Sep 17 00:00:00 2001 From: John Bodley <4567245+john-bodley@users.noreply.github.com> Date: Fri, 12 Nov 2021 09:46:55 -0800 Subject: [PATCH] feature: Add quarter unit to datetrunc (#17416) Co-authored-by: John Bodley --- .../DateFilterControl/components/DateFunctionTooltip.tsx | 2 +- superset/utils/date_parser.py | 7 +++++-- tests/integration_tests/utils/date_parser_tests.py | 4 ++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/superset-frontend/src/explore/components/controls/DateFilterControl/components/DateFunctionTooltip.tsx b/superset-frontend/src/explore/components/controls/DateFilterControl/components/DateFunctionTooltip.tsx index 643c8fa5fd853..b0825347ab465 100644 --- a/superset-frontend/src/explore/components/controls/DateFilterControl/components/DateFunctionTooltip.tsx +++ b/superset-frontend/src/explore/components/controls/DateFilterControl/components/DateFunctionTooltip.tsx @@ -62,7 +62,7 @@ dateadd(datetime("2020-03-01"), 2, day)`}

{t('Syntax')}

         {`datetrunc([datetime], [dateunit])
-dateunit = (year | month | week)`}
+dateunit = (year | quarter | month | week)`}
       

{t('Example')}

diff --git a/superset/utils/date_parser.py b/superset/utils/date_parser.py
index 802c185d9ec64..a72d49fbf47aa 100644
--- a/superset/utils/date_parser.py
+++ b/superset/utils/date_parser.py
@@ -21,6 +21,7 @@
 from time import struct_time
 from typing import Dict, List, Optional, Tuple
 
+import pandas as pd
 import parsedatetime
 from dateutil.parser import parse
 from dateutil.relativedelta import relativedelta
@@ -322,10 +323,12 @@ def eval(self) -> datetime:
             dttm = dttm.replace(
                 month=1, day=1, hour=0, minute=0, second=0, microsecond=0
             )
+        if unit == "quarter":
+            dttm = pd.Period(pd.Timestamp(dttm), freq="Q").to_timestamp()
         elif unit == "month":
             dttm = dttm.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
         elif unit == "week":
-            dttm = dttm - relativedelta(days=dttm.weekday())
+            dttm -= relativedelta(days=dttm.weekday())
             dttm = dttm.replace(hour=0, minute=0, second=0, microsecond=0)
         elif unit == "day":
             dttm = dttm.replace(hour=0, minute=0, second=0, microsecond=0)
@@ -443,7 +446,7 @@ def datetime_parser() -> ParseResults:  # pylint: disable=too-many-locals
         + Group(
             date_expr
             + comma
-            + (YEAR | MONTH | WEEK | DAY | HOUR | MINUTE | SECOND)
+            + (YEAR | QUARTER | MONTH | WEEK | DAY | HOUR | MINUTE | SECOND)
             + ppOptional(comma)
         )
         + rparen
diff --git a/tests/integration_tests/utils/date_parser_tests.py b/tests/integration_tests/utils/date_parser_tests.py
index 4cf979e83c671..98e149d4057af 100644
--- a/tests/integration_tests/utils/date_parser_tests.py
+++ b/tests/integration_tests/utils/date_parser_tests.py
@@ -209,6 +209,10 @@ def test_datetime_eval(self):
         expected = datetime(2016, 1, 1, 0, 0, 0)
         self.assertEqual(result, expected)
 
+        result = datetime_eval("datetrunc(datetime('now'), quarter)")
+        expected = datetime(2016, 10, 1, 0, 0, 0)
+        self.assertEqual(result, expected)
+
         result = datetime_eval("datetrunc(datetime('now'), month)")
         expected = datetime(2016, 11, 1, 0, 0, 0)
         self.assertEqual(result, expected)