Skip to content

File tree

58 files changed

+2719
-283
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2719
-283
lines changed

django/conf/global_settings.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,13 @@
3131

3232
# Local time zone for this installation. All choices can be found here:
3333
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name (although not all
34-
# systems may support all possibilities).
34+
# systems may support all possibilities). When USE_TZ is True, this is
35+
# interpreted as the default user time zone.
3536
TIME_ZONE = 'America/Chicago'
3637

38+
# If you set this to True, Django will use timezone-aware datetimes.
39+
USE_TZ = False
40+
3741
# Language code for this installation. All choices can be found here:
3842
# http://www.i18nguy.com/unicode/language-identifiers.html
3943
LANGUAGE_CODE = 'en-us'
@@ -119,7 +123,7 @@
119123
LANGUAGE_COOKIE_NAME = 'django_language'
120124

121125
# If you set this to True, Django will format dates, numbers and calendars
122-
# according to user current locale
126+
# according to user current locale.
123127
USE_L10N = False
124128

125129
# Not-necessarily-technical managers of the site. They get broken link
@@ -192,6 +196,7 @@
192196
'django.core.context_processors.i18n',
193197
'django.core.context_processors.media',
194198
'django.core.context_processors.static',
199+
'django.core.context_processors.tz',
195200
# 'django.core.context_processors.request',
196201
'django.contrib.messages.context_processors.messages',
197202
)

django/conf/project_template/project_name/settings.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,12 @@
4040
USE_I18N = True
4141

4242
# If you set this to False, Django will not format dates, numbers and
43-
# calendars according to the current locale
43+
# calendars according to the current locale.
4444
USE_L10N = True
4545

46+
# If you set this to False, Django will not use timezone-aware datetimes.
47+
USE_TZ = True
48+
4649
# Absolute filesystem path to the directory that will hold user-uploaded files.
4750
# Example: "/home/media/media.lawrence.com/media/"
4851
MEDIA_ROOT = ''

django/contrib/admin/util.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from django.utils.html import escape
88
from django.utils.safestring import mark_safe
99
from django.utils.text import capfirst
10+
from django.utils import timezone
1011
from django.utils.encoding import force_unicode, smart_unicode, smart_str
1112
from django.utils.translation import ungettext
1213
from django.core.urlresolvers import reverse
@@ -293,6 +294,8 @@ def display_for_field(value, field):
293294
return _boolean_icon(value)
294295
elif value is None:
295296
return EMPTY_CHANGELIST_VALUE
297+
elif isinstance(field, models.DateTimeField):
298+
return formats.localize(timezone.aslocaltime(value))
296299
elif isinstance(field, models.DateField) or isinstance(field, models.TimeField):
297300
return formats.localize(value)
298301
elif isinstance(field, models.DecimalField):

django/contrib/humanize/templatetags/humanize.py

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from django.utils.encoding import force_unicode
88
from django.utils.formats import number_format
99
from django.utils.translation import pgettext, ungettext, ugettext as _
10-
from django.utils.tzinfo import LocalTimezone
10+
from django.utils.timezone import is_aware, utc
1111

1212
register = template.Library()
1313

