tests.py 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871
  1. from __future__ import with_statement
  2. import datetime
  3. import os
  4. import time
  5. try:
  6. import pytz
  7. except ImportError:
  8. pytz = None
  9. from django.conf import settings
  10. from django.core import serializers
  11. from django.core.urlresolvers import reverse
  12. from django.db import connection
  13. from django.db.models import Min, Max
  14. from django.http import HttpRequest
  15. from django.template import Context, RequestContext, Template, TemplateSyntaxError
  16. from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
  17. from django.test.utils import override_settings
  18. from django.utils import timezone
  19. from django.utils.tzinfo import FixedOffset
  20. from django.utils.unittest import skipIf
  21. from .forms import EventForm, EventSplitForm, EventModelForm
  22. from .models import Event, Timestamp
  23. # These tests use the EAT (Eastern Africa Time) and ICT (Indochina Time)
  24. # who don't have Daylight Saving Time, so we can represent them easily
  25. # with FixedOffset, and use them directly as tzinfo in the constructors.
  26. # settings.TIME_ZONE is forced to EAT. Most tests use a variant of
  27. # datetime.datetime(2011, 9, 1, 13, 20, 30), which translates to
  28. # 10:20:30 in UTC and 17:20:30 in ICT.
  29. UTC = timezone.utc
  30. EAT = FixedOffset(180) # Africa/Nairobi
  31. ICT = FixedOffset(420) # Asia/Bangkok
  32. TZ_SUPPORT = hasattr(time, 'tzset')
  33. class BaseDateTimeTests(TestCase):
  34. @classmethod
  35. def setUpClass(self):
  36. self._old_time_zone = settings.TIME_ZONE
  37. settings.TIME_ZONE = connection.settings_dict['TIME_ZONE'] = 'Africa/Nairobi'
  38. timezone._localtime = None
  39. if TZ_SUPPORT:
  40. self._old_tz = os.environ.get('TZ')
  41. os.environ['TZ'] = 'Africa/Nairobi'
  42. time.tzset()
  43. # Create a new cursor, for test cases that change the value of USE_TZ.
  44. connection.close()
  45. @classmethod
  46. def tearDownClass(self):
  47. settings.TIME_ZONE = connection.settings_dict['TIME_ZONE'] = self._old_time_zone
  48. timezone._localtime = None
  49. if TZ_SUPPORT:
  50. if self._old_tz is None:
  51. del os.environ['TZ']
  52. else:
  53. os.environ['TZ'] = self._old_tz
  54. time.tzset()
  55. #@override_settings(USE_TZ=False)
  56. class LegacyDatabaseTests(BaseDateTimeTests):
  57. def test_naive_datetime(self):
  58. dt = datetime.datetime(2011, 9, 1, 13, 20, 30)
  59. Event.objects.create(dt=dt)
  60. event = Event.objects.get()
  61. self.assertEqual(event.dt, dt)
  62. @skipUnlessDBFeature('supports_microsecond_precision')
  63. def test_naive_datetime_with_microsecond(self):
  64. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060)
  65. Event.objects.create(dt=dt)
  66. event = Event.objects.get()
  67. self.assertEqual(event.dt, dt)
  68. @skipIfDBFeature('supports_microsecond_precision')
  69. def test_naive_datetime_with_microsecond_unsupported(self):
  70. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060)
  71. Event.objects.create(dt=dt)
  72. event = Event.objects.get()
  73. # microseconds are lost during a round-trip in the database
  74. self.assertEqual(event.dt, dt.replace(microsecond=0))
  75. @skipUnlessDBFeature('supports_timezones')
  76. def test_aware_datetime_in_local_timezone(self):
  77. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
  78. Event.objects.create(dt=dt)
  79. event = Event.objects.get()
  80. self.assertIsNone(event.dt.tzinfo)
  81. # interpret the naive datetime in local time to get the correct value
  82. self.assertEqual(event.dt.replace(tzinfo=EAT), dt)
  83. @skipUnlessDBFeature('supports_timezones')
  84. @skipUnlessDBFeature('supports_microsecond_precision')
  85. def test_aware_datetime_in_local_timezone_with_microsecond(self):
  86. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060, tzinfo=EAT)
  87. Event.objects.create(dt=dt)
  88. event = Event.objects.get()
  89. self.assertIsNone(event.dt.tzinfo)
  90. # interpret the naive datetime in local time to get the correct value
  91. self.assertEqual(event.dt.replace(tzinfo=EAT), dt)
  92. # This combination actually never happens.
  93. @skipUnlessDBFeature('supports_timezones')
  94. @skipIfDBFeature('supports_microsecond_precision')
  95. def test_aware_datetime_in_local_timezone_with_microsecond_unsupported(self):
  96. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060, tzinfo=EAT)
  97. Event.objects.create(dt=dt)
  98. event = Event.objects.get()
  99. self.assertIsNone(event.dt.tzinfo)
  100. # interpret the naive datetime in local time to get the correct value
  101. # microseconds are lost during a round-trip in the database
  102. self.assertEqual(event.dt.replace(tzinfo=EAT), dt.replace(microsecond=0))
  103. @skipUnlessDBFeature('supports_timezones')
  104. @skipIfDBFeature('needs_datetime_string_cast')
  105. def test_aware_datetime_in_utc(self):
  106. dt = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)
  107. Event.objects.create(dt=dt)
  108. event = Event.objects.get()
  109. self.assertIsNone(event.dt.tzinfo)
  110. # interpret the naive datetime in local time to get the correct value
  111. self.assertEqual(event.dt.replace(tzinfo=EAT), dt)
  112. # This combination is no longer possible since timezone support
  113. # was removed from the SQLite backend -- it didn't work.
  114. @skipUnlessDBFeature('supports_timezones')
  115. @skipUnlessDBFeature('needs_datetime_string_cast')
  116. def test_aware_datetime_in_utc_unsupported(self):
  117. dt = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)
  118. Event.objects.create(dt=dt)
  119. event = Event.objects.get()
  120. self.assertIsNone(event.dt.tzinfo)
  121. # django.db.backend.utils.typecast_dt will just drop the
  122. # timezone, so a round-trip in the database alters the data (!)
  123. # interpret the naive datetime in local time and you get a wrong value
  124. self.assertNotEqual(event.dt.replace(tzinfo=EAT), dt)
  125. # interpret the naive datetime in original time to get the correct value
  126. self.assertEqual(event.dt.replace(tzinfo=UTC), dt)
  127. @skipUnlessDBFeature('supports_timezones')
  128. @skipIfDBFeature('needs_datetime_string_cast')
  129. def test_aware_datetime_in_other_timezone(self):
  130. dt = datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT)
  131. Event.objects.create(dt=dt)
  132. event = Event.objects.get()
  133. self.assertIsNone(event.dt.tzinfo)
  134. # interpret the naive datetime in local time to get the correct value
  135. self.assertEqual(event.dt.replace(tzinfo=EAT), dt)
  136. # This combination is no longer possible since timezone support
  137. # was removed from the SQLite backend -- it didn't work.
  138. @skipUnlessDBFeature('supports_timezones')
  139. @skipUnlessDBFeature('needs_datetime_string_cast')
  140. def test_aware_datetime_in_other_timezone_unsupported(self):
  141. dt = datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT)
  142. Event.objects.create(dt=dt)
  143. event = Event.objects.get()
  144. self.assertIsNone(event.dt.tzinfo)
  145. # django.db.backend.utils.typecast_dt will just drop the
  146. # timezone, so a round-trip in the database alters the data (!)
  147. # interpret the naive datetime in local time and you get a wrong value
  148. self.assertNotEqual(event.dt.replace(tzinfo=EAT), dt)
  149. # interpret the naive datetime in original time to get the correct value
  150. self.assertEqual(event.dt.replace(tzinfo=ICT), dt)
  151. @skipIfDBFeature('supports_timezones')
  152. def test_aware_datetime_unspported(self):
  153. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
  154. with self.assertRaises(ValueError):
  155. Event.objects.create(dt=dt)
  156. def test_auto_now_and_auto_now_add(self):
  157. now = datetime.datetime.now()
  158. past = now - datetime.timedelta(seconds=2)
  159. future = now + datetime.timedelta(seconds=2)
  160. Timestamp.objects.create()
  161. ts = Timestamp.objects.get()
  162. self.assertLess(past, ts.created)
  163. self.assertLess(past, ts.updated)
  164. self.assertGreater(future, ts.updated)
  165. self.assertGreater(future, ts.updated)
  166. def test_query_filter(self):
  167. dt1 = datetime.datetime(2011, 9, 1, 12, 20, 30)
  168. dt2 = datetime.datetime(2011, 9, 1, 14, 20, 30)
  169. Event.objects.create(dt=dt1)
  170. Event.objects.create(dt=dt2)
  171. self.assertEqual(Event.objects.filter(dt__gte=dt1).count(), 2)
  172. self.assertEqual(Event.objects.filter(dt__gt=dt1).count(), 1)
  173. self.assertEqual(Event.objects.filter(dt__gte=dt2).count(), 1)
  174. self.assertEqual(Event.objects.filter(dt__gt=dt2).count(), 0)
  175. def test_query_date_related_filters(self):
  176. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0))
  177. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0))
  178. self.assertEqual(Event.objects.filter(dt__year=2011).count(), 2)
  179. self.assertEqual(Event.objects.filter(dt__month=1).count(), 2)
  180. self.assertEqual(Event.objects.filter(dt__day=1).count(), 2)
  181. self.assertEqual(Event.objects.filter(dt__week_day=7).count(), 2)
  182. def test_query_aggregation(self):
  183. # Only min and max make sense for datetimes.
  184. Event.objects.create(dt=datetime.datetime(2011, 9, 1, 23, 20, 20))
  185. Event.objects.create(dt=datetime.datetime(2011, 9, 1, 13, 20, 30))
  186. Event.objects.create(dt=datetime.datetime(2011, 9, 1, 3, 20, 40))
  187. result = Event.objects.all().aggregate(Min('dt'), Max('dt'))
  188. self.assertEqual(result, {
  189. 'dt__min': datetime.datetime(2011, 9, 1, 3, 20, 40),
  190. 'dt__max': datetime.datetime(2011, 9, 1, 23, 20, 20),
  191. })
  192. def test_query_dates(self):
  193. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0))
  194. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0))
  195. self.assertQuerysetEqual(Event.objects.dates('dt', 'year'),
  196. [datetime.datetime(2011, 1, 1)], transform=lambda d: d)
  197. self.assertQuerysetEqual(Event.objects.dates('dt', 'month'),
  198. [datetime.datetime(2011, 1, 1)], transform=lambda d: d)
  199. self.assertQuerysetEqual(Event.objects.dates('dt', 'day'),
  200. [datetime.datetime(2011, 1, 1)], transform=lambda d: d)
  201. LegacyDatabaseTests = override_settings(USE_TZ=False)(LegacyDatabaseTests)
  202. #@override_settings(USE_TZ=True)
  203. class NewDatabaseTests(BaseDateTimeTests):
  204. def test_naive_datetime(self):
  205. dt = datetime.datetime(2011, 9, 1, 13, 20, 30)
  206. Event.objects.create(dt=dt)
  207. event = Event.objects.get()
  208. # naive datetimes are interpreted in local time
  209. self.assertEqual(event.dt, dt.replace(tzinfo=EAT))
  210. @skipUnlessDBFeature('supports_microsecond_precision')
  211. def test_naive_datetime_with_microsecond(self):
  212. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060)
  213. Event.objects.create(dt=dt)
  214. event = Event.objects.get()
  215. # naive datetimes are interpreted in local time
  216. self.assertEqual(event.dt, dt.replace(tzinfo=EAT))
  217. @skipIfDBFeature('supports_microsecond_precision')
  218. def test_naive_datetime_with_microsecond_unsupported(self):
  219. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060)
  220. Event.objects.create(dt=dt)
  221. event = Event.objects.get()
  222. # microseconds are lost during a round-trip in the database
  223. # naive datetimes are interpreted in local time
  224. self.assertEqual(event.dt, dt.replace(microsecond=0, tzinfo=EAT))
  225. def test_aware_datetime_in_local_timezone(self):
  226. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
  227. Event.objects.create(dt=dt)
  228. event = Event.objects.get()
  229. self.assertEqual(event.dt, dt)
  230. @skipUnlessDBFeature('supports_microsecond_precision')
  231. def test_aware_datetime_in_local_timezone_with_microsecond(self):
  232. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060, tzinfo=EAT)
  233. Event.objects.create(dt=dt)
  234. event = Event.objects.get()
  235. self.assertEqual(event.dt, dt)
  236. @skipIfDBFeature('supports_microsecond_precision')
  237. def test_aware_datetime_in_local_timezone_with_microsecond_unsupported(self):
  238. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060, tzinfo=EAT)
  239. Event.objects.create(dt=dt)
  240. event = Event.objects.get()
  241. # microseconds are lost during a round-trip in the database
  242. self.assertEqual(event.dt, dt.replace(microsecond=0))
  243. def test_aware_datetime_in_utc(self):
  244. dt = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)
  245. Event.objects.create(dt=dt)
  246. event = Event.objects.get()
  247. self.assertEqual(event.dt, dt)
  248. def test_aware_datetime_in_other_timezone(self):
  249. dt = datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT)
  250. Event.objects.create(dt=dt)
  251. event = Event.objects.get()
  252. self.assertEqual(event.dt, dt)
  253. def test_auto_now_and_auto_now_add(self):
  254. now = datetime.datetime.utcnow().replace(tzinfo=UTC)
  255. past = now - datetime.timedelta(seconds=2)
  256. future = now + datetime.timedelta(seconds=2)
  257. Timestamp.objects.create()
  258. ts = Timestamp.objects.get()
  259. self.assertLess(past, ts.created)
  260. self.assertLess(past, ts.updated)
  261. self.assertGreater(future, ts.updated)
  262. self.assertGreater(future, ts.updated)
  263. def test_query_filter(self):
  264. dt1 = datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=EAT)
  265. dt2 = datetime.datetime(2011, 9, 1, 14, 20, 30, tzinfo=EAT)
  266. Event.objects.create(dt=dt1)
  267. Event.objects.create(dt=dt2)
  268. self.assertEqual(Event.objects.filter(dt__gte=dt1).count(), 2)
  269. self.assertEqual(Event.objects.filter(dt__gt=dt1).count(), 1)
  270. self.assertEqual(Event.objects.filter(dt__gte=dt2).count(), 1)
  271. self.assertEqual(Event.objects.filter(dt__gt=dt2).count(), 0)
  272. @skipIf(pytz is None, "this test requires pytz")
  273. def test_query_filter_with_pytz_timezones(self):
  274. tz = pytz.timezone('Europe/Paris')
  275. dt = datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=tz)
  276. Event.objects.create(dt=dt)
  277. next = dt + datetime.timedelta(seconds=3)
  278. prev = dt - datetime.timedelta(seconds=3)
  279. self.assertEqual(Event.objects.filter(dt__exact=dt).count(), 1)
  280. self.assertEqual(Event.objects.filter(dt__exact=next).count(), 0)
  281. self.assertEqual(Event.objects.filter(dt__in=(prev, next)).count(), 0)
  282. self.assertEqual(Event.objects.filter(dt__in=(prev, dt, next)).count(), 1)
  283. self.assertEqual(Event.objects.filter(dt__range=(prev, next)).count(), 1)
  284. def test_query_date_related_filters(self):
  285. # These two dates fall in the same day in EAT, but in different days,
  286. # years and months in UTC, and aggregation is performed in UTC when
  287. # time zone support is enabled. This test could be changed if the
  288. # implementation is changed to perform the aggregation is local time.
  289. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT))
  290. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT))
  291. self.assertEqual(Event.objects.filter(dt__year=2011).count(), 1)
  292. self.assertEqual(Event.objects.filter(dt__month=1).count(), 1)
  293. self.assertEqual(Event.objects.filter(dt__day=1).count(), 1)
  294. self.assertEqual(Event.objects.filter(dt__week_day=7).count(), 1)
  295. def test_query_aggregation(self):
  296. # Only min and max make sense for datetimes.
  297. Event.objects.create(dt=datetime.datetime(2011, 9, 1, 23, 20, 20, tzinfo=EAT))
  298. Event.objects.create(dt=datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT))
  299. Event.objects.create(dt=datetime.datetime(2011, 9, 1, 3, 20, 40, tzinfo=EAT))
  300. result = Event.objects.all().aggregate(Min('dt'), Max('dt'))
  301. self.assertEqual(result, {
  302. 'dt__min': datetime.datetime(2011, 9, 1, 3, 20, 40, tzinfo=EAT),
  303. 'dt__max': datetime.datetime(2011, 9, 1, 23, 20, 20, tzinfo=EAT),
  304. })
  305. def test_query_dates(self):
  306. # Same comment as in test_query_date_related_filters.
  307. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT))
  308. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT))
  309. self.assertQuerysetEqual(Event.objects.dates('dt', 'year'),
  310. [datetime.datetime(2010, 1, 1, tzinfo=UTC),
  311. datetime.datetime(2011, 1, 1, tzinfo=UTC)],
  312. transform=lambda d: d)
  313. self.assertQuerysetEqual(Event.objects.dates('dt', 'month'),
  314. [datetime.datetime(2010, 12, 1, tzinfo=UTC),
  315. datetime.datetime(2011, 1, 1, tzinfo=UTC)],
  316. transform=lambda d: d)
  317. self.assertQuerysetEqual(Event.objects.dates('dt', 'day'),
  318. [datetime.datetime(2010, 12, 31, tzinfo=UTC),
  319. datetime.datetime(2011, 1, 1, tzinfo=UTC)],
  320. transform=lambda d: d)
  321. NewDatabaseTests = override_settings(USE_TZ=True)(NewDatabaseTests)
  322. class SerializationTests(BaseDateTimeTests):
  323. # Backend-specific notes:
  324. # - JSON supports only milliseconds, microseconds will be truncated.
  325. # - PyYAML dumps the UTC offset correctly for timezone-aware datetimes,
  326. # but when it loads this representation, it substracts the offset and
  327. # returns a naive datetime object in UTC (http://pyyaml.org/ticket/202).
  328. # Tests are adapted to take these quirks into account.
  329. def test_naive_datetime(self):
  330. dt = datetime.datetime(2011, 9, 1, 13, 20, 30)
  331. data = serializers.serialize('python', [Event(dt=dt)])
  332. self.assertEqual(data[0]['fields']['dt'], dt)
  333. obj = serializers.deserialize('python', data).next().object
  334. self.assertEqual(obj.dt, dt)
  335. data = serializers.serialize('json', [Event(dt=dt)])
  336. self.assertIn('"fields": {"dt": "2011-09-01T13:20:30"}', data)
  337. obj = serializers.deserialize('json', data).next().object
  338. self.assertEqual(obj.dt, dt)
  339. data = serializers.serialize('xml', [Event(dt=dt)])
  340. self.assertIn('<field type="DateTimeField" name="dt">2011-09-01T13:20:30</field>', data)
  341. obj = serializers.deserialize('xml', data).next().object
  342. self.assertEqual(obj.dt, dt)
  343. if 'yaml' in serializers.get_serializer_formats():
  344. data = serializers.serialize('yaml', [Event(dt=dt)])
  345. self.assertIn("- fields: {dt: !!timestamp '2011-09-01 13:20:30'}", data)
  346. obj = serializers.deserialize('yaml', data).next().object
  347. self.assertEqual(obj.dt, dt)
  348. def test_naive_datetime_with_microsecond(self):
  349. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060)
  350. data = serializers.serialize('python', [Event(dt=dt)])
  351. self.assertEqual(data[0]['fields']['dt'], dt)
  352. obj = serializers.deserialize('python', data).next().object
  353. self.assertEqual(obj.dt, dt)
  354. data = serializers.serialize('json', [Event(dt=dt)])
  355. self.assertIn('"fields": {"dt": "2011-09-01T13:20:30.405"}', data)
  356. obj = serializers.deserialize('json', data).next().object
  357. self.assertEqual(obj.dt, dt.replace(microsecond=405000))
  358. data = serializers.serialize('xml', [Event(dt=dt)])
  359. self.assertIn('<field type="DateTimeField" name="dt">2011-09-01T13:20:30.405060</field>', data)
  360. obj = serializers.deserialize('xml', data).next().object
  361. self.assertEqual(obj.dt, dt)
  362. if 'yaml' in serializers.get_serializer_formats():
  363. data = serializers.serialize('yaml', [Event(dt=dt)])
  364. self.assertIn("- fields: {dt: !!timestamp '2011-09-01 13:20:30.405060'}", data)
  365. obj = serializers.deserialize('yaml', data).next().object
  366. self.assertEqual(obj.dt, dt)
  367. def test_aware_datetime_with_microsecond(self):
  368. dt = datetime.datetime(2011, 9, 1, 17, 20, 30, 405060, tzinfo=ICT)
  369. data = serializers.serialize('python', [Event(dt=dt)])
  370. self.assertEqual(data[0]['fields']['dt'], dt)
  371. obj = serializers.deserialize('python', data).next().object
  372. self.assertEqual(obj.dt, dt)
  373. data = serializers.serialize('json', [Event(dt=dt)])
  374. self.assertIn('"fields": {"dt": "2011-09-01T17:20:30.405+07:00"}', data)
  375. obj = serializers.deserialize('json', data).next().object
  376. self.assertEqual(obj.dt, dt.replace(microsecond=405000))
  377. data = serializers.serialize('xml', [Event(dt=dt)])
  378. self.assertIn('<field type="DateTimeField" name="dt">2011-09-01T17:20:30.405060+07:00</field>', data)
  379. obj = serializers.deserialize('xml', data).next().object
  380. self.assertEqual(obj.dt, dt)
  381. if 'yaml' in serializers.get_serializer_formats():
  382. data = serializers.serialize('yaml', [Event(dt=dt)])
  383. self.assertIn("- fields: {dt: !!timestamp '2011-09-01 17:20:30.405060+07:00'}", data)
  384. obj = serializers.deserialize('yaml', data).next().object
  385. self.assertEqual(obj.dt.replace(tzinfo=UTC), dt)
  386. def test_aware_datetime_in_utc(self):
  387. dt = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)
  388. data = serializers.serialize('python', [Event(dt=dt)])
  389. self.assertEqual(data[0]['fields']['dt'], dt)
  390. obj = serializers.deserialize('python', data).next().object
  391. self.assertEqual(obj.dt, dt)
  392. data = serializers.serialize('json', [Event(dt=dt)])
  393. self.assertIn('"fields": {"dt": "2011-09-01T10:20:30Z"}', data)
  394. obj = serializers.deserialize('json', data).next().object
  395. self.assertEqual(obj.dt, dt)
  396. data = serializers.serialize('xml', [Event(dt=dt)])
  397. self.assertIn('<field type="DateTimeField" name="dt">2011-09-01T10:20:30+00:00</field>', data)
  398. obj = serializers.deserialize('xml', data).next().object
  399. self.assertEqual(obj.dt, dt)
  400. if 'yaml' in serializers.get_serializer_formats():
  401. data = serializers.serialize('yaml', [Event(dt=dt)])
  402. self.assertIn("- fields: {dt: !!timestamp '2011-09-01 10:20:30+00:00'}", data)
  403. obj = serializers.deserialize('yaml', data).next().object
  404. self.assertEqual(obj.dt.replace(tzinfo=UTC), dt)
  405. def test_aware_datetime_in_local_timezone(self):
  406. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
  407. data = serializers.serialize('python', [Event(dt=dt)])
  408. self.assertEqual(data[0]['fields']['dt'], dt)
  409. obj = serializers.deserialize('python', data).next().object
  410. self.assertEqual(obj.dt, dt)
  411. data = serializers.serialize('json', [Event(dt=dt)])
  412. self.assertIn('"fields": {"dt": "2011-09-01T13:20:30+03:00"}', data)
  413. obj = serializers.deserialize('json', data).next().object
  414. self.assertEqual(obj.dt, dt)
  415. data = serializers.serialize('xml', [Event(dt=dt)])
  416. self.assertIn('<field type="DateTimeField" name="dt">2011-09-01T13:20:30+03:00</field>', data)
  417. obj = serializers.deserialize('xml', data).next().object
  418. self.assertEqual(obj.dt, dt)
  419. if 'yaml' in serializers.get_serializer_formats():
  420. data = serializers.serialize('yaml', [Event(dt=dt)])
  421. self.assertIn("- fields: {dt: !!timestamp '2011-09-01 13:20:30+03:00'}", data)
  422. obj = serializers.deserialize('yaml', data).next().object
  423. self.assertEqual(obj.dt.replace(tzinfo=UTC), dt)
  424. def test_aware_datetime_in_other_timezone(self):
  425. dt = datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT)
  426. data = serializers.serialize('python', [Event(dt=dt)])
  427. self.assertEqual(data[0]['fields']['dt'], dt)
  428. obj = serializers.deserialize('python', data).next().object
  429. self.assertEqual(obj.dt, dt)
  430. data = serializers.serialize('json', [Event(dt=dt)])
  431. self.assertIn('"fields": {"dt": "2011-09-01T17:20:30+07:00"}', data)
  432. obj = serializers.deserialize('json', data).next().object
  433. self.assertEqual(obj.dt, dt)
  434. data = serializers.serialize('xml', [Event(dt=dt)])
  435. self.assertIn('<field type="DateTimeField" name="dt">2011-09-01T17:20:30+07:00</field>', data)
  436. obj = serializers.deserialize('xml', data).next().object
  437. self.assertEqual(obj.dt, dt)
  438. if 'yaml' in serializers.get_serializer_formats():
  439. data = serializers.serialize('yaml', [Event(dt=dt)])
  440. self.assertIn("- fields: {dt: !!timestamp '2011-09-01 17:20:30+07:00'}", data)
  441. obj = serializers.deserialize('yaml', data).next().object
  442. self.assertEqual(obj.dt.replace(tzinfo=UTC), dt)
  443. #@override_settings(DATETIME_FORMAT='c', USE_L10N=False, USE_TZ=True)
  444. class TemplateTests(BaseDateTimeTests):
  445. def test_localtime_templatetag_and_filters(self):
  446. """
  447. Test the {% localtime %} templatetag and related filters.
  448. """
  449. datetimes = {
  450. 'utc': datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC),
  451. 'eat': datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT),
  452. 'ict': datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT),
  453. 'naive': datetime.datetime(2011, 9, 1, 13, 20, 30),
  454. }
  455. templates = {
  456. 'notag': Template("{% load tz %}{{ dt }}|{{ dt|aslocaltime }}|{{ dt|asutc }}|{{ dt|astimezone:ICT }}"),
  457. 'noarg': Template("{% load tz %}{% localtime %}{{ dt }}|{{ dt|aslocaltime }}|{{ dt|asutc }}|{{ dt|astimezone:ICT }}{% endlocaltime %}"),
  458. 'on': Template("{% load tz %}{% localtime on %}{{ dt }}|{{ dt|aslocaltime }}|{{ dt|asutc }}|{{ dt|astimezone:ICT }}{% endlocaltime %}"),
  459. 'off': Template("{% load tz %}{% localtime off %}{{ dt }}|{{ dt|aslocaltime }}|{{ dt|asutc }}|{{ dt|astimezone:ICT }}{% endlocaltime %}"),
  460. }
  461. # Transform a list of keys in 'datetimes' to the expected template
  462. # output. This makes the definition of 'results' more readable.
  463. def t(*result):
  464. return '|'.join(datetimes[key].isoformat() for key in result)
  465. # Results for USE_TZ = True
  466. results = {
  467. 'utc': {
  468. 'notag': t('eat', 'eat', 'utc', 'ict'),
  469. 'noarg': t('eat', 'eat', 'utc', 'ict'),
  470. 'on': t('eat', 'eat', 'utc', 'ict'),
  471. 'off': t('utc', 'eat', 'utc', 'ict'),
  472. },
  473. 'eat': {
  474. 'notag': t('eat', 'eat', 'utc', 'ict'),
  475. 'noarg': t('eat', 'eat', 'utc', 'ict'),
  476. 'on': t('eat', 'eat', 'utc', 'ict'),
  477. 'off': t('eat', 'eat', 'utc', 'ict'),
  478. },
  479. 'ict': {
  480. 'notag': t('eat', 'eat', 'utc', 'ict'),
  481. 'noarg': t('eat', 'eat', 'utc', 'ict'),
  482. 'on': t('eat', 'eat', 'utc', 'ict'),
  483. 'off': t('ict', 'eat', 'utc', 'ict'),
  484. },
  485. 'naive': {
  486. 'notag': t('naive', 'eat', 'utc', 'ict'),
  487. 'noarg': t('naive', 'eat', 'utc', 'ict'),
  488. 'on': t('naive', 'eat', 'utc', 'ict'),
  489. 'off': t('naive', 'eat', 'utc', 'ict'),
  490. }
  491. }
  492. for k1, dt in datetimes.iteritems():
  493. for k2, tpl in templates.iteritems():
  494. ctx = Context({'dt': dt, 'ICT': ICT})
  495. actual = tpl.render(ctx)
  496. expected = results[k1][k2]
  497. self.assertEqual(actual, expected, '%s / %s: %r != %r' % (k1, k2, actual, expected))
  498. # Changes for USE_TZ = False
  499. results['utc']['notag'] = t('utc', 'eat', 'utc', 'ict')
  500. results['ict']['notag'] = t('ict', 'eat', 'utc', 'ict')
  501. with self.settings(USE_TZ=False):
  502. for k1, dt in datetimes.iteritems():
  503. for k2, tpl in templates.iteritems():
  504. ctx = Context({'dt': dt, 'ICT': ICT})
  505. actual = tpl.render(ctx)
  506. expected = results[k1][k2]
  507. self.assertEqual(actual, expected, '%s / %s: %r != %r' % (k1, k2, actual, expected))
  508. @skipIf(pytz is None, "this test requires pytz")
  509. def test_localtime_filters_with_pytz(self):
  510. """
  511. Test the |aslocaltime, |asutc, and |astimezone filters with pytz.
  512. """
  513. # Use a pytz timezone as local time
  514. tpl = Template("{% load tz %}{{ dt|aslocaltime }}|{{ dt|asutc }}")
  515. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 12, 20, 30)})
  516. timezone._localtime = None
  517. with self.settings(TIME_ZONE='Europe/Paris'):
  518. self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00|2011-09-01T10:20:30+00:00")
  519. timezone._localtime = None
  520. # Use a pytz timezone as argument
  521. tpl = Template("{% load tz %}{{ dt|astimezone:tz }}")
  522. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30),
  523. 'tz': pytz.timezone('Europe/Paris')})
  524. self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
  525. # Use a pytz timezone name as argument
  526. tpl = Template("{% load tz %}{{ dt|astimezone:'Europe/Paris' }}")
  527. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30),
  528. 'tz': pytz.timezone('Europe/Paris')})
  529. self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
  530. def test_localtime_templatetag_invalid_argument(self):
  531. with self.assertRaises(TemplateSyntaxError):
  532. Template("{% load tz %}{% localtime foo %}{% endlocaltime %}").render()
  533. def test_localtime_filters_do_not_raise_exceptions(self):
  534. """
  535. Test the |aslocaltime, |asutc, and |astimezone filters on bad inputs.
  536. """
  537. tpl = Template("{% load tz %}{{ dt }}|{{ dt|aslocaltime }}|{{ dt|asutc }}|{{ dt|astimezone:tz }}")
  538. with self.settings(USE_TZ=True):
  539. # bad datetime value
  540. ctx = Context({'dt': None, 'tz': ICT})
  541. self.assertEqual(tpl.render(ctx), "None|||")
  542. ctx = Context({'dt': 'not a date', 'tz': ICT})
  543. self.assertEqual(tpl.render(ctx), "not a date|||")
  544. # bad timezone value
  545. tpl = Template("{% load tz %}{{ dt|astimezone:tz }}")
  546. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30), 'tz': None})
  547. self.assertEqual(tpl.render(ctx), "")
  548. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30), 'tz': 'not a tz'})
  549. self.assertEqual(tpl.render(ctx), "")
  550. def test_timezone_templatetag(self):
  551. """
  552. Test the {% timezone %} templatetag.
  553. """
  554. tpl = Template("{% load tz %}"
  555. "{{ dt }}|"
  556. "{% timezone tz1 %}"
  557. "{{ dt }}|"
  558. "{% timezone tz2 %}"
  559. "{{ dt }}"
  560. "{% endtimezone %}"
  561. "{% endtimezone %}")
  562. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC),
  563. 'tz1': ICT, 'tz2': None})
  564. 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")
  565. @skipIf(pytz is None, "this test requires pytz")
  566. def test_timezone_templatetag_with_pytz(self):
  567. """
  568. Test the {% timezone %} templatetag with pytz.
  569. """
  570. tpl = Template("{% load tz %}{% timezone tz %}{{ dt }}{% endtimezone %}")
  571. # Use a pytz timezone as argument
  572. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT),
  573. 'tz': pytz.timezone('Europe/Paris')})
  574. self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
  575. # Use a pytz timezone name as argument
  576. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT),
  577. 'tz': 'Europe/Paris'})
  578. self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
  579. def test_timezone_templatetag_invalid_argument(self):
  580. with self.assertRaises(TemplateSyntaxError):
  581. Template("{% load tz %}{% timezone %}{% endtimezone %}").render()
  582. with self.assertRaises(ValueError if pytz is None else pytz.UnknownTimeZoneError):
  583. Template("{% load tz %}{% timezone tz %}{% endtimezone %}").render(Context({'tz': 'foobar'}))
  584. def test_get_current_timezone_templatetag(self):
  585. """
  586. Test the {% get_current_timezone %} templatetag.
  587. """
  588. tpl = Template("{% load tz %}{% get_current_timezone as time_zone %}{{ time_zone }}")
  589. self.assertEqual(tpl.render(Context()), "Africa/Nairobi" if pytz else "EAT")
  590. with timezone.override(UTC):
  591. self.assertEqual(tpl.render(Context()), "UTC")
  592. tpl = Template("{% load tz %}{% timezone tz %}{% get_current_timezone as time_zone %}{% endtimezone %}{{ time_zone }}")
  593. self.assertEqual(tpl.render(Context({'tz': ICT})), "+0700")
  594. with timezone.override(UTC):
  595. self.assertEqual(tpl.render(Context({'tz': ICT})), "+0700")
  596. @skipIf(pytz is None, "this test requires pytz")
  597. def test_get_current_timezone_templatetag_with_pytz(self):
  598. """
  599. Test the {% get_current_timezone %} templatetag with pytz.
  600. """
  601. tpl = Template("{% load tz %}{% get_current_timezone as time_zone %}{{ time_zone }}")
  602. with timezone.override(pytz.timezone('Europe/Paris')):
  603. self.assertEqual(tpl.render(Context()), "Europe/Paris")
  604. tpl = Template("{% load tz %}{% timezone 'Europe/Paris' %}{% get_current_timezone as time_zone %}{% endtimezone %}{{ time_zone }}")
  605. self.assertEqual(tpl.render(Context()), "Europe/Paris")
  606. def test_get_current_timezone_templatetag_invalid_argument(self):
  607. with self.assertRaises(TemplateSyntaxError):
  608. Template("{% load tz %}{% get_current_timezone %}").render()
  609. def test_tz_template_context_processor(self):
  610. """
  611. Test the django.core.context_processors.tz template context processor.
  612. """
  613. tpl = Template("{{ TIME_ZONE }}")
  614. self.assertEqual(tpl.render(Context()), "")
  615. self.assertEqual(tpl.render(RequestContext(HttpRequest())), "Africa/Nairobi" if pytz else "EAT")
  616. def test_date_and_time_template_filters(self):
  617. tpl = Template("{{ dt|date:'Y-m-d' }} at {{ dt|time:'H:i:s' }}")
  618. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 20, 20, 20, tzinfo=UTC)})
  619. self.assertEqual(tpl.render(ctx), "2011-09-01 at 23:20:20")
  620. with timezone.override(ICT):
  621. self.assertEqual(tpl.render(ctx), "2011-09-02 at 03:20:20")
  622. def test_date_and_time_template_filters_honor_localtime(self):
  623. tpl = Template("{% load tz %}{% localtime off %}{{ dt|date:'Y-m-d' }} at {{ dt|time:'H:i:s' }}{% endlocaltime %}")
  624. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 20, 20, 20, tzinfo=UTC)})
  625. self.assertEqual(tpl.render(ctx), "2011-09-01 at 20:20:20")
  626. with timezone.override(ICT):
  627. self.assertEqual(tpl.render(ctx), "2011-09-01 at 20:20:20")
  628. TemplateTests = override_settings(DATETIME_FORMAT='c', USE_L10N=False, USE_TZ=True)(TemplateTests)
  629. #@override_settings(DATETIME_FORMAT='c', USE_L10N=False, USE_TZ=False)
  630. class LegacyFormsTests(BaseDateTimeTests):
  631. def test_form(self):
  632. form = EventForm({'dt': u'2011-09-01 13:20:30'})
  633. self.assertTrue(form.is_valid())
  634. self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 13, 20, 30))
  635. @skipIf(pytz is None, "this test requires pytz")
  636. def test_form_with_non_existent_time(self):
  637. form = EventForm({'dt': u'2011-03-27 02:30:00'})
  638. with timezone.override(pytz.timezone('Europe/Paris')):
  639. # this is obviously a bug
  640. self.assertTrue(form.is_valid())
  641. self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 3, 27, 2, 30, 0))
  642. @skipIf(pytz is None, "this test requires pytz")
  643. def test_form_with_ambiguous_time(self):
  644. form = EventForm({'dt': u'2011-10-30 02:30:00'})
  645. with timezone.override(pytz.timezone('Europe/Paris')):
  646. # this is obviously a bug
  647. self.assertTrue(form.is_valid())
  648. self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 10, 30, 2, 30, 0))
  649. def test_split_form(self):
  650. form = EventSplitForm({'dt_0': u'2011-09-01', 'dt_1': u'13:20:30'})
  651. self.assertTrue(form.is_valid())
  652. self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 13, 20, 30))
  653. def test_model_form(self):
  654. EventModelForm({'dt': u'2011-09-01 13:20:30'}).save()
  655. e = Event.objects.get()
  656. self.assertEqual(e.dt, datetime.datetime(2011, 9, 1, 13, 20, 30))
  657. LegacyFormsTests = override_settings(DATETIME_FORMAT='c', USE_L10N=False, USE_TZ=False)(LegacyFormsTests)
  658. #@override_settings(DATETIME_FORMAT='c', USE_L10N=False, USE_TZ=True)
  659. class NewFormsTests(BaseDateTimeTests):
  660. def test_form(self):
  661. form = EventForm({'dt': u'2011-09-01 13:20:30'})
  662. self.assertTrue(form.is_valid())
  663. self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
  664. def test_form_with_other_timezone(self):
  665. form = EventForm({'dt': u'2011-09-01 17:20:30'})
  666. with timezone.override(ICT):
  667. self.assertTrue(form.is_valid())
  668. self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
  669. @skipIf(pytz is None, "this test requires pytz")
  670. def test_form_with_non_existent_time(self):
  671. with timezone.override(pytz.timezone('Europe/Paris')):
  672. form = EventForm({'dt': u'2011-03-27 02:30:00'})
  673. self.assertFalse(form.is_valid())
  674. self.assertEqual(form.errors['dt'],
  675. [u"2011-03-27 02:30:00 couldn't be interpreted in time zone "
  676. u"Europe/Paris; it may be ambiguous or it may not exist."])
  677. @skipIf(pytz is None, "this test requires pytz")
  678. def test_form_with_ambiguous_time(self):
  679. with timezone.override(pytz.timezone('Europe/Paris')):
  680. form = EventForm({'dt': u'2011-10-30 02:30:00'})
  681. self.assertFalse(form.is_valid())
  682. self.assertEqual(form.errors['dt'],
  683. [u"2011-10-30 02:30:00 couldn't be interpreted in time zone "
  684. u"Europe/Paris; it may be ambiguous or it may not exist."])
  685. def test_split_form(self):
  686. form = EventSplitForm({'dt_0': u'2011-09-01', 'dt_1': u'13:20:30'})
  687. self.assertTrue(form.is_valid())
  688. self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
  689. def test_model_form(self):
  690. EventModelForm({'dt': u'2011-09-01 13:20:30'}).save()
  691. e = Event.objects.get()
  692. self.assertEqual(e.dt, datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
  693. NewFormsTests = override_settings(DATETIME_FORMAT='c', USE_L10N=False, USE_TZ=True)(NewFormsTests)
  694. #@override_settings(DATETIME_FORMAT='c', USE_L10N=False, USE_TZ=True)
  695. class AdminTests(BaseDateTimeTests):
  696. urls = 'modeltests.timezones.urls'
  697. fixtures = ['users.xml']
  698. def setUp(self):
  699. self.client.login(username='super', password='secret')
  700. def test_changelist(self):
  701. e = Event.objects.create(dt=datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
  702. response = self.client.get(reverse('admin:timezones_event_changelist'))
  703. self.assertContains(response, e.dt.astimezone(EAT).isoformat())
  704. def test_changelist_in_other_timezone(self):
  705. e = Event.objects.create(dt=datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
  706. with timezone.override(ICT):
  707. response = self.client.get(reverse('admin:timezones_event_changelist'))
  708. self.assertContains(response, e.dt.astimezone(ICT).isoformat())
  709. def test_change_editable(self):
  710. e = Event.objects.create(dt=datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
  711. response = self.client.get(reverse('admin:timezones_event_change', args=(e.pk,)))
  712. self.assertContains(response, e.dt.astimezone(EAT).date().isoformat())
  713. self.assertContains(response, e.dt.astimezone(EAT).time().isoformat())
  714. def test_change_editable_in_other_timezone(self):
  715. e = Event.objects.create(dt=datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
  716. with timezone.override(ICT):
  717. response = self.client.get(reverse('admin:timezones_event_change', args=(e.pk,)))
  718. self.assertContains(response, e.dt.astimezone(ICT).date().isoformat())
  719. self.assertContains(response, e.dt.astimezone(ICT).time().isoformat())
  720. def test_change_readonly(self):
  721. Timestamp.objects.create()
  722. # re-fetch the object for backends that lose microseconds (MySQL)
  723. t = Timestamp.objects.get()
  724. response = self.client.get(reverse('admin:timezones_timestamp_change', args=(t.pk,)))
  725. self.assertContains(response, t.created.astimezone(EAT).isoformat())
  726. def test_change_readonly_in_other_timezone(self):
  727. Timestamp.objects.create()
  728. # re-fetch the object for backends that lose microseconds (MySQL)
  729. t = Timestamp.objects.get()
  730. with timezone.override(ICT):
  731. response = self.client.get(reverse('admin:timezones_timestamp_change', args=(t.pk,)))
  732. self.assertContains(response, t.created.astimezone(ICT).isoformat())
  733. AdminTests = override_settings(DATETIME_FORMAT='c', USE_L10N=False, USE_TZ=True)(AdminTests)