Ticket #17260: dates_in_tz.2.diff
File dates_in_tz.2.diff, 12.2 KB (added by , 13 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 1 import datetime 2 1 3 from django.db.utils import DatabaseError 4 from django.utils import timezone 2 5 3 6 try: 4 7 import thread … … class BaseDatabaseOperations(object): 807 810 808 811 `value` is an int, containing the looked-up year. 809 812 """ 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] 813 824 814 825 def year_lookup_bounds_for_date_field(self, value): 815 826 """ -
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..47a89b9 100644
a b 1 1 from django.db.backends import BaseDatabaseOperations 2 from django.conf import settings 3 from django.utils import timezone 2 4 3 5 4 6 class DatabaseOperations(BaseDatabaseOperations): … … class DatabaseOperations(BaseDatabaseOperations): 7 9 8 10 def date_extract_sql(self, lookup_type, field_name): 9 11 # 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 tzname = timezone.get_db_timezone_name(timezone.get_current_timezone()) 15 # SQL-injection warning... 16 field_sql = "%s AT TIME ZONE '%s'" % (field_sql, tzname) 10 17 if lookup_type == 'week_day': 11 18 # For consistency across backends, we return Sunday=1, Saturday=7. 12 return "EXTRACT('dow' FROM %s) + 1" % field_ name19 return "EXTRACT('dow' FROM %s) + 1" % field_sql 13 20 else: 14 return "EXTRACT('%s' FROM %s)" % (lookup_type, field_ name)21 return "EXTRACT('%s' FROM %s)" % (lookup_type, field_sql) 15 22 16 23 def date_interval_sql(self, sql, connector, timedelta): 17 24 """ … … class DatabaseOperations(BaseDatabaseOperations): 32 39 33 40 def date_trunc_sql(self, lookup_type, field_name): 34 41 # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC 35 return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) 42 field_sql = field_name 43 if settings.USE_TZ: 44 tzname = timezone.get_db_timezone_name(timezone.get_current_timezone()) 45 # This more than slightly obscure construction is to get the date truncation done in the 46 # correct time zone. However the AT TIME ZONE clause will cause the timezone to be naive 47 # So we need to make this aware again. The correct solution would be to return a date 48 # from date truncated datetime, but that would be backwards incompatible. 49 field_sql = "%s AT TIME ZONE '%s'" % (field_sql, tzname) 50 return "DATE_TRUNC('%s', %s) AT TIME ZONE 'UTC'" % (lookup_type, field_sql) 51 return "DATE_TRUNC('%s', %s)" % (lookup_type, field_sql) 36 52 37 53 def deferrable_sql(self): 38 54 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): 127 127 # single quotes are used because this is a string (and could otherwise 128 128 # cause a collision with a field name). 129 129 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] 130 149 131 150 def drop_foreignkey_sql(self): 132 151 return "" … … class DatabaseOperations(BaseDatabaseOperations): 178 197 179 198 return unicode(value) 180 199 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 186 200 def convert_values(self, value, field): 187 201 """SQLite returns floats when it should be returning decimals, 188 202 and gets dates and datetimes wrong. … … def _sqlite_extract(lookup_type, dt): 359 373 dt = util.typecast_timestamp(dt) 360 374 except (ValueError, TypeError): 361 375 return None 376 if settings.USE_TZ: 377 tz = timezone.get_current_timezone() 378 dt = dt.astimezone(tz) 362 379 if lookup_type == 'week_day': 363 380 return (dt.isoweekday() % 7) + 1 364 381 else: … … def _sqlite_date_trunc(lookup_type, dt): 369 386 dt = util.typecast_timestamp(dt) 370 387 except (ValueError, TypeError): 371 388 return None 389 if settings.USE_TZ: 390 tz = timezone.get_current_timezone() 391 dt = dt.astimezone(tz) 372 392 if lookup_type == 'year': 373 393 return "%i-01-01 00:00:00" % dt.year 374 394 elif lookup_type == 'month': -
django/utils/timezone.py
diff --git a/django/utils/timezone.py b/django/utils/timezone.py index 676f8f1..b3d1f6e 100644
a b This module uses pytz when it's available and fallbacks when it isn't. 6 6 from datetime import datetime, timedelta, tzinfo 7 7 from threading import local 8 8 import time as _time 9 import re 9 10 10 11 try: 11 12 import pytz … … def _get_timezone_name(timezone): 147 148 local_now = datetime.now(timezone) 148 149 return timezone.tzname(local_now) 149 150 151 def get_db_timezone_name(timezone): 152 # Realistically we would need to convert the timezone 153 # in some way or another in the database backend. 154 try: 155 return timezone.zone 156 except AttributeError: 157 local_now = datetime.now(timezone) 158 tzname = timezone.tzname(local_now) 159 # me is confused: why do I need to flip signs here. 160 if re.match('[\+-]\d\d00', tzname): 161 # Always learn something new: +0300 is -0300 as a POSIX time zone. 162 return ('-' if tzname[0] == '+' else '+') + tzname[1:3] 163 return tzname 164 150 165 # Timezone selection functions. 151 166 152 167 # These functions don't change os.environ['TZ'] and call time.tzset() -
tests/modeltests/timezones/tests.py
diff --git a/tests/modeltests/timezones/tests.py b/tests/modeltests/timezones/tests.py index a8d2c0c..d093659 100644
a b LegacyDatabaseTests = override_settings(USE_TZ=False)(LegacyDatabaseTests) 277 277 278 278 #@override_settings(USE_TZ=True) 279 279 class 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('Africa/Nairobi'): 288 self.assertEqual(Event.objects.dates('dt', 'day')[0].day, 5) 280 289 281 290 @requires_tz_support 282 291 @skipIf(sys.version_info < (2, 6), "this test requires Python >= 2.6") … … class NewDatabaseTests(BaseDateTimeTests): 427 436 # implementation is changed to perform the aggregation is local time. 428 437 Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT)) 429 438 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) 434 449 435 450 def test_query_aggregation(self): 436 451 # Only min and max make sense for datetimes. … … class NewDatabaseTests(BaseDateTimeTests): 469 484 # Same comment as in test_query_date_related_filters. 470 485 Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT)) 471 486 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) 484 510 485 511 def test_raw_sql(self): 486 512 # Regression test for #17755