diff --git a/trytond/CHANGELOG b/trytond/CHANGELOG index 2cb57459de..db7ffc34c5 100644 --- a/trytond/CHANGELOG +++ b/trytond/CHANGELOG @@ -1,3 +1,4 @@ +* Add support for DATE_TRUNC from INTERVAL to SQLite backend * Add support for EXTRACT from INTERVAL to SQLite backend * Add msg_gettext and msg_ngettext to Report context * Support plural translation of messages diff --git a/trytond/trytond/backend/sqlite/database.py b/trytond/trytond/backend/sqlite/database.py index 3e200ebdd1..51e796358f 100644 --- a/trytond/trytond/backend/sqlite/database.py +++ b/trytond/trytond/backend/sqlite/database.py @@ -161,6 +161,8 @@ class SQLiteExtract(Function): def date_trunc(field, source): if field is None or source is None: return None + if isinstance(source, (int, float)): # interval + return _date_trunc_interval(field, source) for fromisoformat in [ dt.date.fromisoformat, dt.time.fromisoformat, @@ -199,6 +201,22 @@ def date_trunc(field, source): return adapt_timedelta(value) +def _date_trunc_interval(field, source): + value = dt.timedelta(seconds=source) + field = field.lower() + for attribute, delta in [ + ('microseconds', value.microseconds), + ('seconds', value.seconds % 60), + ('minutes', (value.seconds // 60) % 60), + ('hours', (value.seconds // (60 * 60)) % 24), + ('days', value.days), + ]: + if field.startswith(attribute[:-1]): + break + value -= dt.timedelta(**{attribute: delta}) + return adapt_timedelta(value) + + def split_part(text, delimiter, count): if text is None: return None diff --git a/trytond/trytond/tests/test_backend.py b/trytond/trytond/tests/test_backend.py index 26675868ec..fa684184cd 100644 --- a/trytond/trytond/tests/test_backend.py +++ b/trytond/trytond/tests/test_backend.py @@ -228,6 +228,32 @@ class BackendTestCase(TestCase): value, = cursor.fetchone() self.assertEqual(value, result) + @with_transaction() + def test_function_date_trunc_timedelta(self): + "Test DateTrunc function with timedelta" + cursor = Transaction().connection.cursor() + date = dt.timedelta( + days=16, hours=20, minutes=38, seconds=40, microseconds=100) + for type_, result in [ + ('microsecond', dt.timedelta( + days=16, hours=20, minutes=38, seconds=40, + microseconds=100)), + ('second', dt.timedelta( + days=16, hours=20, minutes=38, seconds=40)), + ('minute', dt.timedelta(days=16, hours=20, minutes=38)), + ('hour', dt.timedelta(days=16, hours=20)), + ('day', dt.timedelta(days=16)), + ('month', dt.timedelta()), + ]: + for type_ in [type_.lower(), type_.upper()]: + with self.subTest(type_=type_): + query = Select([DateTrunc(type_, date).as_('value')]) + if backend.name == 'sqlite': + sqlite_apply_types(query, ['INTERVAL']) + cursor.execute(*query) + value, = cursor.fetchone() + self.assertEqual(value, result) + @with_transaction() def test_function_date_trunc_null(self): "test DateTrunc function with NULL"