@@ -158,8 +158,8 @@ def naturalday(value, arg=None):
158158
except ValueError:
159159
# Date arguments out of range
160160
return value
161-
today = datetime.now(tzinfo).replace(microsecond=0, second=0, minute=0, hour=0)
162-
delta = value - today.date()
161+
today = datetime.now(tzinfo).date()
162+
delta = value - today
163163
if delta.days == 0:
164164
return _(u'today')
165165
elif delta.days == 1:
@@ -174,18 +174,10 @@ def naturaltime(value):
174174
For date and time values shows how many seconds, minutes or hours ago
175175
compared to current timestamp returns representing string.
176176
"""
177-
try:
178-
value = datetime(value.year, value.month, value.day, value.hour, value.minute, value.second)
179-
except AttributeError:
180-
return value
181-
except ValueError:
177+
if not isinstance(value, date): # datetime is a subclass of date
182178
return value
183179

184-
if getattr(value, 'tzinfo', None):
185-
now = datetime.now(LocalTimezone(value))
186-
else:
187-
now = datetime.now()
188-
now = now - timedelta(0, 0, now.microsecond)
180+
now = datetime.now(utc if is_aware(value) else None)
189181
if value < now:
190182
delta = now - value
191183
if delta.days != 0:

django/contrib/humanize/tests.py

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
from __future__ import with_statement
2-
from datetime import timedelta, date, datetime
2+
import datetime
33

44
from django.template import Template, Context, defaultfilters
55
from django.test import TestCase
66
from django.utils import translation, tzinfo
77
from django.utils.translation import ugettext as _
88
from django.utils.html import escape
9+
from django.utils.timezone import utc
910

1011

1112
class HumanizeTests(TestCase):
@@ -88,10 +89,10 @@ def test_apnumber(self):
8889
self.humanize_tester(test_list, result_list, 'apnumber')
8990

9091
def test_naturalday(self):
91-
today = date.today()
92-
yesterday = today - timedelta(days=1)
93-
tomorrow = today + timedelta(days=1)
94-
someday = today - timedelta(days=10)
92+
today = datetime.date.today()
93+
yesterday = today - datetime.timedelta(days=1)
94+
tomorrow = today + datetime.timedelta(days=1)
95+
someday = today - datetime.timedelta(days=10)
9596
notdate = u"I'm not a date value"
9697

9798
test_list = (today, yesterday, tomorrow, someday, notdate, None)
@@ -103,41 +104,46 @@ def test_naturalday(self):
103104
def test_naturalday_tz(self):
104105
from django.contrib.humanize.templatetags.humanize import naturalday
105106

106-
today = date.today()
107-
tz_one = tzinfo.FixedOffset(timedelta(hours=-12))
108-
tz_two = tzinfo.FixedOffset(timedelta(hours=12))
107+
today = datetime.date.today()
108+
tz_one = tzinfo.FixedOffset(datetime.timedelta(hours=-12))
109+
tz_two = tzinfo.FixedOffset(datetime.timedelta(hours=12))
109110

110111
# Can be today or yesterday
111-
date_one = datetime(today.year, today.month, today.day, tzinfo=tz_one)
112+
date_one = datetime.datetime(today.year, today.month, today.day, tzinfo=tz_one)
112113
naturalday_one = naturalday(date_one)
113114
# Can be today or tomorrow
114-
date_two = datetime(today.year, today.month, today.day, tzinfo=tz_two)
115+
date_two = datetime.datetime(today.year, today.month, today.day, tzinfo=tz_two)
115116
naturalday_two = naturalday(date_two)
116117

117118
# As 24h of difference they will never be the same
118119
self.assertNotEqual(naturalday_one, naturalday_two)
119120

120121
def test_naturaltime(self):
122+
class naive(datetime.tzinfo):
123+
def utcoffset(self, dt):
124+
return None
121125
# we're going to mock datetime.datetime, so use a fixed datetime
122-
now = datetime(2011, 8, 15)
126+
now = datetime.datetime(2011, 8, 15)
123127
test_list = [
124128
now,
125-
now - timedelta(seconds=1),
126-
now - timedelta(seconds=30),
127-
now - timedelta(minutes=1, seconds=30),
128-
now - timedelta(minutes=2),
129-
now - timedelta(hours=1, minutes=30, seconds=30),
130-
now - timedelta(hours=23, minutes=50, seconds=50),
131-
now - timedelta(days=1),
132-
now - timedelta(days=500),
133-
now + timedelta(seconds=1),
134-
now + timedelta(seconds=30),
135-
now + timedelta(minutes=1, seconds=30),
136-
now + timedelta(minutes=2),
137-
now + timedelta(hours=1, minutes=30, seconds=30),
138-
now + timedelta(hours=23, minutes=50, seconds=50),
139-
now + timedelta(days=1),
140-
now + timedelta(days=500),
129+
now - datetime.timedelta(seconds=1),
130+
now - datetime.timedelta(seconds=30),
131+
now - datetime.timedelta(minutes=1, seconds=30),
132+
now - datetime.timedelta(minutes=2),
133+
now - datetime.timedelta(hours=1, minutes=30, seconds=30),
134+
now - datetime.timedelta(hours=23, minutes=50, seconds=50),
135+
now - datetime.timedelta(days=1),
136+
now - datetime.timedelta(days=500),
137+
now + datetime.timedelta(seconds=1),
138+
now + datetime.timedelta(seconds=30),
139+
now + datetime.timedelta(minutes=1, seconds=30),
140+
now + datetime.timedelta(minutes=2),
141+
now + datetime.timedelta(hours=1, minutes=30, seconds=30),
142+
now + datetime.timedelta(hours=23, minutes=50, seconds=50),
143+
now + datetime.timedelta(days=1),
144+
now + datetime.timedelta(days=500),
145+
now.replace(tzinfo=naive()),
146+
now.replace(tzinfo=utc),
141147
]
142148
result_list = [
143149
'now',
@@ -157,14 +163,20 @@ def test_naturaltime(self):
157163
'23 hours from now',
158164
'1 day from now',
159165
'1 year, 4 months from now',
166+
'now',
167+
'now',
160168
]
161169

162170
# mock out datetime so these tests don't fail occasionally when the
163171
# test runs too slow
164-
class MockDateTime(datetime):
172+
class MockDateTime(datetime.datetime):
165173
@classmethod
166-
def now(self):
167-
return now
174+
def now(self, tz=None):
175+
if tz is None or tz.utcoffset(now) is None:
176+
return now
177+
else:
178+
# equals now.replace(tzinfo=utc)
179+
return now.replace(tzinfo=tz) + tz.utcoffset(now)
168180

169181
# naturaltime also calls timesince/timeuntil
170182
from django.contrib.humanize.templatetags import humanize

django/contrib/syndication/views.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from django.utils import feedgenerator, tzinfo
77
from django.utils.encoding import force_unicode, iri_to_uri, smart_unicode
88
from django.utils.html import escape
9+
from django.utils.timezone import is_naive
910

1011
def add_domain(domain, url, secure=False):
1112
if not (url.startswith('http://')
@@ -164,7 +165,7 @@ def get_feed(self, obj, request):
164165
author_email = author_link = None
165166

166167
pubdate = self.__get_dynamic_attr('item_pubdate', item)
167-
if pubdate and not pubdate.tzinfo:
168+
if pubdate and is_naive(pubdate):
168169
ltz = tzinfo.LocalTimezone(pubdate)
169170
pubdate = pubdate.replace(tzinfo=ltz)
170171

django/core/context_processors.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ def i18n(request):
4848

4949
return context_extras
5050

51+
def tz(request):
52+
from django.utils import timezone
53+
54+
return {'TIME_ZONE': timezone.get_current_timezone_name()}
55+
5156
def static(request):
5257
"""
5358
Adds static-related context variables to the context.

