tests.py 57 KB

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