diff --git a/CHANGELOG b/CHANGELOG index a85926de78..e8380b6891 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Add partial TO_CHAR support for date and datetime on SQLite * Setup default logging for WSGI app * Add format argument to report format_date function * Change expand attribute into a factor diff --git a/trytond/backend/sqlite/database.py b/trytond/backend/sqlite/database.py index 9fd9b7f64c..32cb1a1829 100644 --- a/trytond/backend/sqlite/database.py +++ b/trytond/backend/sqlite/database.py @@ -140,6 +140,50 @@ def now(): _nows = WeakKeyDictionary() +def to_char(value, format): + try: + value = datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S.%f') + except ValueError: + try: + value = datetime.datetime.strptime(value, '%Y-%m-%d').date() + except ValueError: + pass + if isinstance(value, datetime.date): + # Convert SQL pattern into compatible Python + return value.strftime(format + .replace('%', '%%') + .replace('HH12', '%I') + .replace('HH24', '%H') + .replace('HH', '%I') + .replace('MI', '%M') + .replace('SS', '%S') + .replace('US', '%f') + .replace('AM', '%p') + .replace('A.M.', '%p') + .replace('PM', '%p') + .replace('P.M.', '%p') + .replace('am', '%p') + .replace('a.m.', '%p') + .replace('pm', '%p') + .replace('p.m.', '%p') + .replace('YYYY', '%Y') + .replace('YY', '%y') + .replace('Month', '%B') + .replace('Mon', '%b') + .replace('MM', '%m') + .replace('Day', '%A') + .replace('Dy', '%a') + .replace('DDD', '%j') + .replace('DD', '%d') + .replace('D', '%w') + .replace('TZ', '%Z') + ) + elif isinstance(value, datetime.timedelta): + raise NotImplementedError + else: + raise NotImplementedError + + class SQLiteSubstring(Function): __slots__ = () _function = 'SUBSTR' @@ -290,6 +334,7 @@ class Database(DatabaseInterface): self._conn.create_function('date_trunc', 2, date_trunc) self._conn.create_function('split_part', 3, split_part) self._conn.create_function('position', 2, SQLitePosition.position) + self._conn.create_function('to_char', 2, to_char) self._conn.create_function('overlay', 3, SQLiteOverlay.overlay) self._conn.create_function('overlay', 4, SQLiteOverlay.overlay) if sqlite.sqlite_version_info < (3, 3, 14): diff --git a/trytond/tests/test_backend.py b/trytond/tests/test_backend.py index f776119dd7..e770e29d79 100644 --- a/trytond/tests/test_backend.py +++ b/trytond/tests/test_backend.py @@ -1,9 +1,10 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. +import datetime import unittest from sql import Select -from sql.functions import CurrentTimestamp +from sql.functions import CurrentTimestamp, ToChar from trytond.tests.test_tryton import activate_module, with_transaction from trytond.transaction import Transaction @@ -59,6 +60,30 @@ class BackendTestCase(unittest.TestCase): self.assertNotEqual(current, second) + @with_transaction() + def test_to_char_datetime(self): + "Test TO_CHAR with datetime" + now = datetime.datetime.now() + query = Select([ToChar(now, 'YYYYMMDD HH24:MI:SS.US')]) + cursor = Transaction().connection.cursor() + + cursor.execute(*query) + text, = cursor.fetchone() + + self.assertEqual(text, now.strftime('%Y%m%d %H:%M:%S.%f')) + + @with_transaction() + def test_to_char_date(self): + "Test TO_CHAR with date" + today = datetime.date.today() + query = Select([ToChar(today, 'YYYY-MM-DD')]) + cursor = Transaction().connection.cursor() + + cursor.execute(*query) + text, = cursor.fetchone() + + self.assertEqual(text, today.strftime('%Y-%m-%d')) + def suite(): return unittest.TestLoader().loadTestsFromTestCase(BackendTestCase)