Ticket #17260: dates_in_tz.diff

File dates_in_tz.diff, 11.6 KB (added by akaariai, 3 years ago)
  • django/db/backends/__init__.py

    diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
    index 7674f5c..c8f010a 100644
    a b  
     1import datetime
     2
    13from django.db.utils import DatabaseError
     4from django.utils import timezone
    25
    36try:
    47    import thread
    class BaseDatabaseOperations(object): 
    807810
    808811        `value` is an int, containing the looked-up year.
    809812        """
    810         first = '%s-01-01 00:00:00'
    811         second = '%s-12-31 23:59:59.999999'
    812         return [first % value, second % value]
     813        tz = timezone.get_current_timezone() if settings.USE_TZ else None
     814        first_dt = datetime.datetime(year=value, month=01, day=01, tzinfo=tz)
     815        second_dt = datetime.datetime(year=value, month=12, day=31, hour=23,
     816                                      minute=59, second=59, microsecond=999999,
     817                                      tzinfo=tz)
     818        if settings.USE_TZ:
     819            first_dt = first_dt.astimezone(timezone.utc)
     820            second_dt = second_dt.astimezone(timezone.utc)
     821        first = first_dt.strftime('%Y-%m-%d %H:%M:%S')
     822        second = second_dt.strftime('%Y-%m-%d %H:%M:%S.%f')
     823        return [first, second]
    813824
    814825    def year_lookup_bounds_for_date_field(self, value):
    815826        """
  • django/db/backends/postgresql_psycopg2/operations.py

    diff --git a/django/db/backends/postgresql_psycopg2/operations.py b/django/db/backends/postgresql_psycopg2/operations.py
    index 949a05c..861d742 100644
    a b  
    11from django.db.backends import BaseDatabaseOperations
     2from django.conf import settings
     3from django.utils import timezone
    24
    35
    46class DatabaseOperations(BaseDatabaseOperations):
    class DatabaseOperations(BaseDatabaseOperations): 
    79
    810    def date_extract_sql(self, lookup_type, field_name):
    911        # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT
     12        field_sql = field_name
     13        if settings.USE_TZ:
     14            # Hey, I finally managed to get the UTC offset! And in oh-so-beautiful way...
     15            # Will this break at DST?
     16            time_offset = timezone.now().astimezone(timezone.get_current_timezone()).utcoffset()
     17            if time_offset:
     18                field_sql = self.date_interval_sql(field_name, '+', time_offset)
    1019        if lookup_type == 'week_day':
    1120            # For consistency across backends, we return Sunday=1, Saturday=7.
    12             return "EXTRACT('dow' FROM %s) + 1" % field_name
     21            return "EXTRACT('dow' FROM %s) + 1" % field_sql
    1322        else:
    14             return "EXTRACT('%s' FROM %s)" % (lookup_type, field_name)
     23            return "EXTRACT('%s' FROM %s)" % (lookup_type, field_sql)
    1524
    1625    def date_interval_sql(self, sql, connector, timedelta):
    1726        """
    class DatabaseOperations(BaseDatabaseOperations): 
    3241
    3342    def date_trunc_sql(self, lookup_type, field_name):
    3443        # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC
    35         return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name)
     44        field_sql = field_name
     45        if settings.USE_TZ:
     46            # Hey, I finally managed to get the UTC offset! And in oh-so-beautiful way...
     47            # Will this break at DST?
     48            time_offset = timezone.now().astimezone(timezone.get_current_timezone()).utcoffset()
     49            if time_offset:
     50                field_sql = self.date_interval_sql(field_name, '+', time_offset)
     51            # Alternate way: better except we don't know if PostgreSQL happens to support this
     52            # timezone... For example the testing returns +0300 for me, which really isn't a
     53            # timezone name :( The above way is a hack at its core, we really don't know if the
     54            # offset is correct for the date in the DB. It is likely not going to matter, the
     55            # DST offset is usually just one hour. But for example some country can change
     56            # their time zone for 23 hours, and that would break date lookups. Maybe a bit
     57            # non-realistic problem, but to be totally correct we should use PostgreSQL's at
     58            # time zone, not adding the current UTC offset.
     59            # field_sql = field_name + " AT TIME ZONE '%s'" % timezone.get_current_timezone_name()
     60        return "DATE_TRUNC('%s', %s)" % (lookup_type, field_sql)
    3661
    3762    def deferrable_sql(self):
    3863        return " DEFERRABLE INITIALLY DEFERRED"
  • django/db/backends/sqlite3/base.py

    diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py
    index 0b19442..421d883 100644
    a b class DatabaseOperations(BaseDatabaseOperations): 
    127127        # single quotes are used because this is a string (and could otherwise
    128128        # cause a collision with a field name).
    129129        return "django_date_trunc('%s', %s)" % (lookup_type.lower(), field_name)
     130 
     131    def year_lookup_bounds(self, value):
     132        """
     133        Returns a two-elements list with the lower and upper bound to be used
     134        with a BETWEEN operator to query a field value using a year lookup
     135
     136        `value` is an int, containing the looked-up year.
     137        """
     138        tz = timezone.get_current_timezone() if settings.USE_TZ else None
     139        first_dt = datetime.datetime(year=value, month=01, day=01, tzinfo=tz)
     140        second_dt = datetime.datetime(year=value, month=12, day=31, hour=23,
     141                                      minute=59, second=59, microsecond=999999,
     142                                      tzinfo=tz)
     143        if settings.USE_TZ:
     144            first_dt = first_dt.astimezone(timezone.utc)
     145            second_dt = second_dt.astimezone(timezone.utc)
     146        first = first_dt.strftime('%Y-%m-%d')
     147        second = second_dt.strftime('%Y-%m-%d %H:%M:%S.%f')
     148        return [first, second]
    130149
    131150    def drop_foreignkey_sql(self):
    132151        return ""
    class DatabaseOperations(BaseDatabaseOperations): 
    178197
    179198        return unicode(value)
    180199
    181     def year_lookup_bounds(self, value):
    182         first = '%s-01-01'
    183         second = '%s-12-31 23:59:59.999999'
    184         return [first % value, second % value]
    185 
    186200    def convert_values(self, value, field):
    187201        """SQLite returns floats when it should be returning decimals,
    188202        and gets dates and datetimes wrong.
    def _sqlite_extract(lookup_type, dt): 
    359373        dt = util.typecast_timestamp(dt)
    360374    except (ValueError, TypeError):
    361375        return None
     376    if settings.USE_TZ:
     377        tz = timezone.get_current_timezone()
     378        dt = dt.astimezone(tz)
    362379    if lookup_type == 'week_day':
    363380        return (dt.isoweekday() % 7) + 1
    364381    else:
    def _sqlite_date_trunc(lookup_type, dt): 
    369386        dt = util.typecast_timestamp(dt)
    370387    except (ValueError, TypeError):
    371388        return None
     389    if settings.USE_TZ:
     390        tz = timezone.get_current_timezone()
     391        dt = dt.astimezone(tz)
    372392    if lookup_type == 'year':
    373393        return "%i-01-01 00:00:00" % dt.year
    374394    elif lookup_type == 'month':
  • tests/modeltests/timezones/tests.py

    diff --git a/tests/modeltests/timezones/tests.py b/tests/modeltests/timezones/tests.py
    index a8d2c0c..81642df 100644
    a b LegacyDatabaseTests = override_settings(USE_TZ=False)(LegacyDatabaseTests) 
    277277
    278278#@override_settings(USE_TZ=True)
    279279class NewDatabaseTests(BaseDateTimeTests):
     280    @requires_tz_support
     281    def test_day_in_current_tz(self):
     282        dt = datetime.datetime(2012, 03, 05, 01, 00, 00)
     283        dt = dt.replace(tzinfo=EAT)
     284        Event.objects.create(dt=dt)
     285        with timezone.override(UTC):
     286            self.assertEqual(Event.objects.dates('dt', 'day')[0].day, 4)
     287        with timezone.override(EAT):
     288            self.assertEqual(Event.objects.dates('dt', 'day')[0].day, 5)
    280289
    281290    @requires_tz_support
    282291    @skipIf(sys.version_info < (2, 6), "this test requires Python >= 2.6")
    class NewDatabaseTests(BaseDateTimeTests): 
    427436        # implementation is changed to perform the aggregation is local time.
    428437        Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT))
    429438        Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT))
    430         self.assertEqual(Event.objects.filter(dt__year=2011).count(), 1)
    431         self.assertEqual(Event.objects.filter(dt__month=1).count(), 1)
    432         self.assertEqual(Event.objects.filter(dt__day=1).count(), 1)
    433         self.assertEqual(Event.objects.filter(dt__week_day=7).count(), 1)
     439        with timezone.override(EAT):
     440            self.assertEqual(Event.objects.filter(dt__year=2011).count(), 2)
     441            self.assertEqual(Event.objects.filter(dt__month=1).count(), 2)
     442            self.assertEqual(Event.objects.filter(dt__day=1).count(), 2)
     443            self.assertEqual(Event.objects.filter(dt__week_day=7).count(), 2)
     444        with timezone.override(UTC):
     445            self.assertEqual(Event.objects.filter(dt__year=2011).count(), 1)
     446            self.assertEqual(Event.objects.filter(dt__month=1).count(), 1)
     447            self.assertEqual(Event.objects.filter(dt__day=1).count(), 1)
     448            self.assertEqual(Event.objects.filter(dt__week_day=7).count(), 1)
    434449
    435450    def test_query_aggregation(self):
    436451        # Only min and max make sense for datetimes.
    class NewDatabaseTests(BaseDateTimeTests): 
    469484        # Same comment as in test_query_date_related_filters.
    470485        Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT))
    471486        Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT))
    472         self.assertQuerysetEqual(Event.objects.dates('dt', 'year'),
    473                 [datetime.datetime(2010, 1, 1, tzinfo=UTC),
    474                  datetime.datetime(2011, 1, 1, tzinfo=UTC)],
    475                 transform=lambda d: d)
    476         self.assertQuerysetEqual(Event.objects.dates('dt', 'month'),
    477                 [datetime.datetime(2010, 12, 1, tzinfo=UTC),
    478                  datetime.datetime(2011, 1, 1, tzinfo=UTC)],
    479                 transform=lambda d: d)
    480         self.assertQuerysetEqual(Event.objects.dates('dt', 'day'),
    481                 [datetime.datetime(2010, 12, 31, tzinfo=UTC),
    482                  datetime.datetime(2011, 1, 1, tzinfo=UTC)],
    483                 transform=lambda d: d)
     487        with timezone.override(EAT):
     488            self.assertQuerysetEqual(Event.objects.dates('dt', 'year'),
     489                    [datetime.datetime(2011, 1, 1, tzinfo=UTC)],
     490                    transform=lambda d: d)
     491            self.assertQuerysetEqual(Event.objects.dates('dt', 'month'),
     492                    [datetime.datetime(2011, 1, 1, tzinfo=UTC)],
     493                    transform=lambda d: d)
     494            self.assertQuerysetEqual(Event.objects.dates('dt', 'day'),
     495                    [datetime.datetime(2011, 1, 1, tzinfo=UTC)],
     496                    transform=lambda d: d)
     497        with timezone.override(UTC):
     498            self.assertQuerysetEqual(Event.objects.dates('dt', 'year'),
     499                    [datetime.datetime(2010, 1, 1, tzinfo=UTC),
     500                    datetime.datetime(2011, 1, 1, tzinfo=UTC)],
     501                    transform=lambda d: d)
     502            self.assertQuerysetEqual(Event.objects.dates('dt', 'month'),
     503                    [datetime.datetime(2010, 12, 1, tzinfo=UTC),
     504                    datetime.datetime(2011, 1, 1, tzinfo=UTC)],
     505                    transform=lambda d: d)
     506            self.assertQuerysetEqual(Event.objects.dates('dt', 'day'),
     507                    [datetime.datetime(2010, 12, 31, tzinfo=UTC),
     508                    datetime.datetime(2011, 1, 1, tzinfo=UTC)],
     509                    transform=lambda d: d)
    484510
    485511    def test_raw_sql(self):
    486512        # Regression test for #17755
Back to Top