Commit 082c52db authored by Simon Charette's avatar Simon Charette
Browse files

Refs #25774, #26348 -- Allowed Trunc functions to operate with time fields.

Thanks Josh for the amazing testing setup and Tim for the review.
parent 90468079
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -113,6 +113,14 @@ class BaseDatabaseOperations(object):
        """
        raise NotImplementedError('subclasses of BaseDatabaseOperations may require a datetime_trunk_sql() method')

    def time_trunc_sql(self, lookup_type, field_name):
        """
        Given a lookup_type of 'hour', 'minute' or 'second', returns the SQL
        that truncates the given time field field_name to a time object with
        only the given specificity.
        """
        raise NotImplementedError('subclasses of BaseDatabaseOperations may require a time_trunc_sql() method')

    def time_extract_sql(self, lookup_type, field_name):
        """
        Given a lookup_type of 'hour', 'minute' or 'second', returns the SQL
+12 −0
Original line number Diff line number Diff line
@@ -70,6 +70,18 @@ class DatabaseOperations(BaseDatabaseOperations):
            sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
        return sql, params

    def time_trunc_sql(self, lookup_type, field_name):
        fields = {
            'hour': '%%H:00:00',
            'minute': '%%H:%%i:00',
            'second': '%%H:%%i:%%s',
        }  # Use double percents to escape.
        if lookup_type in fields:
            format_str = fields[lookup_type]
            return "CAST(DATE_FORMAT(%s, '%s') AS TIME)" % (field_name, format_str)
        else:
            return "TIME(%s)" % (field_name)

    def date_interval_sql(self, timedelta):
        return "INTERVAL '%d 0:0:%d:%d' DAY_MICROSECOND" % (
            timedelta.days, timedelta.seconds, timedelta.microseconds), []
+12 −0
Original line number Diff line number Diff line
@@ -148,6 +148,18 @@ WHEN (new.%(col_name)s IS NULL)
            sql = "CAST(%s AS DATE)" % field_name  # Cast to DATE removes sub-second precision.
        return sql, []

    def time_trunc_sql(self, lookup_type, field_name):
        # The implementation is similar to `datetime_trunc_sql` as both
        # `DateTimeField` and `TimeField` are stored as TIMESTAMP where
        # the date part of the later is ignored.
        if lookup_type == 'hour':
            sql = "TRUNC(%s, 'HH24')" % field_name
        elif lookup_type == 'minute':
            sql = "TRUNC(%s, 'MI')" % field_name
        elif lookup_type == 'second':
            sql = "CAST(%s AS DATE)" % field_name  # Cast to DATE removes sub-second precision.
        return sql

    def get_db_converters(self, expression):
        converters = super(DatabaseOperations, self).get_db_converters(expression)
        internal_type = expression.output_field.get_internal_type()
+3 −0
Original line number Diff line number Diff line
@@ -56,6 +56,9 @@ class DatabaseOperations(BaseDatabaseOperations):
        sql = "DATE_TRUNC('%s', %s)" % (lookup_type, field_name)
        return sql, params

    def time_trunc_sql(self, lookup_type, field_name):
        return "DATE_TRUNC('%s', %s)::time" % (lookup_type, field_name)

    def deferrable_sql(self):
        return " DEFERRABLE INITIALLY DEFERRED"

+14 −0
Original line number Diff line number Diff line
@@ -213,6 +213,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
        conn.create_function("django_datetime_extract", 3, _sqlite_datetime_extract)
        conn.create_function("django_datetime_trunc", 3, _sqlite_datetime_trunc)
        conn.create_function("django_time_extract", 2, _sqlite_time_extract)
        conn.create_function("django_time_trunc", 2, _sqlite_time_trunc)
        conn.create_function("django_time_diff", 2, _sqlite_time_diff)
        conn.create_function("django_timestamp_diff", 2, _sqlite_timestamp_diff)
        conn.create_function("regexp", 2, _sqlite_regexp)
@@ -370,6 +371,19 @@ def _sqlite_date_trunc(lookup_type, dt):
        return "%i-%02i-%02i" % (dt.year, dt.month, dt.day)


def _sqlite_time_trunc(lookup_type, dt):
    try:
        dt = backend_utils.typecast_time(dt)
    except (ValueError, TypeError):
        return None
    if lookup_type == 'hour':
        return "%02i:00:00" % dt.hour
    elif lookup_type == 'minute':
        return "%02i:%02i:00" % (dt.hour, dt.minute)
    elif lookup_type == 'second':
        return "%02i:%02i:%02i" % (dt.hour, dt.minute, dt.second)


def _sqlite_datetime_parse(dt, tzname):
    if dt is None:
        return None
Loading