tests.py 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185
  1. import datetime
  2. import re
  3. import sys
  4. from contextlib import contextmanager
  5. from unittest import SkipTest, skipIf
  6. from xml.dom.minidom import parseString
  7. import pytz
  8. from django.contrib.auth.models import User
  9. from django.core import serializers
  10. from django.core.exceptions import ImproperlyConfigured
  11. from django.db import connection, connections
  12. from django.db.models import F, Max, Min
  13. from django.http import HttpRequest
  14. from django.template import (
  15. Context, RequestContext, Template, TemplateSyntaxError, context_processors,
  16. )
  17. from django.test import (
  18. SimpleTestCase, TestCase, TransactionTestCase, override_settings,
  19. skipIfDBFeature, skipUnlessDBFeature,
  20. )
  21. from django.test.utils import requires_tz_support
  22. from django.urls import reverse
  23. from django.utils import timezone
  24. from django.utils.timezone import timedelta
  25. from .forms import (
  26. EventForm, EventLocalizedForm, EventLocalizedModelForm, EventModelForm,
  27. EventSplitForm,
  28. )
  29. from .models import (
  30. AllDayEvent, Event, MaybeEvent, Session, SessionEvent, Timestamp,
  31. )
  32. # These tests use the EAT (Eastern Africa Time) and ICT (Indochina Time)
  33. # who don't have Daylight Saving Time, so we can represent them easily
  34. # with fixed offset timezones and use them directly as tzinfo in the
  35. # constructors.
  36. # settings.TIME_ZONE is forced to EAT. Most tests use a variant of
  37. # datetime.datetime(2011, 9, 1, 13, 20, 30), which translates to
  38. # 10:20:30 in UTC and 17:20:30 in ICT.
  39. UTC = timezone.utc
  40. EAT = timezone.get_fixed_timezone(180) # Africa/Nairobi
  41. ICT = timezone.get_fixed_timezone(420) # Asia/Bangkok
  42. @override_settings(TIME_ZONE='Africa/Nairobi', USE_TZ=False)
  43. class LegacyDatabaseTests(TestCase):
  44. def test_naive_datetime(self):
  45. dt = datetime.datetime(2011, 9, 1, 13, 20, 30)
  46. Event.objects.create(dt=dt)
  47. event = Event.objects.get()
  48. self.assertEqual(event.dt, dt)
  49. def test_naive_datetime_with_microsecond(self):
  50. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060)
  51. Event.objects.create(dt=dt)
  52. event = Event.objects.get()
  53. self.assertEqual(event.dt, dt)
  54. @skipUnlessDBFeature('supports_timezones')
  55. def test_aware_datetime_in_local_timezone(self):
  56. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
  57. Event.objects.create(dt=dt)
  58. event = Event.objects.get()
  59. self.assertIsNone(event.dt.tzinfo)
  60. # interpret the naive datetime in local time to get the correct value
  61. self.assertEqual(event.dt.replace(tzinfo=EAT), dt)
  62. @skipUnlessDBFeature('supports_timezones')
  63. def test_aware_datetime_in_local_timezone_with_microsecond(self):
  64. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060, tzinfo=EAT)
  65. Event.objects.create(dt=dt)
  66. event = Event.objects.get()
  67. self.assertIsNone(event.dt.tzinfo)
  68. # interpret the naive datetime in local time to get the correct value
  69. self.assertEqual(event.dt.replace(tzinfo=EAT), dt)
  70. @skipUnlessDBFeature('supports_timezones')
  71. def test_aware_datetime_in_utc(self):
  72. dt = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)
  73. Event.objects.create(dt=dt)
  74. event = Event.objects.get()
  75. self.assertIsNone(event.dt.tzinfo)
  76. # interpret the naive datetime in local time to get the correct value
  77. self.assertEqual(event.dt.replace(tzinfo=EAT), dt)
  78. @skipUnlessDBFeature('supports_timezones')
  79. def test_aware_datetime_in_other_timezone(self):
  80. dt = datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT)
  81. Event.objects.create(dt=dt)
  82. event = Event.objects.get()
  83. self.assertIsNone(event.dt.tzinfo)
  84. # interpret the naive datetime in local time to get the correct value
  85. self.assertEqual(event.dt.replace(tzinfo=EAT), dt)
  86. @skipIfDBFeature('supports_timezones')
  87. def test_aware_datetime_unsupported(self):
  88. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
  89. msg = 'backend does not support timezone-aware datetimes when USE_TZ is False.'
  90. with self.assertRaisesMessage(ValueError, msg):
  91. Event.objects.create(dt=dt)
  92. def test_auto_now_and_auto_now_add(self):
  93. now = datetime.datetime.now()
  94. past = now - datetime.timedelta(seconds=2)
  95. future = now + datetime.timedelta(seconds=2)
  96. Timestamp.objects.create()
  97. ts = Timestamp.objects.get()
  98. self.assertLess(past, ts.created)
  99. self.assertLess(past, ts.updated)
  100. self.assertGreater(future, ts.updated)
  101. self.assertGreater(future, ts.updated)
  102. def test_query_filter(self):
  103. dt1 = datetime.datetime(2011, 9, 1, 12, 20, 30)
  104. dt2 = datetime.datetime(2011, 9, 1, 14, 20, 30)
  105. Event.objects.create(dt=dt1)
  106. Event.objects.create(dt=dt2)
  107. self.assertEqual(Event.objects.filter(dt__gte=dt1).count(), 2)
  108. self.assertEqual(Event.objects.filter(dt__gt=dt1).count(), 1)
  109. self.assertEqual(Event.objects.filter(dt__gte=dt2).count(), 1)
  110. self.assertEqual(Event.objects.filter(dt__gt=dt2).count(), 0)
  111. def test_query_datetime_lookups(self):
  112. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0))
  113. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0))
  114. self.assertEqual(Event.objects.filter(dt__year=2011).count(), 2)
  115. self.assertEqual(Event.objects.filter(dt__month=1).count(), 2)
  116. self.assertEqual(Event.objects.filter(dt__day=1).count(), 2)
  117. self.assertEqual(Event.objects.filter(dt__week_day=7).count(), 2)
  118. self.assertEqual(Event.objects.filter(dt__hour=1).count(), 1)
  119. self.assertEqual(Event.objects.filter(dt__minute=30).count(), 2)
  120. self.assertEqual(Event.objects.filter(dt__second=0).count(), 2)
  121. def test_query_aggregation(self):
  122. # Only min and max make sense for datetimes.
  123. Event.objects.create(dt=datetime.datetime(2011, 9, 1, 23, 20, 20))
  124. Event.objects.create(dt=datetime.datetime(2011, 9, 1, 13, 20, 30))
  125. Event.objects.create(dt=datetime.datetime(2011, 9, 1, 3, 20, 40))
  126. result = Event.objects.all().aggregate(Min('dt'), Max('dt'))
  127. self.assertEqual(result, {
  128. 'dt__min': datetime.datetime(2011, 9, 1, 3, 20, 40),
  129. 'dt__max': datetime.datetime(2011, 9, 1, 23, 20, 20),
  130. })
  131. def test_query_annotation(self):
  132. # Only min and max make sense for datetimes.
  133. morning = Session.objects.create(name='morning')
  134. afternoon = Session.objects.create(name='afternoon')
  135. SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 23, 20, 20), session=afternoon)
  136. SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 13, 20, 30), session=afternoon)
  137. SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 3, 20, 40), session=morning)
  138. morning_min_dt = datetime.datetime(2011, 9, 1, 3, 20, 40)
  139. afternoon_min_dt = datetime.datetime(2011, 9, 1, 13, 20, 30)
  140. self.assertQuerysetEqual(
  141. Session.objects.annotate(dt=Min('events__dt')).order_by('dt'),
  142. [morning_min_dt, afternoon_min_dt],
  143. transform=lambda d: d.dt,
  144. )
  145. self.assertQuerysetEqual(
  146. Session.objects.annotate(dt=Min('events__dt')).filter(dt__lt=afternoon_min_dt),
  147. [morning_min_dt],
  148. transform=lambda d: d.dt,
  149. )
  150. self.assertQuerysetEqual(
  151. Session.objects.annotate(dt=Min('events__dt')).filter(dt__gte=afternoon_min_dt),
  152. [afternoon_min_dt],
  153. transform=lambda d: d.dt,
  154. )
  155. def test_query_datetimes(self):
  156. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0))
  157. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0))
  158. self.assertSequenceEqual(Event.objects.datetimes('dt', 'year'), [datetime.datetime(2011, 1, 1, 0, 0, 0)])
  159. self.assertSequenceEqual(Event.objects.datetimes('dt', 'month'), [datetime.datetime(2011, 1, 1, 0, 0, 0)])
  160. self.assertSequenceEqual(Event.objects.datetimes('dt', 'day'), [datetime.datetime(2011, 1, 1, 0, 0, 0)])
  161. self.assertSequenceEqual(
  162. Event.objects.datetimes('dt', 'hour'),
  163. [datetime.datetime(2011, 1, 1, 1, 0, 0),
  164. datetime.datetime(2011, 1, 1, 4, 0, 0)]
  165. )
  166. self.assertSequenceEqual(
  167. Event.objects.datetimes('dt', 'minute'),
  168. [datetime.datetime(2011, 1, 1, 1, 30, 0),
  169. datetime.datetime(2011, 1, 1, 4, 30, 0)]
  170. )
  171. self.assertSequenceEqual(
  172. Event.objects.datetimes('dt', 'second'),
  173. [datetime.datetime(2011, 1, 1, 1, 30, 0),
  174. datetime.datetime(2011, 1, 1, 4, 30, 0)]
  175. )
  176. def test_raw_sql(self):
  177. # Regression test for #17755
  178. dt = datetime.datetime(2011, 9, 1, 13, 20, 30)
  179. event = Event.objects.create(dt=dt)
  180. self.assertEqual(list(Event.objects.raw('SELECT * FROM timezones_event WHERE dt = %s', [dt])), [event])
  181. def test_cursor_execute_accepts_naive_datetime(self):
  182. dt = datetime.datetime(2011, 9, 1, 13, 20, 30)
  183. with connection.cursor() as cursor:
  184. cursor.execute('INSERT INTO timezones_event (dt) VALUES (%s)', [dt])
  185. event = Event.objects.get()
  186. self.assertEqual(event.dt, dt)
  187. def test_cursor_execute_returns_naive_datetime(self):
  188. dt = datetime.datetime(2011, 9, 1, 13, 20, 30)
  189. Event.objects.create(dt=dt)
  190. with connection.cursor() as cursor:
  191. cursor.execute('SELECT dt FROM timezones_event WHERE dt = %s', [dt])
  192. self.assertEqual(cursor.fetchall()[0][0], dt)
  193. def test_filter_date_field_with_aware_datetime(self):
  194. # Regression test for #17742
  195. day = datetime.date(2011, 9, 1)
  196. AllDayEvent.objects.create(day=day)
  197. # This is 2011-09-02T01:30:00+03:00 in EAT
  198. dt = datetime.datetime(2011, 9, 1, 22, 30, 0, tzinfo=UTC)
  199. self.assertTrue(AllDayEvent.objects.filter(day__gte=dt).exists())
  200. @override_settings(TIME_ZONE='Africa/Nairobi', USE_TZ=True)
  201. class NewDatabaseTests(TestCase):
  202. naive_warning = 'DateTimeField Event.dt received a naive datetime'
  203. @requires_tz_support
  204. def test_naive_datetime(self):
  205. dt = datetime.datetime(2011, 9, 1, 13, 20, 30)
  206. with self.assertWarnsMessage(RuntimeWarning, self.naive_warning):
  207. Event.objects.create(dt=dt)
  208. event = Event.objects.get()
  209. # naive datetimes are interpreted in local time
  210. self.assertEqual(event.dt, dt.replace(tzinfo=EAT))
  211. @requires_tz_support
  212. def test_datetime_from_date(self):
  213. dt = datetime.date(2011, 9, 1)
  214. with self.assertWarnsMessage(RuntimeWarning, self.naive_warning):
  215. Event.objects.create(dt=dt)
  216. event = Event.objects.get()
  217. self.assertEqual(event.dt, datetime.datetime(2011, 9, 1, tzinfo=EAT))
  218. @requires_tz_support
  219. def test_naive_datetime_with_microsecond(self):
  220. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060)
  221. with self.assertWarnsMessage(RuntimeWarning, self.naive_warning):
  222. Event.objects.create(dt=dt)
  223. event = Event.objects.get()
  224. # naive datetimes are interpreted in local time
  225. self.assertEqual(event.dt, dt.replace(tzinfo=EAT))
  226. def test_aware_datetime_in_local_timezone(self):
  227. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
  228. Event.objects.create(dt=dt)
  229. event = Event.objects.get()
  230. self.assertEqual(event.dt, dt)
  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. def test_aware_datetime_in_utc(self):
  237. dt = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)
  238. Event.objects.create(dt=dt)
  239. event = Event.objects.get()
  240. self.assertEqual(event.dt, dt)
  241. def test_aware_datetime_in_other_timezone(self):
  242. dt = datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT)
  243. Event.objects.create(dt=dt)
  244. event = Event.objects.get()
  245. self.assertEqual(event.dt, dt)
  246. def test_auto_now_and_auto_now_add(self):
  247. now = timezone.now()
  248. past = now - datetime.timedelta(seconds=2)
  249. future = now + datetime.timedelta(seconds=2)
  250. Timestamp.objects.create()
  251. ts = Timestamp.objects.get()
  252. self.assertLess(past, ts.created)
  253. self.assertLess(past, ts.updated)
  254. self.assertGreater(future, ts.updated)
  255. self.assertGreater(future, ts.updated)
  256. def test_query_filter(self):
  257. dt1 = datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=EAT)
  258. dt2 = datetime.datetime(2011, 9, 1, 14, 20, 30, tzinfo=EAT)
  259. Event.objects.create(dt=dt1)
  260. Event.objects.create(dt=dt2)
  261. self.assertEqual(Event.objects.filter(dt__gte=dt1).count(), 2)
  262. self.assertEqual(Event.objects.filter(dt__gt=dt1).count(), 1)
  263. self.assertEqual(Event.objects.filter(dt__gte=dt2).count(), 1)
  264. self.assertEqual(Event.objects.filter(dt__gt=dt2).count(), 0)
  265. def test_query_filter_with_pytz_timezones(self):
  266. tz = pytz.timezone('Europe/Paris')
  267. dt = datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=tz)
  268. Event.objects.create(dt=dt)
  269. next = dt + datetime.timedelta(seconds=3)
  270. prev = dt - datetime.timedelta(seconds=3)
  271. self.assertEqual(Event.objects.filter(dt__exact=dt).count(), 1)
  272. self.assertEqual(Event.objects.filter(dt__exact=next).count(), 0)
  273. self.assertEqual(Event.objects.filter(dt__in=(prev, next)).count(), 0)
  274. self.assertEqual(Event.objects.filter(dt__in=(prev, dt, next)).count(), 1)
  275. self.assertEqual(Event.objects.filter(dt__range=(prev, next)).count(), 1)
  276. @requires_tz_support
  277. def test_query_filter_with_naive_datetime(self):
  278. dt = datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=EAT)
  279. Event.objects.create(dt=dt)
  280. dt = dt.replace(tzinfo=None)
  281. # naive datetimes are interpreted in local time
  282. with self.assertWarnsMessage(RuntimeWarning, self.naive_warning):
  283. self.assertEqual(Event.objects.filter(dt__exact=dt).count(), 1)
  284. with self.assertWarnsMessage(RuntimeWarning, self.naive_warning):
  285. self.assertEqual(Event.objects.filter(dt__lte=dt).count(), 1)
  286. with self.assertWarnsMessage(RuntimeWarning, self.naive_warning):
  287. self.assertEqual(Event.objects.filter(dt__gt=dt).count(), 0)
  288. @skipUnlessDBFeature('has_zoneinfo_database')
  289. def test_query_datetime_lookups(self):
  290. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT))
  291. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT))
  292. self.assertEqual(Event.objects.filter(dt__year=2011).count(), 2)
  293. self.assertEqual(Event.objects.filter(dt__month=1).count(), 2)
  294. self.assertEqual(Event.objects.filter(dt__day=1).count(), 2)
  295. self.assertEqual(Event.objects.filter(dt__week_day=7).count(), 2)
  296. self.assertEqual(Event.objects.filter(dt__hour=1).count(), 1)
  297. self.assertEqual(Event.objects.filter(dt__minute=30).count(), 2)
  298. self.assertEqual(Event.objects.filter(dt__second=0).count(), 2)
  299. @skipUnlessDBFeature('has_zoneinfo_database')
  300. def test_query_datetime_lookups_in_other_timezone(self):
  301. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT))
  302. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT))
  303. with timezone.override(UTC):
  304. # These two dates fall in the same day in EAT, but in different days,
  305. # years and months in UTC.
  306. self.assertEqual(Event.objects.filter(dt__year=2011).count(), 1)
  307. self.assertEqual(Event.objects.filter(dt__month=1).count(), 1)
  308. self.assertEqual(Event.objects.filter(dt__day=1).count(), 1)
  309. self.assertEqual(Event.objects.filter(dt__week_day=7).count(), 1)
  310. self.assertEqual(Event.objects.filter(dt__hour=22).count(), 1)
  311. self.assertEqual(Event.objects.filter(dt__minute=30).count(), 2)
  312. self.assertEqual(Event.objects.filter(dt__second=0).count(), 2)
  313. def test_query_aggregation(self):
  314. # Only min and max make sense for datetimes.
  315. Event.objects.create(dt=datetime.datetime(2011, 9, 1, 23, 20, 20, tzinfo=EAT))
  316. Event.objects.create(dt=datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT))
  317. Event.objects.create(dt=datetime.datetime(2011, 9, 1, 3, 20, 40, tzinfo=EAT))
  318. result = Event.objects.all().aggregate(Min('dt'), Max('dt'))
  319. self.assertEqual(result, {
  320. 'dt__min': datetime.datetime(2011, 9, 1, 3, 20, 40, tzinfo=EAT),
  321. 'dt__max': datetime.datetime(2011, 9, 1, 23, 20, 20, tzinfo=EAT),
  322. })
  323. def test_query_annotation(self):
  324. # Only min and max make sense for datetimes.
  325. morning = Session.objects.create(name='morning')
  326. afternoon = Session.objects.create(name='afternoon')
  327. SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 23, 20, 20, tzinfo=EAT), session=afternoon)
  328. SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT), session=afternoon)
  329. SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 3, 20, 40, tzinfo=EAT), session=morning)
  330. morning_min_dt = datetime.datetime(2011, 9, 1, 3, 20, 40, tzinfo=EAT)
  331. afternoon_min_dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
  332. self.assertQuerysetEqual(
  333. Session.objects.annotate(dt=Min('events__dt')).order_by('dt'),
  334. [morning_min_dt, afternoon_min_dt],
  335. transform=lambda d: d.dt,
  336. )
  337. self.assertQuerysetEqual(
  338. Session.objects.annotate(dt=Min('events__dt')).filter(dt__lt=afternoon_min_dt),
  339. [morning_min_dt],
  340. transform=lambda d: d.dt,
  341. )
  342. self.assertQuerysetEqual(
  343. Session.objects.annotate(dt=Min('events__dt')).filter(dt__gte=afternoon_min_dt),
  344. [afternoon_min_dt],
  345. transform=lambda d: d.dt,
  346. )
  347. @skipUnlessDBFeature('has_zoneinfo_database')
  348. def test_query_datetimes(self):
  349. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT))
  350. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT))
  351. self.assertSequenceEqual(
  352. Event.objects.datetimes('dt', 'year'),
  353. [datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=EAT)]
  354. )
  355. self.assertSequenceEqual(
  356. Event.objects.datetimes('dt', 'month'),
  357. [datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=EAT)]
  358. )
  359. self.assertSequenceEqual(
  360. Event.objects.datetimes('dt', 'day'),
  361. [datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=EAT)]
  362. )
  363. self.assertSequenceEqual(
  364. Event.objects.datetimes('dt', 'hour'),
  365. [datetime.datetime(2011, 1, 1, 1, 0, 0, tzinfo=EAT),
  366. datetime.datetime(2011, 1, 1, 4, 0, 0, tzinfo=EAT)]
  367. )
  368. self.assertSequenceEqual(
  369. Event.objects.datetimes('dt', 'minute'),
  370. [datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT),
  371. datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT)]
  372. )
  373. self.assertSequenceEqual(
  374. Event.objects.datetimes('dt', 'second'),
  375. [datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT),
  376. datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT)]
  377. )
  378. @skipUnlessDBFeature('has_zoneinfo_database')
  379. def test_query_datetimes_in_other_timezone(self):
  380. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT))
  381. Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT))
  382. with timezone.override(UTC):
  383. self.assertSequenceEqual(
  384. Event.objects.datetimes('dt', 'year'),
  385. [datetime.datetime(2010, 1, 1, 0, 0, 0, tzinfo=UTC),
  386. datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=UTC)]
  387. )
  388. self.assertSequenceEqual(
  389. Event.objects.datetimes('dt', 'month'),
  390. [datetime.datetime(2010, 12, 1, 0, 0, 0, tzinfo=UTC),
  391. datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=UTC)]
  392. )
  393. self.assertSequenceEqual(
  394. Event.objects.datetimes('dt', 'day'),
  395. [datetime.datetime(2010, 12, 31, 0, 0, 0, tzinfo=UTC),
  396. datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=UTC)]
  397. )
  398. self.assertSequenceEqual(
  399. Event.objects.datetimes('dt', 'hour'),
  400. [datetime.datetime(2010, 12, 31, 22, 0, 0, tzinfo=UTC),
  401. datetime.datetime(2011, 1, 1, 1, 0, 0, tzinfo=UTC)]
  402. )
  403. self.assertSequenceEqual(
  404. Event.objects.datetimes('dt', 'minute'),
  405. [datetime.datetime(2010, 12, 31, 22, 30, 0, tzinfo=UTC),
  406. datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=UTC)]
  407. )
  408. self.assertSequenceEqual(
  409. Event.objects.datetimes('dt', 'second'),
  410. [datetime.datetime(2010, 12, 31, 22, 30, 0, tzinfo=UTC),
  411. datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=UTC)]
  412. )
  413. def test_raw_sql(self):
  414. # Regression test for #17755
  415. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
  416. event = Event.objects.create(dt=dt)
  417. self.assertSequenceEqual(list(Event.objects.raw('SELECT * FROM timezones_event WHERE dt = %s', [dt])), [event])
  418. @skipUnlessDBFeature('supports_timezones')
  419. def test_cursor_execute_accepts_aware_datetime(self):
  420. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
  421. with connection.cursor() as cursor:
  422. cursor.execute('INSERT INTO timezones_event (dt) VALUES (%s)', [dt])
  423. event = Event.objects.get()
  424. self.assertEqual(event.dt, dt)
  425. @skipIfDBFeature('supports_timezones')
  426. def test_cursor_execute_accepts_naive_datetime(self):
  427. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
  428. utc_naive_dt = timezone.make_naive(dt, timezone.utc)
  429. with connection.cursor() as cursor:
  430. cursor.execute('INSERT INTO timezones_event (dt) VALUES (%s)', [utc_naive_dt])
  431. event = Event.objects.get()
  432. self.assertEqual(event.dt, dt)
  433. @skipUnlessDBFeature('supports_timezones')
  434. def test_cursor_execute_returns_aware_datetime(self):
  435. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
  436. Event.objects.create(dt=dt)
  437. with connection.cursor() as cursor:
  438. cursor.execute('SELECT dt FROM timezones_event WHERE dt = %s', [dt])
  439. self.assertEqual(cursor.fetchall()[0][0], dt)
  440. @skipIfDBFeature('supports_timezones')
  441. def test_cursor_execute_returns_naive_datetime(self):
  442. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
  443. utc_naive_dt = timezone.make_naive(dt, timezone.utc)
  444. Event.objects.create(dt=dt)
  445. with connection.cursor() as cursor:
  446. cursor.execute('SELECT dt FROM timezones_event WHERE dt = %s', [utc_naive_dt])
  447. self.assertEqual(cursor.fetchall()[0][0], utc_naive_dt)
  448. @requires_tz_support
  449. def test_filter_date_field_with_aware_datetime(self):
  450. # Regression test for #17742
  451. day = datetime.date(2011, 9, 1)
  452. AllDayEvent.objects.create(day=day)
  453. # This is 2011-09-02T01:30:00+03:00 in EAT
  454. dt = datetime.datetime(2011, 9, 1, 22, 30, 0, tzinfo=UTC)
  455. self.assertFalse(AllDayEvent.objects.filter(day__gte=dt).exists())
  456. def test_null_datetime(self):
  457. # Regression test for #17294
  458. e = MaybeEvent.objects.create()
  459. self.assertIsNone(e.dt)
  460. def test_update_with_timedelta(self):
  461. initial_dt = timezone.now().replace(microsecond=0)
  462. event = Event.objects.create(dt=initial_dt)
  463. Event.objects.update(dt=F('dt') + timedelta(hours=2))
  464. event.refresh_from_db()
  465. self.assertEqual(event.dt, initial_dt + timedelta(hours=2))
  466. @override_settings(TIME_ZONE='Africa/Nairobi', USE_TZ=True)
  467. class ForcedTimeZoneDatabaseTests(TransactionTestCase):
  468. """
  469. Test the TIME_ZONE database configuration parameter.
  470. Since this involves reading and writing to the same database through two
  471. connections, this is a TransactionTestCase.
  472. """
  473. available_apps = ['timezones']
  474. @classmethod
  475. def setUpClass(cls):
  476. # @skipIfDBFeature and @skipUnlessDBFeature cannot be chained. The
  477. # outermost takes precedence. Handle skipping manually instead.
  478. if connection.features.supports_timezones:
  479. raise SkipTest("Database has feature(s) supports_timezones")
  480. if not connection.features.test_db_allows_multiple_connections:
  481. raise SkipTest("Database doesn't support feature(s): test_db_allows_multiple_connections")
  482. super().setUpClass()
  483. @contextmanager
  484. def override_database_connection_timezone(self, timezone):
  485. try:
  486. orig_timezone = connection.settings_dict['TIME_ZONE']
  487. connection.settings_dict['TIME_ZONE'] = timezone
  488. # Clear cached properties, after first accessing them to ensure they exist.
  489. connection.timezone
  490. del connection.timezone
  491. connection.timezone_name
  492. del connection.timezone_name
  493. yield
  494. finally:
  495. connection.settings_dict['TIME_ZONE'] = orig_timezone
  496. # Clear cached properties, after first accessing them to ensure they exist.
  497. connection.timezone
  498. del connection.timezone
  499. connection.timezone_name
  500. del connection.timezone_name
  501. def test_read_datetime(self):
  502. fake_dt = datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=UTC)
  503. Event.objects.create(dt=fake_dt)
  504. with self.override_database_connection_timezone('Asia/Bangkok'):
  505. event = Event.objects.get()
  506. dt = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)
  507. self.assertEqual(event.dt, dt)
  508. def test_write_datetime(self):
  509. dt = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)
  510. with self.override_database_connection_timezone('Asia/Bangkok'):
  511. Event.objects.create(dt=dt)
  512. event = Event.objects.get()
  513. fake_dt = datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=UTC)
  514. self.assertEqual(event.dt, fake_dt)
  515. @skipUnlessDBFeature('supports_timezones')
  516. @override_settings(TIME_ZONE='Africa/Nairobi', USE_TZ=True)
  517. class UnsupportedTimeZoneDatabaseTests(SimpleTestCase):
  518. def test_time_zone_parameter_not_supported_if_database_supports_timezone(self):
  519. connections.databases['tz'] = connections.databases['default'].copy()
  520. connections.databases['tz']['TIME_ZONE'] = 'Asia/Bangkok'
  521. tz_conn = connections['tz']
  522. try:
  523. msg = (
  524. "Connection 'tz' cannot set TIME_ZONE because its engine "
  525. "handles time zones conversions natively."
  526. )
  527. with self.assertRaisesMessage(ImproperlyConfigured, msg):
  528. tz_conn.cursor()
  529. finally:
  530. connections['tz'].close() # in case the test fails
  531. del connections['tz']
  532. del connections.databases['tz']
  533. @override_settings(TIME_ZONE='Africa/Nairobi')
  534. class SerializationTests(SimpleTestCase):
  535. # Backend-specific notes:
  536. # - JSON supports only milliseconds, microseconds will be truncated.
  537. # - PyYAML dumps the UTC offset correctly for timezone-aware datetimes,
  538. # but when it loads this representation, it subtracts the offset and
  539. # returns a naive datetime object in UTC (https://pyyaml.org/ticket/202).
  540. # Tests are adapted to take these quirks into account.
  541. def assert_python_contains_datetime(self, objects, dt):
  542. self.assertEqual(objects[0]['fields']['dt'], dt)
  543. def assert_json_contains_datetime(self, json, dt):
  544. self.assertIn('"fields": {"dt": "%s"}' % dt, json)
  545. def assert_xml_contains_datetime(self, xml, dt):
  546. field = parseString(xml).getElementsByTagName('field')[0]
  547. self.assertXMLEqual(field.childNodes[0].wholeText, dt)
  548. def assert_yaml_contains_datetime(self, yaml, dt):
  549. # Depending on the yaml dumper, '!timestamp' might be absent
  550. self.assertRegex(yaml, r"\n fields: {dt: !(!timestamp)? '%s'}" % re.escape(dt))
  551. def test_naive_datetime(self):
  552. dt = datetime.datetime(2011, 9, 1, 13, 20, 30)
  553. data = serializers.serialize('python', [Event(dt=dt)])
  554. self.assert_python_contains_datetime(data, dt)
  555. obj = next(serializers.deserialize('python', data)).object
  556. self.assertEqual(obj.dt, dt)
  557. data = serializers.serialize('json', [Event(dt=dt)])
  558. self.assert_json_contains_datetime(data, "2011-09-01T13:20:30")
  559. obj = next(serializers.deserialize('json', data)).object
  560. self.assertEqual(obj.dt, dt)
  561. data = serializers.serialize('xml', [Event(dt=dt)])
  562. self.assert_xml_contains_datetime(data, "2011-09-01T13:20:30")
  563. obj = next(serializers.deserialize('xml', data)).object
  564. self.assertEqual(obj.dt, dt)
  565. if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer):
  566. data = serializers.serialize('yaml', [Event(dt=dt)])
  567. self.assert_yaml_contains_datetime(data, "2011-09-01 13:20:30")
  568. obj = next(serializers.deserialize('yaml', data)).object
  569. self.assertEqual(obj.dt, dt)
  570. def test_naive_datetime_with_microsecond(self):
  571. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060)
  572. data = serializers.serialize('python', [Event(dt=dt)])
  573. self.assert_python_contains_datetime(data, dt)
  574. obj = next(serializers.deserialize('python', data)).object
  575. self.assertEqual(obj.dt, dt)
  576. data = serializers.serialize('json', [Event(dt=dt)])
  577. self.assert_json_contains_datetime(data, "2011-09-01T13:20:30.405")
  578. obj = next(serializers.deserialize('json', data)).object
  579. self.assertEqual(obj.dt, dt.replace(microsecond=405000))
  580. data = serializers.serialize('xml', [Event(dt=dt)])
  581. self.assert_xml_contains_datetime(data, "2011-09-01T13:20:30.405060")
  582. obj = next(serializers.deserialize('xml', data)).object
  583. self.assertEqual(obj.dt, dt)
  584. if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer):
  585. data = serializers.serialize('yaml', [Event(dt=dt)])
  586. self.assert_yaml_contains_datetime(data, "2011-09-01 13:20:30.405060")
  587. obj = next(serializers.deserialize('yaml', data)).object
  588. self.assertEqual(obj.dt, dt)
  589. def test_aware_datetime_with_microsecond(self):
  590. dt = datetime.datetime(2011, 9, 1, 17, 20, 30, 405060, tzinfo=ICT)
  591. data = serializers.serialize('python', [Event(dt=dt)])
  592. self.assert_python_contains_datetime(data, dt)
  593. obj = next(serializers.deserialize('python', data)).object
  594. self.assertEqual(obj.dt, dt)
  595. data = serializers.serialize('json', [Event(dt=dt)])
  596. self.assert_json_contains_datetime(data, "2011-09-01T17:20:30.405+07:00")
  597. obj = next(serializers.deserialize('json', data)).object
  598. self.assertEqual(obj.dt, dt.replace(microsecond=405000))
  599. data = serializers.serialize('xml', [Event(dt=dt)])
  600. self.assert_xml_contains_datetime(data, "2011-09-01T17:20:30.405060+07:00")
  601. obj = next(serializers.deserialize('xml', data)).object
  602. self.assertEqual(obj.dt, dt)
  603. if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer):
  604. data = serializers.serialize('yaml', [Event(dt=dt)])
  605. self.assert_yaml_contains_datetime(data, "2011-09-01 17:20:30.405060+07:00")
  606. obj = next(serializers.deserialize('yaml', data)).object
  607. self.assertEqual(obj.dt.replace(tzinfo=UTC), dt)
  608. def test_aware_datetime_in_utc(self):
  609. dt = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)
  610. data = serializers.serialize('python', [Event(dt=dt)])
  611. self.assert_python_contains_datetime(data, dt)
  612. obj = next(serializers.deserialize('python', data)).object
  613. self.assertEqual(obj.dt, dt)
  614. data = serializers.serialize('json', [Event(dt=dt)])
  615. self.assert_json_contains_datetime(data, "2011-09-01T10:20:30Z")
  616. obj = next(serializers.deserialize('json', data)).object
  617. self.assertEqual(obj.dt, dt)
  618. data = serializers.serialize('xml', [Event(dt=dt)])
  619. self.assert_xml_contains_datetime(data, "2011-09-01T10:20:30+00:00")
  620. obj = next(serializers.deserialize('xml', data)).object
  621. self.assertEqual(obj.dt, dt)
  622. if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer):
  623. data = serializers.serialize('yaml', [Event(dt=dt)])
  624. self.assert_yaml_contains_datetime(data, "2011-09-01 10:20:30+00:00")
  625. obj = next(serializers.deserialize('yaml', data)).object
  626. self.assertEqual(obj.dt.replace(tzinfo=UTC), dt)
  627. def test_aware_datetime_in_local_timezone(self):
  628. dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
  629. data = serializers.serialize('python', [Event(dt=dt)])
  630. self.assert_python_contains_datetime(data, dt)
  631. obj = next(serializers.deserialize('python', data)).object
  632. self.assertEqual(obj.dt, dt)
  633. data = serializers.serialize('json', [Event(dt=dt)])
  634. self.assert_json_contains_datetime(data, "2011-09-01T13:20:30+03:00")
  635. obj = next(serializers.deserialize('json', data)).object
  636. self.assertEqual(obj.dt, dt)
  637. data = serializers.serialize('xml', [Event(dt=dt)])
  638. self.assert_xml_contains_datetime(data, "2011-09-01T13:20:30+03:00")
  639. obj = next(serializers.deserialize('xml', data)).object
  640. self.assertEqual(obj.dt, dt)
  641. if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer):
  642. data = serializers.serialize('yaml', [Event(dt=dt)])
  643. self.assert_yaml_contains_datetime(data, "2011-09-01 13:20:30+03:00")
  644. obj = next(serializers.deserialize('yaml', data)).object
  645. self.assertEqual(obj.dt.replace(tzinfo=UTC), dt)
  646. def test_aware_datetime_in_other_timezone(self):
  647. dt = datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT)
  648. data = serializers.serialize('python', [Event(dt=dt)])
  649. self.assert_python_contains_datetime(data, dt)
  650. obj = next(serializers.deserialize('python', data)).object
  651. self.assertEqual(obj.dt, dt)
  652. data = serializers.serialize('json', [Event(dt=dt)])
  653. self.assert_json_contains_datetime(data, "2011-09-01T17:20:30+07:00")
  654. obj = next(serializers.deserialize('json', data)).object
  655. self.assertEqual(obj.dt, dt)
  656. data = serializers.serialize('xml', [Event(dt=dt)])
  657. self.assert_xml_contains_datetime(data, "2011-09-01T17:20:30+07:00")
  658. obj = next(serializers.deserialize('xml', data)).object
  659. self.assertEqual(obj.dt, dt)
  660. if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer):
  661. data = serializers.serialize('yaml', [Event(dt=dt)])
  662. self.assert_yaml_contains_datetime(data, "2011-09-01 17:20:30+07:00")
  663. obj = next(serializers.deserialize('yaml', data)).object
  664. self.assertEqual(obj.dt.replace(tzinfo=UTC), dt)
  665. @override_settings(DATETIME_FORMAT='c', TIME_ZONE='Africa/Nairobi', USE_L10N=False, USE_TZ=True)
  666. class TemplateTests(SimpleTestCase):
  667. @requires_tz_support
  668. def test_localtime_templatetag_and_filters(self):
  669. """
  670. Test the {% localtime %} templatetag and related filters.
  671. """
  672. datetimes = {
  673. 'utc': datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC),
  674. 'eat': datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT),
  675. 'ict': datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT),
  676. 'naive': datetime.datetime(2011, 9, 1, 13, 20, 30),
  677. }
  678. templates = {
  679. 'notag': Template("{% load tz %}{{ dt }}|{{ dt|localtime }}|{{ dt|utc }}|{{ dt|timezone:ICT }}"),
  680. 'noarg': Template(
  681. "{% load tz %}{% localtime %}{{ dt }}|{{ dt|localtime }}|"
  682. "{{ dt|utc }}|{{ dt|timezone:ICT }}{% endlocaltime %}"
  683. ),
  684. 'on': Template(
  685. "{% load tz %}{% localtime on %}{{ dt }}|{{ dt|localtime }}|"
  686. "{{ dt|utc }}|{{ dt|timezone:ICT }}{% endlocaltime %}"
  687. ),
  688. 'off': Template(
  689. "{% load tz %}{% localtime off %}{{ dt }}|{{ dt|localtime }}|"
  690. "{{ dt|utc }}|{{ dt|timezone:ICT }}{% endlocaltime %}"
  691. ),
  692. }
  693. # Transform a list of keys in 'datetimes' to the expected template
  694. # output. This makes the definition of 'results' more readable.
  695. def t(*result):
  696. return '|'.join(datetimes[key].isoformat() for key in result)
  697. # Results for USE_TZ = True
  698. results = {
  699. 'utc': {
  700. 'notag': t('eat', 'eat', 'utc', 'ict'),
  701. 'noarg': t('eat', 'eat', 'utc', 'ict'),
  702. 'on': t('eat', 'eat', 'utc', 'ict'),
  703. 'off': t('utc', 'eat', 'utc', 'ict'),
  704. },
  705. 'eat': {
  706. 'notag': t('eat', 'eat', 'utc', 'ict'),
  707. 'noarg': t('eat', 'eat', 'utc', 'ict'),
  708. 'on': t('eat', 'eat', 'utc', 'ict'),
  709. 'off': t('eat', 'eat', 'utc', 'ict'),
  710. },
  711. 'ict': {
  712. 'notag': t('eat', 'eat', 'utc', 'ict'),
  713. 'noarg': t('eat', 'eat', 'utc', 'ict'),
  714. 'on': t('eat', 'eat', 'utc', 'ict'),
  715. 'off': t('ict', 'eat', 'utc', 'ict'),
  716. },
  717. 'naive': {
  718. 'notag': t('naive', 'eat', 'utc', 'ict'),
  719. 'noarg': t('naive', 'eat', 'utc', 'ict'),
  720. 'on': t('naive', 'eat', 'utc', 'ict'),
  721. 'off': t('naive', 'eat', 'utc', 'ict'),
  722. }
  723. }
  724. for k1, dt in datetimes.items():
  725. for k2, tpl in templates.items():
  726. ctx = Context({'dt': dt, 'ICT': ICT})
  727. actual = tpl.render(ctx)
  728. expected = results[k1][k2]
  729. self.assertEqual(actual, expected, '%s / %s: %r != %r' % (k1, k2, actual, expected))
  730. # Changes for USE_TZ = False
  731. results['utc']['notag'] = t('utc', 'eat', 'utc', 'ict')
  732. results['ict']['notag'] = t('ict', 'eat', 'utc', 'ict')
  733. with self.settings(USE_TZ=False):
  734. for k1, dt in datetimes.items():
  735. for k2, tpl in templates.items():
  736. ctx = Context({'dt': dt, 'ICT': ICT})
  737. actual = tpl.render(ctx)
  738. expected = results[k1][k2]
  739. self.assertEqual(actual, expected, '%s / %s: %r != %r' % (k1, k2, actual, expected))
  740. def test_localtime_filters_with_pytz(self):
  741. """
  742. Test the |localtime, |utc, and |timezone filters with pytz.
  743. """
  744. # Use a pytz timezone as local time
  745. tpl = Template("{% load tz %}{{ dt|localtime }}|{{ dt|utc }}")
  746. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 12, 20, 30)})
  747. with self.settings(TIME_ZONE='Europe/Paris'):
  748. self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00|2011-09-01T10:20:30+00:00")
  749. # Use a pytz timezone as argument
  750. tpl = Template("{% load tz %}{{ dt|timezone:tz }}")
  751. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30),
  752. 'tz': pytz.timezone('Europe/Paris')})
  753. self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
  754. # Use a pytz timezone name as argument
  755. tpl = Template("{% load tz %}{{ dt|timezone:'Europe/Paris' }}")
  756. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30),
  757. 'tz': pytz.timezone('Europe/Paris')})
  758. self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
  759. def test_localtime_templatetag_invalid_argument(self):
  760. with self.assertRaises(TemplateSyntaxError):
  761. Template("{% load tz %}{% localtime foo %}{% endlocaltime %}").render()
  762. def test_localtime_filters_do_not_raise_exceptions(self):
  763. """
  764. Test the |localtime, |utc, and |timezone filters on bad inputs.
  765. """
  766. tpl = Template("{% load tz %}{{ dt }}|{{ dt|localtime }}|{{ dt|utc }}|{{ dt|timezone:tz }}")
  767. with self.settings(USE_TZ=True):
  768. # bad datetime value
  769. ctx = Context({'dt': None, 'tz': ICT})
  770. self.assertEqual(tpl.render(ctx), "None|||")
  771. ctx = Context({'dt': 'not a date', 'tz': ICT})
  772. self.assertEqual(tpl.render(ctx), "not a date|||")
  773. # bad timezone value
  774. tpl = Template("{% load tz %}{{ dt|timezone:tz }}")
  775. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30), 'tz': None})
  776. self.assertEqual(tpl.render(ctx), "")
  777. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30), 'tz': 'not a tz'})
  778. self.assertEqual(tpl.render(ctx), "")
  779. @requires_tz_support
  780. def test_timezone_templatetag(self):
  781. """
  782. Test the {% timezone %} templatetag.
  783. """
  784. tpl = Template(
  785. "{% load tz %}"
  786. "{{ dt }}|"
  787. "{% timezone tz1 %}"
  788. "{{ dt }}|"
  789. "{% timezone tz2 %}"
  790. "{{ dt }}"
  791. "{% endtimezone %}"
  792. "{% endtimezone %}"
  793. )
  794. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC),
  795. 'tz1': ICT, 'tz2': None})
  796. self.assertEqual(
  797. tpl.render(ctx),
  798. "2011-09-01T13:20:30+03:00|2011-09-01T17:20:30+07:00|2011-09-01T13:20:30+03:00"
  799. )
  800. def test_timezone_templatetag_with_pytz(self):
  801. """
  802. Test the {% timezone %} templatetag with pytz.
  803. """
  804. tpl = Template("{% load tz %}{% timezone tz %}{{ dt }}{% endtimezone %}")
  805. # Use a pytz timezone as argument
  806. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT),
  807. 'tz': pytz.timezone('Europe/Paris')})
  808. self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
  809. # Use a pytz timezone name as argument
  810. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT),
  811. 'tz': 'Europe/Paris'})
  812. self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
  813. def test_timezone_templatetag_invalid_argument(self):
  814. with self.assertRaises(TemplateSyntaxError):
  815. Template("{% load tz %}{% timezone %}{% endtimezone %}").render()
  816. with self.assertRaises(pytz.UnknownTimeZoneError):
  817. Template("{% load tz %}{% timezone tz %}{% endtimezone %}").render(Context({'tz': 'foobar'}))
  818. @skipIf(sys.platform.startswith('win'), "Windows uses non-standard time zone names")
  819. def test_get_current_timezone_templatetag(self):
  820. """
  821. Test the {% get_current_timezone %} templatetag.
  822. """
  823. tpl = Template("{% load tz %}{% get_current_timezone as time_zone %}{{ time_zone }}")
  824. self.assertEqual(tpl.render(Context()), "Africa/Nairobi")
  825. with timezone.override(UTC):
  826. self.assertEqual(tpl.render(Context()), "UTC")
  827. tpl = Template(
  828. "{% load tz %}{% timezone tz %}{% get_current_timezone as time_zone %}"
  829. "{% endtimezone %}{{ time_zone }}"
  830. )
  831. self.assertEqual(tpl.render(Context({'tz': ICT})), "+0700")
  832. with timezone.override(UTC):
  833. self.assertEqual(tpl.render(Context({'tz': ICT})), "+0700")
  834. def test_get_current_timezone_templatetag_with_pytz(self):
  835. """
  836. Test the {% get_current_timezone %} templatetag with pytz.
  837. """
  838. tpl = Template("{% load tz %}{% get_current_timezone as time_zone %}{{ time_zone }}")
  839. with timezone.override(pytz.timezone('Europe/Paris')):
  840. self.assertEqual(tpl.render(Context()), "Europe/Paris")
  841. tpl = Template(
  842. "{% load tz %}{% timezone 'Europe/Paris' %}"
  843. "{% get_current_timezone as time_zone %}{% endtimezone %}"
  844. "{{ time_zone }}"
  845. )
  846. self.assertEqual(tpl.render(Context()), "Europe/Paris")
  847. def test_get_current_timezone_templatetag_invalid_argument(self):
  848. msg = "'get_current_timezone' requires 'as variable' (got ['get_current_timezone'])"
  849. with self.assertRaisesMessage(TemplateSyntaxError, msg):
  850. Template("{% load tz %}{% get_current_timezone %}").render()
  851. @skipIf(sys.platform.startswith('win'), "Windows uses non-standard time zone names")
  852. def test_tz_template_context_processor(self):
  853. """
  854. Test the django.template.context_processors.tz template context processor.
  855. """
  856. tpl = Template("{{ TIME_ZONE }}")
  857. context = Context()
  858. self.assertEqual(tpl.render(context), "")
  859. request_context = RequestContext(HttpRequest(), processors=[context_processors.tz])
  860. self.assertEqual(tpl.render(request_context), "Africa/Nairobi")
  861. @requires_tz_support
  862. def test_date_and_time_template_filters(self):
  863. tpl = Template("{{ dt|date:'Y-m-d' }} at {{ dt|time:'H:i:s' }}")
  864. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 20, 20, 20, tzinfo=UTC)})
  865. self.assertEqual(tpl.render(ctx), "2011-09-01 at 23:20:20")
  866. with timezone.override(ICT):
  867. self.assertEqual(tpl.render(ctx), "2011-09-02 at 03:20:20")
  868. def test_date_and_time_template_filters_honor_localtime(self):
  869. tpl = Template(
  870. "{% load tz %}{% localtime off %}{{ dt|date:'Y-m-d' }} at "
  871. "{{ dt|time:'H:i:s' }}{% endlocaltime %}"
  872. )
  873. ctx = Context({'dt': datetime.datetime(2011, 9, 1, 20, 20, 20, tzinfo=UTC)})
  874. self.assertEqual(tpl.render(ctx), "2011-09-01 at 20:20:20")
  875. with timezone.override(ICT):
  876. self.assertEqual(tpl.render(ctx), "2011-09-01 at 20:20:20")
  877. @requires_tz_support
  878. def test_now_template_tag_uses_current_time_zone(self):
  879. # Regression for #17343
  880. tpl = Template("{% now \"O\" %}")
  881. self.assertEqual(tpl.render(Context({})), "+0300")
  882. with timezone.override(ICT):
  883. self.assertEqual(tpl.render(Context({})), "+0700")
  884. @override_settings(DATETIME_FORMAT='c', TIME_ZONE='Africa/Nairobi', USE_L10N=False, USE_TZ=False)
  885. class LegacyFormsTests(TestCase):
  886. def test_form(self):
  887. form = EventForm({'dt': '2011-09-01 13:20:30'})
  888. self.assertTrue(form.is_valid())
  889. self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 13, 20, 30))
  890. def test_form_with_non_existent_time(self):
  891. form = EventForm({'dt': '2011-03-27 02:30:00'})
  892. with timezone.override(pytz.timezone('Europe/Paris')):
  893. # this is obviously a bug
  894. self.assertTrue(form.is_valid())
  895. self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 3, 27, 2, 30, 0))
  896. def test_form_with_ambiguous_time(self):
  897. form = EventForm({'dt': '2011-10-30 02:30:00'})
  898. with timezone.override(pytz.timezone('Europe/Paris')):
  899. # this is obviously a bug
  900. self.assertTrue(form.is_valid())
  901. self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 10, 30, 2, 30, 0))
  902. def test_split_form(self):
  903. form = EventSplitForm({'dt_0': '2011-09-01', 'dt_1': '13:20:30'})
  904. self.assertTrue(form.is_valid())
  905. self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 13, 20, 30))
  906. def test_model_form(self):
  907. EventModelForm({'dt': '2011-09-01 13:20:30'}).save()
  908. e = Event.objects.get()
  909. self.assertEqual(e.dt, datetime.datetime(2011, 9, 1, 13, 20, 30))
  910. @override_settings(DATETIME_FORMAT='c', TIME_ZONE='Africa/Nairobi', USE_L10N=False, USE_TZ=True)
  911. class NewFormsTests(TestCase):
  912. @requires_tz_support
  913. def test_form(self):
  914. form = EventForm({'dt': '2011-09-01 13:20:30'})
  915. self.assertTrue(form.is_valid())
  916. self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
  917. def test_form_with_other_timezone(self):
  918. form = EventForm({'dt': '2011-09-01 17:20:30'})
  919. with timezone.override(ICT):
  920. self.assertTrue(form.is_valid())
  921. self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
  922. def test_form_with_explicit_timezone(self):
  923. form = EventForm({'dt': '2011-09-01 17:20:30+07:00'})
  924. # Datetime inputs formats don't allow providing a time zone.
  925. self.assertFalse(form.is_valid())
  926. def test_form_with_non_existent_time(self):
  927. with timezone.override(pytz.timezone('Europe/Paris')):
  928. form = EventForm({'dt': '2011-03-27 02:30:00'})
  929. self.assertFalse(form.is_valid())
  930. self.assertEqual(
  931. form.errors['dt'], [
  932. "2011-03-27 02:30:00 couldn't be interpreted in time zone "
  933. "Europe/Paris; it may be ambiguous or it may not exist."
  934. ]
  935. )
  936. def test_form_with_ambiguous_time(self):
  937. with timezone.override(pytz.timezone('Europe/Paris')):
  938. form = EventForm({'dt': '2011-10-30 02:30:00'})
  939. self.assertFalse(form.is_valid())
  940. self.assertEqual(
  941. form.errors['dt'], [
  942. "2011-10-30 02:30:00 couldn't be interpreted in time zone "
  943. "Europe/Paris; it may be ambiguous or it may not exist."
  944. ]
  945. )
  946. @requires_tz_support
  947. def test_split_form(self):
  948. form = EventSplitForm({'dt_0': '2011-09-01', 'dt_1': '13:20:30'})
  949. self.assertTrue(form.is_valid())
  950. self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
  951. @requires_tz_support
  952. def test_localized_form(self):
  953. form = EventLocalizedForm(initial={'dt': datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)})
  954. with timezone.override(ICT):
  955. self.assertIn("2011-09-01 17:20:30", str(form))
  956. @requires_tz_support
  957. def test_model_form(self):
  958. EventModelForm({'dt': '2011-09-01 13:20:30'}).save()
  959. e = Event.objects.get()
  960. self.assertEqual(e.dt, datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
  961. @requires_tz_support
  962. def test_localized_model_form(self):
  963. form = EventLocalizedModelForm(instance=Event(dt=datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)))
  964. with timezone.override(ICT):
  965. self.assertIn("2011-09-01 17:20:30", str(form))
  966. @override_settings(
  967. DATETIME_FORMAT='c',
  968. TIME_ZONE='Africa/Nairobi',
  969. USE_L10N=False,
  970. USE_TZ=True,
  971. ROOT_URLCONF='timezones.urls',
  972. )
  973. class AdminTests(TestCase):
  974. @classmethod
  975. def setUpTestData(cls):
  976. cls.u1 = User.objects.create_user(
  977. password='secret',
  978. last_login=datetime.datetime(2007, 5, 30, 13, 20, 10, tzinfo=UTC),
  979. is_superuser=True, username='super', first_name='Super', last_name='User',
  980. email='super@example.com', is_staff=True, is_active=True,
  981. date_joined=datetime.datetime(2007, 5, 30, 13, 20, 10, tzinfo=UTC),
  982. )
  983. def setUp(self):
  984. self.client.force_login(self.u1)
  985. @requires_tz_support
  986. def test_changelist(self):
  987. e = Event.objects.create(dt=datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
  988. response = self.client.get(reverse('admin_tz:timezones_event_changelist'))
  989. self.assertContains(response, e.dt.astimezone(EAT).isoformat())
  990. def test_changelist_in_other_timezone(self):
  991. e = Event.objects.create(dt=datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
  992. with timezone.override(ICT):
  993. response = self.client.get(reverse('admin_tz:timezones_event_changelist'))
  994. self.assertContains(response, e.dt.astimezone(ICT).isoformat())
  995. @requires_tz_support
  996. def test_change_editable(self):
  997. e = Event.objects.create(dt=datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
  998. response = self.client.get(reverse('admin_tz:timezones_event_change', args=(e.pk,)))
  999. self.assertContains(response, e.dt.astimezone(EAT).date().isoformat())
  1000. self.assertContains(response, e.dt.astimezone(EAT).time().isoformat())
  1001. def test_change_editable_in_other_timezone(self):
  1002. e = Event.objects.create(dt=datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC))
  1003. with timezone.override(ICT):
  1004. response = self.client.get(reverse('admin_tz:timezones_event_change', args=(e.pk,)))
  1005. self.assertContains(response, e.dt.astimezone(ICT).date().isoformat())
  1006. self.assertContains(response, e.dt.astimezone(ICT).time().isoformat())
  1007. @requires_tz_support
  1008. def test_change_readonly(self):
  1009. Timestamp.objects.create()
  1010. # re-fetch the object for backends that lose microseconds (MySQL)
  1011. t = Timestamp.objects.get()
  1012. response = self.client.get(reverse('admin_tz:timezones_timestamp_change', args=(t.pk,)))
  1013. self.assertContains(response, t.created.astimezone(EAT).isoformat())
  1014. def test_change_readonly_in_other_timezone(self):
  1015. Timestamp.objects.create()
  1016. # re-fetch the object for backends that lose microseconds (MySQL)
  1017. t = Timestamp.objects.get()
  1018. with timezone.override(ICT):
  1019. response = self.client.get(reverse('admin_tz:timezones_timestamp_change', args=(t.pk,)))
  1020. self.assertContains(response, t.created.astimezone(ICT).isoformat())