django/core/serializers/json.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
from django.core.serializers.python import Serializer as PythonSerializer
1010
from django.core.serializers.python import Deserializer as PythonDeserializer
11-
from django.utils import datetime_safe
1211
from django.utils import simplejson
12+
from django.utils.timezone import is_aware
1313

1414
class Serializer(PythonSerializer):
1515
"""
@@ -39,19 +39,24 @@ class DjangoJSONEncoder(simplejson.JSONEncoder):
3939
"""
4040
JSONEncoder subclass that knows how to encode date/time and decimal types.
4141
"""
42-
43-
DATE_FORMAT = "%Y-%m-%d"
44-
TIME_FORMAT = "%H:%M:%S"
45-
4642
def default(self, o):
43+
# See "Date Time String Format" in the ECMA-262 specification.
4744
if isinstance(o, datetime.datetime):
48-
d = datetime_safe.new_datetime(o)
49-
return d.strftime("%s %s" % (self.DATE_FORMAT, self.TIME_FORMAT))
45+
r = o.isoformat()
46+
if o.microsecond:
47+
r = r[:23] + r[26:]
48+
if r.endswith('+00:00'):
49+
r = r[:-6] + 'Z'
50+
return r
5051
elif isinstance(o, datetime.date):
51-
d = datetime_safe.new_date(o)
52-
return d.strftime(self.DATE_FORMAT)
52+
return o.isoformat()
5353
elif isinstance(o, datetime.time):
54-
return o.strftime(self.TIME_FORMAT)
54+
if is_aware(o):
55+
raise ValueError("JSON can't represent timezone-aware times.")
56+
r = o.isoformat()
57+
if o.microsecond:
58+
r = r[:12]
59+
return r
5560
elif isinstance(o, decimal.Decimal):
5661
return str(o)
5762
else:

django/db/backends/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from django.db.backends import util
1111
from django.db.transaction import TransactionManagementError
1212
from django.utils.importlib import import_module
13+
from django.utils.timezone import is_aware
1314

1415

1516
class BaseDatabaseWrapper(local):
@@ -743,6 +744,8 @@ def value_to_db_time(self, value):
743744
"""
744745
if value is None:
745746
return None
747+
if is_aware(value):
748+
raise ValueError("Django does not support timezone-aware times.")
746749
return unicode(value)
747750

