summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorAymeric Augustin <aymeric.augustin@m4x.org>2011-11-18 13:01:06 +0000
committerAymeric Augustin <aymeric.augustin@m4x.org>2011-11-18 13:01:06 +0000
commit9b1cb755a28f020e27d4268c214b25315d4de42e (patch)
tree2ff0827176f0eb49defa4ce7ce10164f2fc26e86 /tests
parent01f70349c9ef23d6751437dcd57d2efc193b2661 (diff)
Added support for time zones. Thanks Luke Plant for the review. Fixed #2626.
For more information on this project, see this thread: http://groups.google.com/group/django-developers/browse_thread/thread/cf0423bbb85b1bbf git-svn-id: http://code.djangoproject.com/svn/django/trunk@17106 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests')
-rw-r--r--tests/modeltests/fixtures/tests.py22
-rw-r--r--tests/modeltests/serializers/tests.py6
-rw-r--r--tests/modeltests/timezones/__init__.py0
-rw-r--r--tests/modeltests/timezones/admin.py15
-rw-r--r--tests/modeltests/timezones/fixtures/users.xml17
-rw-r--r--tests/modeltests/timezones/forms.py13
-rw-r--r--tests/modeltests/timezones/models.py8
-rw-r--r--tests/modeltests/timezones/tests.py871
-rw-r--r--tests/modeltests/timezones/urls.py10
-rw-r--r--tests/modeltests/validation/test_error_messages.py43
-rw-r--r--tests/regressiontests/cache/tests.py18
-rw-r--r--tests/regressiontests/datatypes/tests.py4
-rw-r--r--tests/regressiontests/defaultfilters/tests.py2
-rw-r--r--tests/regressiontests/utils/dateformat.py2
-rw-r--r--tests/regressiontests/utils/tests.py1
-rw-r--r--tests/regressiontests/utils/timesince.py9
-rw-r--r--tests/regressiontests/utils/timezone.py18
-rw-r--r--tests/regressiontests/utils/tzinfo.py17
18 files changed, 1047 insertions, 29 deletions
diff --git a/tests/modeltests/fixtures/tests.py b/tests/modeltests/fixtures/tests.py
index 2dca7ced9c..e7daf2d861 100644
--- a/tests/modeltests/fixtures/tests.py
+++ b/tests/modeltests/fixtures/tests.py
@@ -56,25 +56,25 @@ class FixtureLoadingTests(TestCase):
])
# Dump the current contents of the database as a JSON fixture
- self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]')
+ self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16T13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16T11:00:00"}}]')
# Try just dumping the contents of fixtures.Category
self._dumpdata_assert(['fixtures.Category'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]')
# ...and just fixtures.Article
- self._dumpdata_assert(['fixtures.Article'], '[{"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]')
+ self._dumpdata_assert(['fixtures.Article'], '[{"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16T13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16T11:00:00"}}]')
# ...and both
- self._dumpdata_assert(['fixtures.Category', 'fixtures.Article'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]')
+ self._dumpdata_assert(['fixtures.Category', 'fixtures.Article'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16T13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16T11:00:00"}}]')
# Specify a specific model twice
- self._dumpdata_assert(['fixtures.Article', 'fixtures.Article'], '[{"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]')
+ self._dumpdata_assert(['fixtures.Article', 'fixtures.Article'], '[{"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16T13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16T11:00:00"}}]')
# Specify a dump that specifies Article both explicitly and implicitly
- self._dumpdata_assert(['fixtures.Article', 'fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]')
+ self._dumpdata_assert(['fixtures.Article', 'fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16T13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16T11:00:00"}}]')
# Same again, but specify in the reverse order
- self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]')
+ self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16T13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16T11:00:00"}}]')
# Specify one model from one application, and an entire other application.
self._dumpdata_assert(['fixtures.Category', 'sites'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}]')
@@ -153,11 +153,11 @@ class FixtureLoadingTests(TestCase):
self._dumpdata_assert(['fixtures.book'], '[{"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}]', natural_keys=True)
# Dump the current contents of the database as a JSON fixture
- self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 5, "model": "fixtures.article", "fields": {"headline": "XML identified as leading cause of cancer", "pub_date": "2006-06-16 16:00:00"}}, {"pk": 4, "model": "fixtures.article", "fields": {"headline": "Django conquers world!", "pub_date": "2006-06-16 15:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16 14:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker on TV is great!", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "legal", "tagged_id": 3}}, {"pk": 3, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "django", "tagged_id": 4}}, {"pk": 4, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "world domination", "tagged_id": 4}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Artist formerly known as \\"Prince\\""}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}, {"pk": 1, "model": "fixtures.visa", "fields": {"person": ["Django Reinhardt"], "permissions": [["add_user", "auth", "user"], ["change_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 2, "model": "fixtures.visa", "fields": {"person": ["Stephane Grappelli"], "permissions": [["add_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 3, "model": "fixtures.visa", "fields": {"person": ["Artist formerly known as \\"Prince\\""], "permissions": [["change_user", "auth", "user"]]}}, {"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}]', natural_keys=True)
+ self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 5, "model": "fixtures.article", "fields": {"headline": "XML identified as leading cause of cancer", "pub_date": "2006-06-16T16:00:00"}}, {"pk": 4, "model": "fixtures.article", "fields": {"headline": "Django conquers world!", "pub_date": "2006-06-16T15:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16T14:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker on TV is great!", "pub_date": "2006-06-16T11:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16T11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "legal", "tagged_id": 3}}, {"pk": 3, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "django", "tagged_id": 4}}, {"pk": 4, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "world domination", "tagged_id": 4}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Artist formerly known as \\"Prince\\""}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}, {"pk": 1, "model": "fixtures.visa", "fields": {"person": ["Django Reinhardt"], "permissions": [["add_user", "auth", "user"], ["change_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 2, "model": "fixtures.visa", "fields": {"person": ["Stephane Grappelli"], "permissions": [["add_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 3, "model": "fixtures.visa", "fields": {"person": ["Artist formerly known as \\"Prince\\""], "permissions": [["change_user", "auth", "user"]]}}, {"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}]', natural_keys=True)
# Dump the current contents of the database as an XML fixture
self._dumpdata_assert(['fixtures'], """<?xml version="1.0" encoding="utf-8"?>
-<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="5" model="fixtures.article"><field type="CharField" name="headline">XML identified as leading cause of cancer</field><field type="DateTimeField" name="pub_date">2006-06-16 16:00:00</field></object><object pk="4" model="fixtures.article"><field type="CharField" name="headline">Django conquers world!</field><field type="DateTimeField" name="pub_date">2006-06-16 15:00:00</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Copyright is fine the way it is</field><field type="DateTimeField" name="pub_date">2006-06-16 14:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker on TV is great!</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">legal</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="3" model="fixtures.tag"><field type="CharField" name="name">django</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="4" model="fixtures.tag"><field type="CharField" name="name">world domination</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Artist formerly known as "Prince"</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object><object pk="1" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Django Reinhardt</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="2" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Stephane Grappelli</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="3" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Artist formerly known as "Prince"</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="1" model="fixtures.book"><field type="CharField" name="name">Music for all ages</field><field to="fixtures.person" name="authors" rel="ManyToManyRel"><object><natural>Artist formerly known as "Prince"</natural></object><object><natural>Django Reinhardt</natural></object></field></object></django-objects>""", format='xml', natural_keys=True)
+<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="5" model="fixtures.article"><field type="CharField" name="headline">XML identified as leading cause of cancer</field><field type="DateTimeField" name="pub_date">2006-06-16T16:00:00</field></object><object pk="4" model="fixtures.article"><field type="CharField" name="headline">Django conquers world!</field><field type="DateTimeField" name="pub_date">2006-06-16T15:00:00</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Copyright is fine the way it is</field><field type="DateTimeField" name="pub_date">2006-06-16T14:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker on TV is great!</field><field type="DateTimeField" name="pub_date">2006-06-16T11:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16T11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">legal</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="3" model="fixtures.tag"><field type="CharField" name="name">django</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="4" model="fixtures.tag"><field type="CharField" name="name">world domination</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Artist formerly known as "Prince"</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object><object pk="1" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Django Reinhardt</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="2" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Stephane Grappelli</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="3" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Artist formerly known as "Prince"</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="1" model="fixtures.book"><field type="CharField" name="name">Music for all ages</field><field to="fixtures.person" name="authors" rel="ManyToManyRel"><object><natural>Artist formerly known as "Prince"</natural></object><object><natural>Django Reinhardt</natural></object></field></object></django-objects>""", format='xml', natural_keys=True)
def test_dumpdata_with_excludes(self):
# Load fixture1 which has a site, two articles, and a category
@@ -305,11 +305,11 @@ class FixtureLoadingTests(TestCase):
])
# Dump the current contents of the database as a JSON fixture
- self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "law", "tagged_id": 3}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Prince"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}]', natural_keys=True)
+ self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16T13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16T11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "law", "tagged_id": 3}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Prince"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}]', natural_keys=True)
# Dump the current contents of the database as an XML fixture
self._dumpdata_assert(['fixtures'], """<?xml version="1.0" encoding="utf-8"?>
-<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Time to reform copyright</field><field type="DateTimeField" name="pub_date">2006-06-16 13:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker has no place on ESPN</field><field type="DateTimeField" name="pub_date">2006-06-16 12:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">law</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Prince</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object></django-objects>""", format='xml', natural_keys=True)
+<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Time to reform copyright</field><field type="DateTimeField" name="pub_date">2006-06-16T13:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker has no place on ESPN</field><field type="DateTimeField" name="pub_date">2006-06-16T12:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16T11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">law</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Prince</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object></django-objects>""", format='xml', natural_keys=True)
class FixtureTransactionTests(TransactionTestCase):
def _dumpdata_assert(self, args, output, format='json'):
@@ -344,7 +344,7 @@ class FixtureTransactionTests(TransactionTestCase):
])
# Dump the current contents of the database as a JSON fixture
- self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]')
+ self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16T13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16T11:00:00"}}]')
# Load fixture 4 (compressed), using format discovery
management.call_command('loaddata', 'fixture4', verbosity=0, commit=False)
diff --git a/tests/modeltests/serializers/tests.py b/tests/modeltests/serializers/tests.py
index 99c065aeaf..721ca09282 100644
--- a/tests/modeltests/serializers/tests.py
+++ b/tests/modeltests/serializers/tests.py
@@ -230,7 +230,7 @@ class SerializersTestBase(object):
serial_str = serializers.serialize(self.serializer_name, [a])
date_values = self._get_field_values(serial_str, "pub_date")
- self.assertEqual(date_values[0], "0001-02-03 04:05:06")
+ self.assertEqual(date_values[0].replace('T', ' '), "0001-02-03 04:05:06")
def test_pkless_serialized_strings(self):
"""
@@ -323,7 +323,7 @@ class XmlSerializerTransactionTestCase(SerializersTransactionTestBase, Transacti
<object pk="1" model="serializers.article">
<field to="serializers.author" name="author" rel="ManyToOneRel">1</field>
<field type="CharField" name="headline">Forward references pose no problem</field>
- <field type="DateTimeField" name="pub_date">2006-06-16 15:00:00</field>
+ <field type="DateTimeField" name="pub_date">2006-06-16T15:00:00</field>
<field to="serializers.category" name="categories" rel="ManyToManyRel">
<object pk="1"></object>
</field>
@@ -374,7 +374,7 @@ class JsonSerializerTransactionTestCase(SerializersTransactionTestBase, Transact
"model": "serializers.article",
"fields": {
"headline": "Forward references pose no problem",
- "pub_date": "2006-06-16 15:00:00",
+ "pub_date": "2006-06-16T15:00:00",
"categories": [1],
"author": 1
}
diff --git a/tests/modeltests/timezones/__init__.py b/tests/modeltests/timezones/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/modeltests/timezones/__init__.py
diff --git a/tests/modeltests/timezones/admin.py b/tests/modeltests/timezones/admin.py
new file mode 100644
index 0000000000..4c199813e2
--- /dev/null
+++ b/tests/modeltests/timezones/admin.py
@@ -0,0 +1,15 @@
+from __future__ import absolute_import
+
+from django.contrib import admin
+
+from .models import Event, Timestamp
+
+class EventAdmin(admin.ModelAdmin):
+ list_display = ('dt',)
+
+admin.site.register(Event, EventAdmin)
+
+class TimestampAdmin(admin.ModelAdmin):
+ readonly_fields = ('created', 'updated')
+
+admin.site.register(Timestamp, TimestampAdmin)
diff --git a/tests/modeltests/timezones/fixtures/users.xml b/tests/modeltests/timezones/fixtures/users.xml
new file mode 100644
index 0000000000..6cf441f01e
--- /dev/null
+++ b/tests/modeltests/timezones/fixtures/users.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0">
+ <object pk="100" model="auth.user">
+ <field type="CharField" name="username">super</field>
+ <field type="CharField" name="first_name">Super</field>
+ <field type="CharField" name="last_name">User</field>
+ <field type="CharField" name="email">super@example.com</field>
+ <field type="CharField" name="password">sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158</field>
+ <field type="BooleanField" name="is_staff">True</field>
+ <field type="BooleanField" name="is_active">True</field>
+ <field type="BooleanField" name="is_superuser">True</field>
+ <field type="DateTimeField" name="last_login">2007-05-30 13:20:10</field>
+ <field type="DateTimeField" name="date_joined">2007-05-30 13:20:10</field>
+ <field to="auth.group" name="groups" rel="ManyToManyRel"></field>
+ <field to="auth.permission" name="user_permissions" rel="ManyToManyRel"></field>
+ </object>
+</django-objects> \ No newline at end of file
diff --git a/tests/modeltests/timezones/forms.py b/tests/modeltests/timezones/forms.py
new file mode 100644
index 0000000000..3c9c31167e
--- /dev/null
+++ b/tests/modeltests/timezones/forms.py
@@ -0,0 +1,13 @@
+from django import forms
+
+from .models import Event
+
+class EventForm(forms.Form):
+ dt = forms.DateTimeField()
+
+class EventSplitForm(forms.Form):
+ dt = forms.SplitDateTimeField()
+
+class EventModelForm(forms.ModelForm):
+ class Meta:
+ model = Event
diff --git a/tests/modeltests/timezones/models.py b/tests/modeltests/timezones/models.py
new file mode 100644
index 0000000000..bd488d1132
--- /dev/null
+++ b/tests/modeltests/timezones/models.py
@@ -0,0 +1,8 @@
+from django.db import models
+
+class Event(models.Model):
+ dt = models.DateTimeField()
+
+class Timestamp(models.Model):
+ created = models.DateTimeField(auto_now_add=True)
+ updated = models.DateTimeField(auto_now=True)
diff --git a/tests/modeltests/timezones/tests.py b/tests/modeltests/timezones/tests.py
new file mode 100644
index 0000000000..e33d4c61f8
--- /dev/null
+++ b/tests/modeltests/timezones/tests.py
@@ -0,0 +1,871 @@
+from __future__ import with_statement
+
+import datetime
+import os
+import time
+
+try:
+ import pytz
+except ImportError:
+ pytz = None
+
+from django.conf import settings
+from django.core import serializers
+from django.core.urlresolvers import reverse
+from django.db import connection
+from django.db.models import Min, Max
+from django.http import HttpRequest
+from django.template import Context, RequestContext, Template, TemplateSyntaxError
+from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
+from django.test.utils import override_settings
+from django.utils import timezone
+from django.utils.tzinfo import FixedOffset
+from django.utils.unittest import skipIf
+
+from .forms import EventForm, EventSplitForm, EventModelForm
+from .models import Event, Timestamp
+
+
+# These tests use the EAT (Eastern Africa Time) and ICT (Indochina Time)
+# who don't have Daylight Saving Time, so we can represent them easily
+# with FixedOffset, and use them directly as tzinfo in the constructors.
+
+# settings.TIME_ZONE is forced to EAT. Most tests use a variant of
+# datetime.datetime(2011, 9, 1, 13, 20, 30), which translates to
+# 10:20:30 in UTC and 17:20:30 in ICT.
+
+UTC = timezone.utc
+EAT = FixedOffset(180) # Africa/Nairobi
+ICT = FixedOffset(420) # Asia/Bangkok
+
+TZ_SUPPORT = hasattr(time, 'tzset')
+
+
+class BaseDateTimeTests(TestCase):
+
+ @classmethod
+ def setUpClass(self):
+ self._old_time_zone = settings.TIME_ZONE
+ settings.TIME_ZONE = connection.settings_dict['TIME_ZONE'] = 'Africa/Nairobi'
+ timezone._localtime = None
+ if TZ_SUPPORT:
+ self._old_tz = os.environ.get('TZ')
+ os.environ['TZ'] = 'Africa/Nairobi'
+ time.tzset()
+ # Create a new cursor, for test cases that change the value of USE_TZ.
+ connection.close()
+
+ @classmethod
+ def tearDownClass(self):
+ settings.TIME_ZONE = connection.settings_dict['TIME_ZONE'] = self._old_time_zone
+ timezone._localtime = None
+ if TZ_SUPPORT:
+ if self._old_tz is None:
+ del os.environ['TZ']
+ else:
+ os.environ['TZ'] = self._old_tz
+ time.tzset()
+
+
+#@override_settings(USE_TZ=False)
+class LegacyDatabaseTests(BaseDateTimeTests):
+
+ def test_naive_datetime(self):
+ dt = datetime.datetime(2011, 9, 1, 13, 20, 30)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ self.assertEqual(event.dt, dt)
+
+ @skipUnlessDBFeature('supports_microsecond_precision')
+ def test_naive_datetime_with_microsecond(self):
+ dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ self.assertEqual(event.dt, dt)
+
+ @skipIfDBFeature('supports_microsecond_precision')
+ def test_naive_datetime_with_microsecond_unsupported(self):
+ dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ # microseconds are lost during a round-trip in the database
+ self.assertEqual(event.dt, dt.replace(microsecond=0))
+
+ @skipUnlessDBFeature('supports_timezones')
+ def test_aware_datetime_in_local_timezone(self):
+ dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ self.assertIsNone(event.dt.tzinfo)
+ # interpret the naive datetime in local time to get the correct value
+ self.assertEqual(event.dt.replace(tzinfo=EAT), dt)
+
+ @skipUnlessDBFeature('supports_timezones')
+ @skipUnlessDBFeature('supports_microsecond_precision')
+ def test_aware_datetime_in_local_timezone_with_microsecond(self):
+ dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060, tzinfo=EAT)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ self.assertIsNone(event.dt.tzinfo)
+ # interpret the naive datetime in local time to get the correct value
+ self.assertEqual(event.dt.replace(tzinfo=EAT), dt)
+
+ # This combination actually never happens.
+ @skipUnlessDBFeature('supports_timezones')
+ @skipIfDBFeature('supports_microsecond_precision')
+ def test_aware_datetime_in_local_timezone_with_microsecond_unsupported(self):
+ dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060, tzinfo=EAT)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ self.assertIsNone(event.dt.tzinfo)
+ # interpret the naive datetime in local time to get the correct value
+ # microseconds are lost during a round-trip in the database
+ self.assertEqual(event.dt.replace(tzinfo=EAT), dt.replace(microsecond=0))
+
+ @skipUnlessDBFeature('supports_timezones')
+ @skipIfDBFeature('needs_datetime_string_cast')
+ def test_aware_datetime_in_utc(self):
+ dt = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ self.assertIsNone(event.dt.tzinfo)
+ # interpret the naive datetime in local time to get the correct value
+ self.assertEqual(event.dt.replace(tzinfo=EAT), dt)
+
+ # This combination is no longer possible since timezone support
+ # was removed from the SQLite backend -- it didn't work.
+ @skipUnlessDBFeature('supports_timezones')
+ @skipUnlessDBFeature('needs_datetime_string_cast')
+ def test_aware_datetime_in_utc_unsupported(self):
+ dt = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ self.assertIsNone(event.dt.tzinfo)
+ # django.db.backend.utils.typecast_dt will just drop the
+ # timezone, so a round-trip in the database alters the data (!)
+ # interpret the naive datetime in local time and you get a wrong value
+ self.assertNotEqual(event.dt.replace(tzinfo=EAT), dt)
+ # interpret the naive datetime in original time to get the correct value
+ self.assertEqual(event.dt.replace(tzinfo=UTC), dt)
+
+ @skipUnlessDBFeature('supports_timezones')
+ @skipIfDBFeature('needs_datetime_string_cast')
+ def test_aware_datetime_in_other_timezone(self):
+ dt = datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ self.assertIsNone(event.dt.tzinfo)
+ # interpret the naive datetime in local time to get the correct value
+ self.assertEqual(event.dt.replace(tzinfo=EAT), dt)
+
+ # This combination is no longer possible since timezone support
+ # was removed from the SQLite backend -- it didn't work.
+ @skipUnlessDBFeature('supports_timezones')
+ @skipUnlessDBFeature('needs_datetime_string_cast')
+ def test_aware_datetime_in_other_timezone_unsupported(self):
+ dt = datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ self.assertIsNone(event.dt.tzinfo)
+ # django.db.backend.utils.typecast_dt will just drop the
+ # timezone, so a round-trip in the database alters the data (!)
+ # interpret the naive datetime in local time and you get a wrong value
+ self.assertNotEqual(event.dt.replace(tzinfo=EAT), dt)
+ # interpret the naive datetime in original time to get the correct value
+ self.assertEqual(event.dt.replace(tzinfo=ICT), dt)
+
+ @skipIfDBFeature('supports_timezones')
+ def test_aware_datetime_unspported(self):
+ dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
+ with self.assertRaises(ValueError):
+ Event.objects.create(dt=dt)
+
+ def test_auto_now_and_auto_now_add(self):
+ now = datetime.datetime.now()
+ past = now - datetime.timedelta(seconds=2)
+ future = now + datetime.timedelta(seconds=2)
+ Timestamp.objects.create()
+ ts = Timestamp.objects.get()
+ self.assertLess(past, ts.created)
+ self.assertLess(past, ts.updated)
+ self.assertGreater(future, ts.updated)
+ self.assertGreater(future, ts.updated)
+
+ def test_query_filter(self):
+ dt1 = datetime.datetime(2011, 9, 1, 12, 20, 30)
+ dt2 = datetime.datetime(2011, 9, 1, 14, 20, 30)
+ Event.objects.create(dt=dt1)
+ Event.objects.create(dt=dt2)
+ self.assertEqual(Event.objects.filter(dt__gte=dt1).count(), 2)
+ self.assertEqual(Event.objects.filter(dt__gt=dt1).count(), 1)
+ self.assertEqual(Event.objects.filter(dt__gte=dt2).count(), 1)
+ self.assertEqual(Event.objects.filter(dt__gt=dt2).count(), 0)
+
+ def test_query_date_related_filters(self):
+ Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0))
+ Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0))
+ self.assertEqual(Event.objects.filter(dt__year=2011).count(), 2)
+ self.assertEqual(Event.objects.filter(dt__month=1).count(), 2)
+ self.assertEqual(Event.objects.filter(dt__day=1).count(), 2)
+ self.assertEqual(Event.objects.filter(dt__week_day=7).count(), 2)
+
+ def test_query_aggregation(self):
+ # Only min and max make sense for datetimes.
+ Event.objects.create(dt=datetime.datetime(2011, 9, 1, 23, 20, 20))
+ Event.objects.create(dt=datetime.datetime(2011, 9, 1, 13, 20, 30))
+ Event.objects.create(dt=datetime.datetime(2011, 9, 1, 3, 20, 40))
+ result = Event.objects.all().aggregate(Min('dt'), Max('dt'))
+ self.assertEqual(result, {
+ 'dt__min': datetime.datetime(2011, 9, 1, 3, 20, 40),
+ 'dt__max': datetime.datetime(2011, 9, 1, 23, 20, 20),
+ })
+
+ def test_query_dates(self):
+ Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0))
+ Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0))
+ self.assertQuerysetEqual(Event.objects.dates('dt', 'year'),
+ [datetime.datetime(2011, 1, 1)], transform=lambda d: d)
+ self.assertQuerysetEqual(Event.objects.dates('dt', 'month'),
+ [datetime.datetime(2011, 1, 1)], transform=lambda d: d)
+ self.assertQuerysetEqual(Event.objects.dates('dt', 'day'),
+ [datetime.datetime(2011, 1, 1)], transform=lambda d: d)
+
+LegacyDatabaseTests = override_settings(USE_TZ=False)(LegacyDatabaseTests)
+
+
+#@override_settings(USE_TZ=True)
+class NewDatabaseTests(BaseDateTimeTests):
+
+ def test_naive_datetime(self):
+ dt = datetime.datetime(2011, 9, 1, 13, 20, 30)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ # naive datetimes are interpreted in local time
+ self.assertEqual(event.dt, dt.replace(tzinfo=EAT))
+
+ @skipUnlessDBFeature('supports_microsecond_precision')
+ def test_naive_datetime_with_microsecond(self):
+ dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ # naive datetimes are interpreted in local time
+ self.assertEqual(event.dt, dt.replace(tzinfo=EAT))
+
+ @skipIfDBFeature('supports_microsecond_precision')
+ def test_naive_datetime_with_microsecond_unsupported(self):
+ dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ # microseconds are lost during a round-trip in the database
+ # naive datetimes are interpreted in local time
+ self.assertEqual(event.dt, dt.replace(microsecond=0, tzinfo=EAT))
+
+ def test_aware_datetime_in_local_timezone(self):
+ dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ self.assertEqual(event.dt, dt)
+
+ @skipUnlessDBFeature('supports_microsecond_precision')
+ def test_aware_datetime_in_local_timezone_with_microsecond(self):
+ dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060, tzinfo=EAT)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ self.assertEqual(event.dt, dt)
+
+ @skipIfDBFeature('supports_microsecond_precision')
+ def test_aware_datetime_in_local_timezone_with_microsecond_unsupported(self):
+ dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060, tzinfo=EAT)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ # microseconds are lost during a round-trip in the database
+ self.assertEqual(event.dt, dt.replace(microsecond=0))
+
+ def test_aware_datetime_in_utc(self):
+ dt = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ self.assertEqual(event.dt, dt)
+
+ def test_aware_datetime_in_other_timezone(self):
+ dt = datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT)
+ Event.objects.create(dt=dt)
+ event = Event.objects.get()
+ self.assertEqual(event.dt, dt)
+
+ def test_auto_now_and_auto_now_add(self):
+ now = datetime.datetime.utcnow().replace(tzinfo=UTC)
+ past = now - datetime.timedelta(seconds=2)
+ future = now + datetime.timedelta(seconds=2)
+ Timestamp.objects.create()
+ ts = Timestamp.objects.get()
+ self.assertLess(past, ts.created)
+ self.assertLess(past, ts.updated)
+ self.assertGreater(future, ts.updated)
+ self.assertGreater(future, ts.updated)
+
+ def test_query_filter(self):
+ dt1 = datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=EAT)
+ dt2 = datetime.datetime(2011, 9, 1, 14, 20, 30, tzinfo=EAT)
+ Event.objects.create(dt=dt1)
+ Event.objects.create(dt=dt2)
+ self.assertEqual(Event.objects.filter(dt__gte=dt1).count(), 2)
+ self.assertEqual(Event.objects.filter(dt__gt=dt1).count(), 1)
+ self.assertEqual(Event.objects.filter(dt__gte=dt2).count(), 1)
+ self.assertEqual(Event.objects.filter(dt__gt=dt2).count(), 0)
+
+ @skipIf(pytz is None, "this test requires pytz")
+ def test_query_filter_with_pytz_timezones(self):
+ tz = pytz.timezone('Europe/Paris')
+ dt = datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=tz)
+ Event.objects.create(dt=dt)
+ next = dt + datetime.timedelta(seconds=3)
+ prev = dt - datetime.timedelta(seconds=3)
+ self.assertEqual(Event.objects.filter(dt__exact=dt).count(), 1)
+ self.assertEqual(Event.objects.filter(dt__exact=next).count(), 0)
+ self.assertEqual(Event.objects.filter(dt__in=(prev, next)).count(), 0)
+ self.assertEqual(Event.objects.filter(dt__in=(prev, dt, next)).count(), 1)
+ self.assertEqual(Event.objects.filter(dt__range=(prev, next)).count(), 1)
+
+ def test_query_date_related_filters(self):
+ # These two dates fall in the same day in EAT, but in different days,
+ # years and months in UTC, and aggregation is performed in UTC when
+ # time zone support is enabled. This test could be changed if the
+ # implementation is changed to perform the aggregation is local time.
+ Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT))
+ Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT))
+ self.assertEqual(Event.objects.filter(dt__year=2011).count(), 1)
+ self.assertEqual(Event.objects.filter(dt__month=1).count(), 1)
+ self.assertEqual(Event.objects.filter(dt__day=1).count(), 1)
+ self.assertEqual(Event.objects.filter(dt__week_day=7).count(), 1)
+
+ def test_query_aggregation(self):
+ # Only min and max make sense for datetimes.
+ Event.objects.create(dt=datetime.datetime(2011, 9, 1, 23, 20, 20, tzinfo=EAT))
+ Event.objects.create(dt=datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT))
+ Event.objects.create(dt=datetime.datetime(2011, 9, 1, 3, 20, 40, tzinfo=EAT))
+ result = Event.objects.all().aggregate(Min('dt'), Max('dt'))
+ self.assertEqual(result, {
+ 'dt__min': datetime.datetime(2011, 9, 1, 3, 20, 40, tzinfo=EAT),
+ 'dt__max': datetime.datetime(2011, 9, 1, 23, 20, 20, tzinfo=EAT),
+ })
+
+ def test_query_dates(self):
+ # Same comment as in test_query_date_related_filters.
+ Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT))
+ Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT))
+ self.assertQuerysetEqual(Event.objects.dates('dt', 'year'),
+ [datetime.datetime(2010, 1, 1, tzinfo=UTC),
+ datetime.datetime(2011, 1, 1, tzinfo=UTC)],
+ transform=lambda d: d)
+ self.assertQuerysetEqual(Event.objects.dates('dt', 'month'),
+ [datetime.datetime(2010, 12, 1, tzinfo=UTC),
+ datetime.datetime(2011, 1, 1, tzinfo=UTC)],
+ transform=lambda d: d)
+ self.assertQuerysetEqual(Event.objects.dates('dt', 'day'),
+ [datetime.datetime(2010, 12, 31, tzinfo=UTC),
+ datetime.datetime(2011, 1, 1, tzinfo=UTC)],
+ transform=lambda d: d)
+
+NewDatabaseTests = override_settings(USE_TZ=True)(NewDatabaseTests)
+
+
+class SerializationTests(BaseDateTimeTests):
+
+ # Backend-specific notes:
+ # - JSON supports only milliseconds, microseconds will be truncated.
+ # - PyYAML dumps the UTC offset correctly for timezone-aware datetimes,
+ # but when it loads this representation, it substracts the offset and
+ # returns a naive datetime object in UTC (http://pyyaml.org/ticket/202).
+ # Tests are adapted to take these quirks into account.
+
+ def test_naive_datetime(self):
+ dt = datetime.datetime(2011, 9, 1, 13, 20, 30)
+
+ data = serializers.serialize('python', [Event(dt=dt)])
+ self.assertEqual(data[0]['fields']['dt'], dt)
+ obj = serializers.deserialize('python', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ data = serializers.serialize('json', [Event(dt=dt)])
+ self.assertIn('"fields": {"dt": "2011-09-01T13:20:30"}', data)
+ obj = serializers.deserialize('json', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ data = serializers.serialize('xml', [Event(dt=dt)])
+ self.assertIn('<field type="DateTimeField" name="dt">2011-09-01T13:20:30</field>', data)
+ obj = serializers.deserialize('xml', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ if 'yaml' in serializers.get_serializer_formats():
+ data = serializers.serialize('yaml', [Event(dt=dt)])
+ self.assertIn("- fields: {dt: !!timestamp '2011-09-01 13:20:30'}", data)
+ obj = serializers.deserialize('yaml', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ def test_naive_datetime_with_microsecond(self):
+ dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060)
+
+ data = serializers.serialize('python', [Event(dt=dt)])
+ self.assertEqual(data[0]['fields']['dt'], dt)
+ obj = serializers.deserialize('python', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ data = serializers.serialize('json', [Event(dt=dt)])
+ self.assertIn('"fields": {"dt": "2011-09-01T13:20:30.405"}', data)
+ obj = serializers.deserialize('json', data).next().object
+ self.assertEqual(obj.dt, dt.replace(microsecond=405000))
+
+ data = serializers.serialize('xml', [Event(dt=dt)])
+ self.assertIn('<field type="DateTimeField" name="dt">2011-09-01T13:20:30.405060</field>', data)
+ obj = serializers.deserialize('xml', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ if 'yaml' in serializers.get_serializer_formats():
+ data = serializers.serialize('yaml', [Event(dt=dt)])
+ self.assertIn("- fields: {dt: !!timestamp '2011-09-01 13:20:30.405060'}", data)
+ obj = serializers.deserialize('yaml', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ def test_aware_datetime_with_microsecond(self):
+ dt = datetime.datetime(2011, 9, 1, 17, 20, 30, 405060, tzinfo=ICT)
+
+ data = serializers.serialize('python', [Event(dt=dt)])
+ self.assertEqual(data[0]['fields']['dt'], dt)
+ obj = serializers.deserialize('python', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ data = serializers.serialize('json', [Event(dt=dt)])
+ self.assertIn('"fields": {"dt": "2011-09-01T17:20:30.405+07:00"}', data)
+ obj = serializers.deserialize('json', data).next().object
+ self.assertEqual(obj.dt, dt.replace(microsecond=405000))
+
+ data = serializers.serialize('xml', [Event(dt=dt)])
+ self.assertIn('<field type="DateTimeField" name="dt">2011-09-01T17:20:30.405060+07:00</field>', data)
+ obj = serializers.deserialize('xml', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ if 'yaml' in serializers.get_serializer_formats():
+ data = serializers.serialize('yaml', [Event(dt=dt)])
+ self.assertIn("- fields: {dt: !!timestamp '2011-09-01 17:20:30.405060+07:00'}", data)
+ obj = serializers.deserialize('yaml', data).next().object
+ self.assertEqual(obj.dt.replace(tzinfo=UTC), dt)
+
+ def test_aware_datetime_in_utc(self):
+ dt = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)
+
+ data = serializers.serialize('python', [Event(dt=dt)])
+ self.assertEqual(data[0]['fields']['dt'], dt)
+ obj = serializers.deserialize('python', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ data = serializers.serialize('json', [Event(dt=dt)])
+ self.assertIn('"fields": {"dt": "2011-09-01T10:20:30Z"}', data)
+ obj = serializers.deserialize('json', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ data = serializers.serialize('xml', [Event(dt=dt)])
+ self.assertIn('<field type="DateTimeField" name="dt">2011-09-01T10:20:30+00:00</field>', data)
+ obj = serializers.deserialize('xml', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ if 'yaml' in serializers.get_serializer_formats():
+ data = serializers.serialize('yaml', [Event(dt=dt)])
+ self.assertIn("- fields: {dt: !!timestamp '2011-09-01 10:20:30+00:00'}", data)
+ obj = serializers.deserialize('yaml', data).next().object
+ self.assertEqual(obj.dt.replace(tzinfo=UTC), dt)
+
+ def test_aware_datetime_in_local_timezone(self):
+ dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
+
+ data = serializers.serialize('python', [Event(dt=dt)])
+ self.assertEqual(data[0]['fields']['dt'], dt)
+ obj = serializers.deserialize('python', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ data = serializers.serialize('json', [Event(dt=dt)])
+ self.assertIn('"fields": {"dt": "2011-09-01T13:20:30+03:00"}', data)
+ obj = serializers.deserialize('json', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ data = serializers.serialize('xml', [Event(dt=dt)])
+ self.assertIn('<field type="DateTimeField" name="dt">2011-09-01T13:20:30+03:00</field>', data)
+ obj = serializers.deserialize('xml', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ if 'yaml' in serializers.get_serializer_formats():
+ data = serializers.serialize('yaml', [Event(dt=dt)])
+ self.assertIn("- fields: {dt: !!timestamp '2011-09-01 13:20:30+03:00'}", data)
+ obj = serializers.deserialize('yaml', data).next().object
+ self.assertEqual(obj.dt.replace(tzinfo=UTC), dt)
+
+ def test_aware_datetime_in_other_timezone(self):
+ dt = datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT)
+
+ data = serializers.serialize('python', [Event(dt=dt)])
+ self.assertEqual(data[0]['fields']['dt'], dt)
+ obj = serializers.deserialize('python', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ data = serializers.serialize('json', [Event(dt=dt)])
+ self.assertIn('"fields": {"dt": "2011-09-01T17:20:30+07:00"}', data)
+ obj = serializers.deserialize('json', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ data = serializers.serialize('xml', [Event(dt=dt)])
+ self.assertIn('<field type="DateTimeField" name="dt">2011-09-01T17:20:30+07:00</field>', data)
+ obj = serializers.deserialize('xml', data).next().object
+ self.assertEqual(obj.dt, dt)
+
+ if 'yaml' in serializers.get_serializer_formats():
+ data = serializers.serialize('yaml', [Event(dt=dt)])
+ self.assertIn("- fields: {dt: !!timestamp '2011-09-01 17:20:30+07:00'}", data)
+ obj = serializers.deserialize('yaml', data).next().object
+ self.assertEqual(obj.dt.replace(tzinfo=UTC), dt)
+
+#@override_settings(DATETIME_FORMAT='c', USE_L10N=False, USE_TZ=True)
+class TemplateTests(BaseDateTimeTests):
+
+ def test_localtime_templatetag_and_filters(self):
+ """
+ Test the {% localtime %} templatetag and related filters.
+ """
+ datetimes = {
+ 'utc': datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC),
+ 'eat': datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT),
+ 'ict': datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT),
+ 'naive': datetime.datetime(2011, 9, 1, 13, 20, 30),
+ }
+ templates = {
+ 'notag': Template("{% load tz %}{{ dt }}|{{ dt|aslocaltime }}|{{ dt|asutc }}|{{ dt|astimezone:ICT }}"),
+ 'noarg': Template("{% load tz %}{% localtime %}{{ dt }}|{{ dt|aslocaltime }}|{{ dt|asutc }}|{{ dt|astimezone:ICT }}{% endlocaltime %}"),
+ 'on': Template("{% load tz %}{% localtime on %}{{ dt }}|{{ dt|aslocaltime }}|{{ dt|asutc }}|{{ dt|astimezone:ICT }}{% endlocaltime %}"),
+ 'off': Template("{% load tz %}{% localtime off %}{{ dt }}|{{ dt|aslocaltime }}|{{ dt|asutc }}|{{ dt|astimezone:ICT }}{% endlocaltime %}"),
+ }
+
+ # Transform a list of keys in 'datetimes' to the expected template
+ # output. This makes the definition of 'results' more readable.
+ def t(*result):
+ return '|'.join(datetimes[key].isoformat() for key in result)
+
+ # Results for USE_TZ = True
+
+ results = {
+ 'utc': {
+ 'notag': t('eat', 'eat', 'utc', 'ict'),
+ 'noarg': t('eat', 'eat', 'utc', 'ict'),
+ 'on': t('eat', 'eat', 'utc', 'ict'),
+ 'off': t('utc', 'eat', 'utc', 'ict'),
+ },
+ 'eat': {
+ 'notag': t('eat', 'eat', 'utc', 'ict'),
+ 'noarg': t('eat', 'eat', 'utc', 'ict'),
+ 'on': t('eat', 'eat', 'utc', 'ict'),
+ 'off': t('eat', 'eat', 'utc', 'ict'),
+ },
+ 'ict': {
+ 'notag': t('eat', 'eat', 'utc', 'ict'),
+ 'noarg': t('eat', 'eat', 'utc', 'ict'),
+ 'on': t('eat', 'eat', 'utc', 'ict'),
+ 'off': t('ict', 'eat', 'utc', 'ict'),
+ },
+ 'naive': {
+ 'notag': t('naive', 'eat', 'utc', 'ict'),
+ 'noarg': t('naive', 'eat', 'utc', 'ict'),
+ 'on': t('naive', 'eat', 'utc', 'ict'),
+ 'off': t('naive', 'eat', 'utc', 'ict'),
+ }
+ }
+
+ for k1, dt in datetimes.iteritems():
+ for k2, tpl in templates.iteritems():
+ ctx = Context({'dt': dt, 'ICT': ICT})
+ actual = tpl.render(ctx)
+ expected = results[k1][k2]
+ self.assertEqual(actual, expected, '%s / %s: %r != %r' % (k1, k2, actual, expected))
+
+ # Changes for USE_TZ = False
+
+ results['utc']['notag'] = t('utc', 'eat', 'utc', 'ict')
+ results['ict']['notag'] = t('ict', 'eat', 'utc', 'ict')
+
+ with self.settings(USE_TZ=False):
+ for k1, dt in datetimes.iteritems():
+ for k2, tpl in templates.iteritems():
+ ctx = Context({'dt': dt, 'ICT': ICT})
+ actual = tpl.render(ctx)
+ expected = results[k1][k2]
+ self.assertEqual(actual, expected, '%s / %s: %r != %r' % (k1, k2, actual, expected))
+
+ @skipIf(pytz is None, "this test requires pytz")
+ def test_localtime_filters_with_pytz(self):
+ """
+ Test the |aslocaltime, |asutc, and |astimezone filters with pytz.
+ """
+ # Use a pytz timezone as local time
+ tpl = Template("{% load tz %}{{ dt|aslocaltime }}|{{ dt|asutc }}")
+ ctx = Context({'dt': datetime.datetime(2011, 9, 1, 12, 20, 30)})
+
+ timezone._localtime = None
+ with self.settings(TIME_ZONE='Europe/Paris'):
+ self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00|2011-09-01T10:20:30+00:00")
+ timezone._localtime = None
+
+ # Use a pytz timezone as argument
+ tpl = Template("{% load tz %}{{ dt|astimezone:tz }}")
+ ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30),
+ 'tz': pytz.timezone('Europe/Paris')})
+ self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
+
+ # Use a pytz timezone name as argument
+ tpl = Template("{% load tz %}{{ dt|astimezone:'Europe/Paris' }}")
+ ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30),
+ 'tz': pytz.timezone('Europe/Paris')})
+ self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
+
+ def test_localtime_templatetag_invalid_argument(self):
+ with self.assertRaises(TemplateSyntaxError):
+ Template("{% load tz %}{% localtime foo %}{% endlocaltime %}").render()
+
+ def test_localtime_filters_do_not_raise_exceptions(self):
+ """
+ Test the |aslocaltime, |asutc, and |astimezone filters on bad inputs.
+ """
+ tpl = Template("{% load tz %}{{ dt }}|{{ dt|aslocaltime }}|{{ dt|asutc }}|{{ dt|astimezone:tz }}")
+ with self.settings(USE_TZ=True):
+ # bad datetime value
+ ctx = Context({'dt': None, 'tz': ICT})
+ self.assertEqual(tpl.render(ctx), "None|||")
+ ctx = Context({'dt': 'not a date', 'tz': ICT})
+ self.assertEqual(tpl.render(ctx), "not a date|||")
+ # bad timezone value
+ tpl = Template("{% load tz %}{{ dt|astimezone:tz }}")
+ ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30), 'tz': None})
+ self.assertEqual(tpl.render(ctx), "")
+ ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30), 'tz': 'not a tz'})
+ self.assertEqual(tpl.render(ctx), "")
+
+ def test_timezone_templatetag(self):
+ """
+ Test the {% timezone %} templatetag.
+ """
+ tpl = Template("{% load tz %}"
+ "{{ dt }}|"
+ "{% timezone tz1 %}"
+ "{{ dt }}|"
+ "{% timezone tz2 %}"
+ "{{ dt }}"
+ "{% endtimezone %}"
+ "{% endtimezone %}")
+ ctx = Context({'dt': datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC),
+ 'tz1': ICT, 'tz2': None})
+ self.assertEqual(tpl.render(ctx), "2011-09-01T13:20:30+03:00|2011-09-01T17:20:30+07:00|2011-09-01T13:20:30+03:00")
+
+ @skipIf(pytz is None, "this test requires pytz")
+ def test_timezone_templatetag_with_pytz(self):
+ """
+ Test the {% timezone %} templatetag with pytz.
+ """
+ tpl = Template("{% load tz %}{% timezone tz %}{{ dt }}{% endtimezone %}")
+
+ # Use a pytz timezone as argument
+ ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT),
+ 'tz': pytz.timezone('Europe/Paris')})
+ self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
+
+ # Use a pytz timezone name as argument
+ ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT),
+ 'tz': 'Europe/Paris'})
+ self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
+
+ def test_timezone_templatetag_invalid_argument(self):
+ with self.assertRaises(TemplateSyntaxError):
+ Template("{% load tz %}{% timezone %}{% endtimezone %}").render()
+ with self.assertRaises(ValueError if pytz is None else pytz.UnknownTimeZoneError):
+ Template("{% load tz %}{% timezone tz %}{% endtimezone %}").render(Context({'tz': 'foobar'}))
+
+ def test_get_current_timezone_templatetag(self):
+ """
+ Test the {% get_current_timezone %} templatetag.
+ """
+ tpl = Template("{% load tz %}{% get_current_timezone as time_zone %}{{ time_zone }}")
+
+ self.assertEqual(tpl.render(Context()), "Africa/Nairobi" if pytz else "EAT")
+ with timezone.override(UTC):
+ self.assertEqual(tpl.render(Context()), "UTC")
+
+ tpl = Template("{% load tz %}{% timezone tz %}{% get_current_timezone as time_zone %}{% endtimezone %}{{ time_zone }}")
+
+ self.assertEqual(tpl.render(Context({'tz': ICT})), "+0700")
+ with timezone.override(UTC):
+ self.assertEqual(tpl.render(Context({'tz': ICT})), "+0700")
+
+ @skipIf(pytz is None, "this test requires pytz")
+ def test_get_current_timezone_templatetag_with_pytz(self):
+ """
+ Test the {% get_current_timezone %} templatetag with pytz.
+ """
+ tpl = Template("{% load tz %}{% get_current_timezone as time_zone %}{{ time_zone }}")
+ with timezone.override(pytz.timezone('Europe/Paris')):
+ self.assertEqual(tpl.render(Context()), "Europe/Paris")
+
+ tpl = Template("{% load tz %}{% timezone 'Europe/Paris' %}{% get_current_timezone as time_zone %}{% endtimezone %}{{ time_zone }}")
+ self.assertEqual(tpl.render(Context()), "Europe/Paris")
+
+ def test_get_current_timezone_templatetag_invalid_argument(self):
+ with self.assertRaises(TemplateSyntaxError):
+ Template("{% load tz %}{% get_current_timezone %}").render()
+
+ def test_tz_template_context_processor(self):
+ """
+ Test the django.core.context_processors.tz template context processor.
+ """
+ tpl = Template("{{ TIME_ZONE }}")
+ self.assertEqual(tpl.render(Context()), "")
+ self.assertEqual(tpl.render(RequestContext(HttpRequest())), "Africa/Nairobi" if pytz else "EAT")
+
+ def test_date_and_time_template_filters(self):
+ tpl = Template("{{ dt|date:'Y-m-d' }} at {{ dt|time:'H:i:s' }}")
+ ctx = Context({'dt': datetime.datetime(2011, 9, 1, 20, 20, 20, tzinfo=UTC)})
+ self.assertEqual(tpl.render(ctx), "2011-09-01 at 23:20:20")
+ with timezone.override(ICT):
+ self.assertEqual(tpl.render(ctx), "2011-09-02 at 03:20:20")
+
+ def test_date_and_time_template_filters_honor_localtime(self):
+ tpl = Template("{% load tz %}{% localtime off %}{{ dt|date:'Y-m-d' }} at {{ dt|time:'H:i:s' }}{% endlocaltime %}")
+ ctx = Context({'dt': datetime.datetime(2011, 9, 1, 20, 20, 20, tzinfo=UTC)})
+ self.assertEqual(tpl.render(ctx), "2011-09-01 at 20:20:20")
+ with timezone.override(ICT):
+ self.assertEqual(tpl.render(ctx), "2011-09-01 at 20:20:20")
+
+TemplateTests = override_settings(DATETIME_FORMAT='c', USE_L10N=False, USE_TZ=True)(TemplateTests)
+
+#@override_settings(DATETIME_FORMAT='c', USE_L10N=False, USE_TZ=False)
+class LegacyFormsTests(BaseDateTimeTests):
+
+ def test_form(self):
+ form = EventForm({'dt': u'2011-09-01 13:20:30'})
+ self.assertTrue(form.is_valid())
+ self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 13, 20, 30))
+
+ @skipIf(pytz is None, "this test requires pytz")
+ def test_form_with_non_existent_time(self):
+ form = EventForm({'dt': u'2011-03-27 02:30:00'})
+ with timezone.override(pytz.timezone('Europe/Paris')):
+ # this is obviously a bug
+ self.assertTrue(form.is_valid())
+ self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 3, 27, 2, 30, 0))
+
+ @skipIf(pytz is None, "this test requires pytz")
+ def test_form_with_ambiguous_time(self):
+ form = EventForm({'dt': u'2011-10-30 02:30:00'})
+ with timezone.override(pytz.timezone('Europe/Paris')):
+ # this is obviously a bug
+ self.assertTrue(form.is_valid())
+ self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 10, 30, 2, 30, 0))
+
+ def test_split_form(self):
+ form = EventSplitForm({'dt_0': u'2011-09-01', 'dt_1': u'13:20:30'})
+ self.assertTrue(form.is_valid())
+ self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 13, 20, 30))
+
+ def test_model_form(self):
+ EventModelForm({'dt': u'2011-09-01 13:20:30'}).save()
+ e = Event.objects.get()
+ self.assertEqual(e.dt, datetime.datetime(2011, 9, 1, 13, 20, 30))
+
+LegacyFormsTests = override_settings(DATETIME_FORMAT='c', USE_L10N=False, USE_TZ=False)(LegacyFormsTests)
+
+#@override_settings(DATETIME_FORMAT='c', USE_L10N=False, USE_TZ=True)
+class NewFormsTests(BaseDateTimeTests):
+
+ def test_form(self):
+ form = EventForm({'dt': u'2011-09-01 13:20:30'})
+ self.assertTrue(form.is_valid())
+ self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
+
+ def test_form_with_other_timezone(self):
+ form = EventForm({'dt': u'2011-09-01 17:20:30'})
+ with timezone.override(ICT):
+ self.assertTrue(form.is_valid())
+ self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
+
+ @skipIf(pytz is None, "this test requires pytz")
+ def test_form_with_non_existent_time(self):
+ with timezone.override(pytz.timezone('Europe/Paris')):
+ form = EventForm({'dt': u'2011-03-27 02:30:00'})
+ self.assertFalse(form.is_valid())
+ self.assertEqual(form.errors['dt'],
+ [u"2011-03-27 02:30:00 couldn't be interpreted in time zone "
+ u"Europe/Paris; it may be ambiguous or it may not exist."])
+
+ @skipIf(pytz is None, "this test requires pytz")
+ def test_form_with_ambiguous_time(self):
+ with timezone.override(pytz.timezone('Europe/Paris')):
+ form = EventForm({'dt': u'2011-10-30 02:30:00'})
+ self.assertFalse(form.is_valid())
+ self.assertEqual(form.errors['dt'],
+ [u"2011-10-30 02:30:00 couldn't be interpreted in time zone "
+ u"Europe/Paris; it may be ambiguous or it may not exist."])
+
+ def test_split_form(self):
+ form = EventSplitForm({'dt_0': u'2011-09-01', 'dt_1': u'13:20:30'})
+ self.assertTrue(form.is_valid())
+ self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
+
+ def test_model_form(self):
+ EventModelForm({'dt': u'2011-09-01 13:20:30'}).save()
+ e = Event.objects.get()
+ self.assertEqual(e.dt, datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
+
+NewFormsTests = override_settings(DATETIME_FORMAT='c', USE_L10N=False, USE_TZ=True)(NewFormsTests)
+
+#@override_settings(DATETIME_FORMAT='c', USE_L10N=False, USE_TZ=True)
+class AdminTests(BaseDateTimeTests):
+
+ urls = 'modeltests.timezones.urls'
+ fixtures = ['users.xml']
+
+ def setUp(self):
+ self.client.login(username='super', password='secret')
+
+ def test_changelist(self):
+ e = Event.objects.create(dt=datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
+ response = self.client.get(reverse('admin:timezones_event_changelist'))
+ self.assertContains(response, e.dt.astimezone(EAT).isoformat())
+
+ def test_changelist_in_other_timezone(self):
+ e = Event.objects.create(dt=datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
+ with timezone.override(ICT):
+ response = self.client.get(reverse('admin:timezones_event_changelist'))
+ self.assertContains(response, e.dt.astimezone(ICT).isoformat())
+
+ def test_change_editable(self):
+ e = Event.objects.create(dt=datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
+ response = self.client.get(reverse('admin:timezones_event_change', args=(e.pk,)))
+ self.assertContains(response, e.dt.astimezone(EAT).date().isoformat())
+ self.assertContains(response, e.dt.astimezone(EAT).time().isoformat())
+
+ def test_change_editable_in_other_timezone(self):
+ e = Event.objects.create(dt=datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
+ with timezone.override(ICT):
+ response = self.client.get(reverse('admin:timezones_event_change', args=(e.pk,)))
+ self.assertContains(response, e.dt.astimezone(ICT).date().isoformat())
+ self.assertContains(response, e.dt.astimezone(ICT).time().isoformat())
+
+ def test_change_readonly(self):
+ Timestamp.objects.create()
+ # re-fetch the object for backends that lose microseconds (MySQL)
+ t = Timestamp.objects.get()
+ response = self.client.get(reverse('admin:timezones_timestamp_change', args=(t.pk,)))
+ self.assertContains(response, t.created.astimezone(EAT).isoformat())
+
+ def test_change_readonly_in_other_timezone(self):
+ Timestamp.objects.create()
+ # re-fetch the object for backends that lose microseconds (MySQL)
+ t = Timestamp.objects.get()
+ with timezone.override(ICT):
+ response = self.client.get(reverse('admin:timezones_timestamp_change', args=(t.pk,)))
+ self.assertContains(response, t.created.astimezone(ICT).isoformat())
+
+AdminTests = override_settings(DATETIME_FORMAT='c', USE_L10N=False, USE_TZ=True)(AdminTests)
diff --git a/tests/modeltests/timezones/urls.py b/tests/modeltests/timezones/urls.py
new file mode 100644
index 0000000000..e4f42972db
--- /dev/null
+++ b/tests/modeltests/timezones/urls.py
@@ -0,0 +1,10 @@
+from __future__ import absolute_import
+
+from django.conf.urls import patterns, include
+from django.contrib import admin
+
+from . import admin as tz_admin
+
+urlpatterns = patterns('',
+ (r'^admin/', include(admin.site.urls)),
+)
diff --git a/tests/modeltests/validation/test_error_messages.py b/tests/modeltests/validation/test_error_messages.py
index b042c2ee7c..4a2ad1fffa 100644
--- a/tests/modeltests/validation/test_error_messages.py
+++ b/tests/modeltests/validation/test_error_messages.py
@@ -104,16 +104,43 @@ class ValidationMessagesTest(TestCase):
f.clean('foo', None)
except ValidationError, e:
self.assertEqual(e.messages, [
- u"'foo' value either has an invalid valid format "
- u"(The format must be YYYY-MM-DD HH:MM[:ss[.uuuuuu]]) "
- u"or is an invalid date/time."])
- self.assertRaises(ValidationError, f.clean,
- '2011-10-32 10:10', None)
+ u"'foo' value has an invalid format. It must be "
+ u"in YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."])
+
+ # Correct format but invalid date
+ self.assertRaises(ValidationError, f.clean, '2011-10-32', None)
+ try:
+ f.clean('2011-10-32', None)
+ except ValidationError, e:
+ self.assertEqual(e.messages, [
+ u"'2011-10-32' value has the correct format "
+ u"(YYYY-MM-DD) but it is an invalid date."])
+
# Correct format but invalid date/time
+ self.assertRaises(ValidationError, f.clean, '2011-10-32 10:10', None)
try:
f.clean('2011-10-32 10:10', None)
except ValidationError, e:
self.assertEqual(e.messages, [
- u"'2011-10-32 10:10' value either has an invalid valid format "
- u"(The format must be YYYY-MM-DD HH:MM[:ss[.uuuuuu]]) "
- u"or is an invalid date/time."]) \ No newline at end of file
+ u"'2011-10-32 10:10' value has the correct format "
+ u"(YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) "
+ u"but it is an invalid date/time."])
+
+ def test_time_field_raises_error_message(self):
+ f = models.TimeField()
+ # Wrong format
+ self.assertRaises(ValidationError, f.clean, 'foo', None)
+ try:
+ f.clean('foo', None)
+ except ValidationError, e:
+ self.assertEqual(e.messages, [
+ u"'foo' value has an invalid format. It must be in "
+ u"HH:MM[:ss[.uuuuuu]] format."])
+ # Correct format but invalid time
+ self.assertRaises(ValidationError, f.clean, '25:50', None)
+ try:
+ f.clean('25:50', None)
+ except ValidationError, e:
+ self.assertEqual(e.messages, [
+ u"'25:50' value has the correct format "
+ u"(HH:MM[:ss[.uuuuuu]]) but it is an invalid time."])
diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py
index e6c03837d4..f1590d1121 100644
--- a/tests/regressiontests/cache/tests.py
+++ b/tests/regressiontests/cache/tests.py
@@ -24,7 +24,7 @@ from django.template.response import TemplateResponse
from django.test import TestCase, RequestFactory
from django.test.utils import (get_warnings_state, restore_warnings_state,
override_settings)
-from django.utils import translation, unittest
+from django.utils import timezone, translation, unittest
from django.utils.cache import (patch_vary_headers, get_cache_key,
learn_cache_key, patch_cache_control, patch_response_headers)
from django.views.decorators.cache import cache_page
@@ -1154,7 +1154,7 @@ class CacheI18nTest(TestCase):
request.session = {}
return request
- @override_settings(USE_I18N=True, USE_L10N=False)
+ @override_settings(USE_I18N=True, USE_L10N=False, USE_TZ=False)
def test_cache_key_i18n_translation(self):
request = self._get_request()
lang = translation.get_language()
@@ -1164,7 +1164,7 @@ class CacheI18nTest(TestCase):
key2 = get_cache_key(request)
self.assertEqual(key, key2)
- @override_settings(USE_I18N=False, USE_L10N=True)
+ @override_settings(USE_I18N=False, USE_L10N=True, USE_TZ=False)
def test_cache_key_i18n_formatting(self):
request = self._get_request()
lang = translation.get_language()
@@ -1174,13 +1174,25 @@ class CacheI18nTest(TestCase):
key2 = get_cache_key(request)
self.assertEqual(key, key2)
+ @override_settings(USE_I18N=False, USE_L10N=False, USE_TZ=True)
+ def test_cache_key_i18n_timezone(self):
+ request = self._get_request()
+ tz = timezone.get_current_timezone_name()
+ response = HttpResponse()
+ key = learn_cache_key(request, response)
+ self.assertIn(tz, key, "Cache keys should include the time zone name when time zones are active")
+ key2 = get_cache_key(request)
+ self.assertEqual(key, key2)
+
@override_settings(USE_I18N=False, USE_L10N=False)
def test_cache_key_no_i18n (self):
request = self._get_request()
lang = translation.get_language()
+ tz = timezone.get_current_timezone_name()
response = HttpResponse()
key = learn_cache_key(request, response)
self.assertNotIn(lang, key, "Cache keys shouldn't include the language name when i18n isn't active")
+ self.assertNotIn(tz, key, "Cache keys shouldn't include the time zone name when i18n isn't active")
@override_settings(
CACHE_MIDDLEWARE_KEY_PREFIX="test",
diff --git a/tests/regressiontests/datatypes/tests.py b/tests/regressiontests/datatypes/tests.py
index 3cf11024fd..fb94e831ba 100644
--- a/tests/regressiontests/datatypes/tests.py
+++ b/tests/regressiontests/datatypes/tests.py
@@ -3,7 +3,7 @@ from __future__ import absolute_import
import datetime
from django.test import TestCase, skipIfDBFeature
-from django.utils import tzinfo
+from django.utils.timezone import utc
from .models import Donut, RumBaba
@@ -79,7 +79,7 @@ class DataTypesTestCase(TestCase):
def test_error_on_timezone(self):
"""Regression test for #8354: the MySQL and Oracle backends should raise
an error if given a timezone-aware datetime object."""
- dt = datetime.datetime(2008, 8, 31, 16, 20, tzinfo=tzinfo.FixedOffset(0))
+ dt = datetime.datetime(2008, 8, 31, 16, 20, tzinfo=utc)
d = Donut(name='Bear claw', consumed_at=dt)
self.assertRaises(ValueError, d.save)
# ValueError: MySQL backend does not support timezone-aware datetimes.
diff --git a/tests/regressiontests/defaultfilters/tests.py b/tests/regressiontests/defaultfilters/tests.py
index b18334d011..6da4f75a67 100644
--- a/tests/regressiontests/defaultfilters/tests.py
+++ b/tests/regressiontests/defaultfilters/tests.py
@@ -421,7 +421,7 @@ class DefaultFiltersTests(TestCase):
def test_timeuntil(self):
self.assertEqual(
- timeuntil_filter(datetime.datetime.now() + datetime.timedelta(1)),
+ timeuntil_filter(datetime.datetime.now() + datetime.timedelta(1, 1)),
u'1 day')
self.assertEqual(
diff --git a/tests/regressiontests/utils/dateformat.py b/tests/regressiontests/utils/dateformat.py
index f1ef720b20..313a76ba4b 100644
--- a/tests/regressiontests/utils/dateformat.py
+++ b/tests/regressiontests/utils/dateformat.py
@@ -4,6 +4,7 @@ import time
from django.utils.dateformat import format
from django.utils import dateformat, translation, unittest
+from django.utils.timezone import utc
from django.utils.tzinfo import FixedOffset, LocalTimezone
@@ -56,7 +57,6 @@ class DateFormatTests(unittest.TestCase):
self.assertEqual(datetime.fromtimestamp(int(format(dt, 'U')), ltz).utctimetuple(), dt.utctimetuple())
def test_epoch(self):
- utc = FixedOffset(0)
udt = datetime(1970, 1, 1, tzinfo=utc)
self.assertEqual(format(udt, 'U'), u'0')
diff --git a/tests/regressiontests/utils/tests.py b/tests/regressiontests/utils/tests.py
index 4db894d6ad..b72a88b33d 100644
--- a/tests/regressiontests/utils/tests.py
+++ b/tests/regressiontests/utils/tests.py
@@ -23,3 +23,4 @@ from .datetime_safe import DatetimeTests
from .baseconv import TestBaseConv
from .jslex import JsTokensTest, JsToCForGettextTest
from .ipv6 import TestUtilsIPv6
+from .timezone import TimezoneTests
diff --git a/tests/regressiontests/utils/timesince.py b/tests/regressiontests/utils/timesince.py
index c0fe590028..b0f94d182b 100644
--- a/tests/regressiontests/utils/timesince.py
+++ b/tests/regressiontests/utils/timesince.py
@@ -105,3 +105,12 @@ class TimesinceTests(unittest.TestCase):
self.assertEqual(timeuntil(today+self.oneday, today), u'1 day')
self.assertEqual(timeuntil(today-self.oneday, today), u'0 minutes')
self.assertEqual(timeuntil(today+self.oneweek, today), u'1 week')
+
+ def test_naive_datetime_with_tzinfo_attribute(self):
+ class naive(datetime.tzinfo):
+ def utcoffset(self, dt):
+ return None
+ future = datetime.datetime(2080, 1, 1, tzinfo=naive())
+ self.assertEqual(timesince(future), u'0 minutes')
+ past = datetime.datetime(1980, 1, 1, tzinfo=naive())
+ self.assertEqual(timeuntil(past), u'0 minutes')
diff --git a/tests/regressiontests/utils/timezone.py b/tests/regressiontests/utils/timezone.py
new file mode 100644
index 0000000000..870997db65
--- /dev/null
+++ b/tests/regressiontests/utils/timezone.py
@@ -0,0 +1,18 @@
+import copy
+import pickle
+from django.utils.timezone import UTC, LocalTimezone
+from django.utils import unittest
+
+class TimezoneTests(unittest.TestCase):
+
+ def test_copy(self):
+ self.assertIsInstance(copy.copy(UTC()), UTC)
+ self.assertIsInstance(copy.copy(LocalTimezone()), LocalTimezone)
+
+ def test_deepcopy(self):
+ self.assertIsInstance(copy.deepcopy(UTC()), UTC)
+ self.assertIsInstance(copy.deepcopy(LocalTimezone()), LocalTimezone)
+
+ def test_pickling_unpickling(self):
+ self.assertIsInstance(pickle.loads(pickle.dumps(UTC())), UTC)
+ self.assertIsInstance(pickle.loads(pickle.dumps(LocalTimezone())), LocalTimezone)
diff --git a/tests/regressiontests/utils/tzinfo.py b/tests/regressiontests/utils/tzinfo.py
index a60a4574de..cec92652cd 100644
--- a/tests/regressiontests/utils/tzinfo.py
+++ b/tests/regressiontests/utils/tzinfo.py
@@ -1,5 +1,7 @@
+import copy
import datetime
import os
+import pickle
import time
from django.utils.tzinfo import FixedOffset, LocalTimezone
from django.utils import unittest
@@ -60,3 +62,18 @@ class TzinfoTests(unittest.TestCase):
self.assertEqual(
repr(datetime.datetime.fromtimestamp(ts + 3600, tz)),
'datetime.datetime(2010, 11, 7, 1, 0, tzinfo=EST)')
+
+ def test_copy(self):
+ now = datetime.datetime.now()
+ self.assertIsInstance(copy.copy(FixedOffset(90)), FixedOffset)
+ self.assertIsInstance(copy.copy(LocalTimezone(now)), LocalTimezone)
+
+ def test_deepcopy(self):
+ now = datetime.datetime.now()
+ self.assertIsInstance(copy.deepcopy(FixedOffset(90)), FixedOffset)
+ self.assertIsInstance(copy.deepcopy(LocalTimezone(now)), LocalTimezone)
+
+ def test_pickling_unpickling(self):
+ now = datetime.datetime.now()
+ self.assertIsInstance(pickle.loads(pickle.dumps(FixedOffset(90))), FixedOffset)
+ self.assertIsInstance(pickle.loads(pickle.dumps(LocalTimezone(now))), LocalTimezone)