748751
def value_to_db_decimal(self, value, max_digits, decimal_places):

django/db/backends/mysql/base.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from django.db.backends.mysql.introspection import DatabaseIntrospection
3434
from django.db.backends.mysql.validation import DatabaseValidation
3535
from django.utils.safestring import SafeString, SafeUnicode
36+
from django.utils.timezone import is_aware, is_naive, utc
3637

3738
# Raise exceptions for database warnings if DEBUG is on
3839
from django.conf import settings
@@ -43,16 +44,29 @@
4344
DatabaseError = Database.DatabaseError
4445
IntegrityError = Database.IntegrityError
4546

47+
# It's impossible to import datetime_or_None directly from MySQLdb.times
48+
datetime_or_None = conversions[FIELD_TYPE.DATETIME]
49+
50+
def datetime_or_None_with_timezone_support(value):
51+
dt = datetime_or_None(value)
52+
# Confirm that dt is naive before overwriting its tzinfo.
53+
if dt is not None and settings.USE_TZ and is_naive(dt):
54+
dt = dt.replace(tzinfo=utc)
55+
return dt
56+
4657
# MySQLdb-1.2.1 returns TIME columns as timedelta -- they are more like
4758
# timedelta in terms of actual behavior as they are signed and include days --
4859
# and Django expects time, so we still need to override that. We also need to
4960
# add special handling for SafeUnicode and SafeString as MySQLdb's type
5061
# checking is too tight to catch those (see Django ticket #6052).
62+
# Finally, MySQLdb always returns naive datetime objects. However, when
63+
# timezone support is active, Django expects timezone-aware datetime objects.
5164
django_conversions = conversions.copy()
5265
django_conversions.update({
5366
FIELD_TYPE.TIME: util.typecast_time,
5467
FIELD_TYPE.DECIMAL: util.typecast_decimal,
5568
FIELD_TYPE.NEWDECIMAL: util.typecast_decimal,
69+
FIELD_TYPE.DATETIME: datetime_or_None_with_timezone_support,
5670
})
5771

5872
# This should match the numerical portion of the version numbers (we can treat
@@ -238,8 +252,11 @@ def value_to_db_datetime(self, value):
238252
return None
239253

240254
# MySQL doesn't support tz-aware datetimes
241-
if value.tzinfo is not None:
242-
raise ValueError("MySQL backend does not support timezone-aware datetimes.")
255+
if is_aware(value):
256+
if settings.USE_TZ:
257+
value = value.astimezone(utc).replace(tzinfo=None)
258+
else:
259+
raise ValueError("MySQL backend does not support timezone-aware datetimes when USE_TZ is False.")
243260

244261
# MySQL doesn't support microseconds
245262
return unicode(value.replace(microsecond=0))
@@ -248,9 +265,9 @@ def value_to_db_time(self, value):
248265
if value is None:
249266
return None
250267

251-
# MySQL doesn't support tz-aware datetimes
252-
if value.tzinfo is not None:
253-
raise ValueError("MySQL backend does not support timezone-aware datetimes.")
268+
# MySQL doesn't support tz-aware times
269+
if is_aware(value):
270+
raise ValueError("MySQL backend does not support timezone-aware times.")
254271

255272
# MySQL doesn't support microseconds
256273
return unicode(value.replace(microsecond=0))

0 commit comments

Comments
 (0)