test_timezone.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. import datetime
  2. import unittest
  3. from unittest import mock
  4. import pytz
  5. try:
  6. import zoneinfo
  7. except ImportError:
  8. try:
  9. from backports import zoneinfo
  10. except ImportError:
  11. zoneinfo = None
  12. from django.test import SimpleTestCase, override_settings
  13. from django.utils import timezone
  14. CET = pytz.timezone("Europe/Paris")
  15. EAT = timezone.get_fixed_timezone(180) # Africa/Nairobi
  16. ICT = timezone.get_fixed_timezone(420) # Asia/Bangkok
  17. UTC = datetime.timezone.utc
  18. HAS_ZONEINFO = zoneinfo is not None
  19. if not HAS_ZONEINFO:
  20. PARIS_ZI = None
  21. PARIS_IMPLS = (CET,)
  22. needs_zoneinfo = unittest.skip("Test requires zoneinfo")
  23. else:
  24. PARIS_ZI = zoneinfo.ZoneInfo('Europe/Paris')
  25. PARIS_IMPLS = (CET, PARIS_ZI)
  26. def needs_zoneinfo(f):
  27. return f
  28. class TimezoneTests(SimpleTestCase):
  29. def test_now(self):
  30. with override_settings(USE_TZ=True):
  31. self.assertTrue(timezone.is_aware(timezone.now()))
  32. with override_settings(USE_TZ=False):
  33. self.assertTrue(timezone.is_naive(timezone.now()))
  34. def test_localdate(self):
  35. naive = datetime.datetime(2015, 1, 1, 0, 0, 1)
  36. with self.assertRaisesMessage(ValueError, 'localtime() cannot be applied to a naive datetime'):
  37. timezone.localdate(naive)
  38. with self.assertRaisesMessage(ValueError, 'localtime() cannot be applied to a naive datetime'):
  39. timezone.localdate(naive, timezone=EAT)
  40. aware = datetime.datetime(2015, 1, 1, 0, 0, 1, tzinfo=ICT)
  41. self.assertEqual(timezone.localdate(aware, timezone=EAT), datetime.date(2014, 12, 31))
  42. with timezone.override(EAT):
  43. self.assertEqual(timezone.localdate(aware), datetime.date(2014, 12, 31))
  44. with mock.patch('django.utils.timezone.now', return_value=aware):
  45. self.assertEqual(timezone.localdate(timezone=EAT), datetime.date(2014, 12, 31))
  46. with timezone.override(EAT):
  47. self.assertEqual(timezone.localdate(), datetime.date(2014, 12, 31))
  48. def test_override(self):
  49. default = timezone.get_default_timezone()
  50. try:
  51. timezone.activate(ICT)
  52. with timezone.override(EAT):
  53. self.assertIs(EAT, timezone.get_current_timezone())
  54. self.assertIs(ICT, timezone.get_current_timezone())
  55. with timezone.override(None):
  56. self.assertIs(default, timezone.get_current_timezone())
  57. self.assertIs(ICT, timezone.get_current_timezone())
  58. timezone.deactivate()
  59. with timezone.override(EAT):
  60. self.assertIs(EAT, timezone.get_current_timezone())
  61. self.assertIs(default, timezone.get_current_timezone())
  62. with timezone.override(None):
  63. self.assertIs(default, timezone.get_current_timezone())
  64. self.assertIs(default, timezone.get_current_timezone())
  65. finally:
  66. timezone.deactivate()
  67. def test_override_decorator(self):
  68. default = timezone.get_default_timezone()
  69. @timezone.override(EAT)
  70. def func_tz_eat():
  71. self.assertIs(EAT, timezone.get_current_timezone())
  72. @timezone.override(None)
  73. def func_tz_none():
  74. self.assertIs(default, timezone.get_current_timezone())
  75. try:
  76. timezone.activate(ICT)
  77. func_tz_eat()
  78. self.assertIs(ICT, timezone.get_current_timezone())
  79. func_tz_none()
  80. self.assertIs(ICT, timezone.get_current_timezone())
  81. timezone.deactivate()
  82. func_tz_eat()
  83. self.assertIs(default, timezone.get_current_timezone())
  84. func_tz_none()
  85. self.assertIs(default, timezone.get_current_timezone())
  86. finally:
  87. timezone.deactivate()
  88. def test_override_string_tz(self):
  89. with timezone.override('Asia/Bangkok'):
  90. self.assertEqual(timezone.get_current_timezone_name(), 'Asia/Bangkok')
  91. def test_override_fixed_offset(self):
  92. with timezone.override(datetime.timezone(datetime.timedelta(), 'tzname')):
  93. self.assertEqual(timezone.get_current_timezone_name(), 'tzname')
  94. def test_activate_invalid_timezone(self):
  95. with self.assertRaisesMessage(ValueError, 'Invalid timezone: None'):
  96. timezone.activate(None)
  97. def test_is_aware(self):
  98. self.assertTrue(timezone.is_aware(datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)))
  99. self.assertFalse(timezone.is_aware(datetime.datetime(2011, 9, 1, 13, 20, 30)))
  100. def test_is_naive(self):
  101. self.assertFalse(timezone.is_naive(datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)))
  102. self.assertTrue(timezone.is_naive(datetime.datetime(2011, 9, 1, 13, 20, 30)))
  103. def test_make_aware(self):
  104. self.assertEqual(
  105. timezone.make_aware(datetime.datetime(2011, 9, 1, 13, 20, 30), EAT),
  106. datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT))
  107. with self.assertRaises(ValueError):
  108. timezone.make_aware(datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT), EAT)
  109. def test_make_naive(self):
  110. self.assertEqual(
  111. timezone.make_naive(datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT), EAT),
  112. datetime.datetime(2011, 9, 1, 13, 20, 30))
  113. self.assertEqual(
  114. timezone.make_naive(datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT), EAT),
  115. datetime.datetime(2011, 9, 1, 13, 20, 30))
  116. with self.assertRaisesMessage(ValueError, 'make_naive() cannot be applied to a naive datetime'):
  117. timezone.make_naive(datetime.datetime(2011, 9, 1, 13, 20, 30), EAT)
  118. def test_make_naive_no_tz(self):
  119. self.assertEqual(
  120. timezone.make_naive(datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)),
  121. datetime.datetime(2011, 9, 1, 5, 20, 30)
  122. )
  123. def test_make_aware_no_tz(self):
  124. self.assertEqual(
  125. timezone.make_aware(datetime.datetime(2011, 9, 1, 13, 20, 30)),
  126. datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=timezone.get_fixed_timezone(-300))
  127. )
  128. def test_make_aware2(self):
  129. CEST = datetime.timezone(datetime.timedelta(hours=2), 'CEST')
  130. for tz in PARIS_IMPLS:
  131. with self.subTest(repr(tz)):
  132. self.assertEqual(
  133. timezone.make_aware(datetime.datetime(2011, 9, 1, 12, 20, 30), tz),
  134. datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=CEST))
  135. with self.assertRaises(ValueError):
  136. timezone.make_aware(CET.localize(datetime.datetime(2011, 9, 1, 12, 20, 30)), CET)
  137. if HAS_ZONEINFO:
  138. with self.assertRaises(ValueError):
  139. timezone.make_aware(datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=PARIS_ZI), PARIS_ZI)
  140. def test_make_naive_pytz(self):
  141. self.assertEqual(
  142. timezone.make_naive(CET.localize(datetime.datetime(2011, 9, 1, 12, 20, 30)), CET),
  143. datetime.datetime(2011, 9, 1, 12, 20, 30))
  144. self.assertEqual(
  145. timezone.make_naive(
  146. pytz.timezone("Asia/Bangkok").localize(datetime.datetime(2011, 9, 1, 17, 20, 30)), CET
  147. ),
  148. datetime.datetime(2011, 9, 1, 12, 20, 30))
  149. with self.assertRaisesMessage(ValueError, 'make_naive() cannot be applied to a naive datetime'):
  150. timezone.make_naive(datetime.datetime(2011, 9, 1, 12, 20, 30), CET)
  151. @needs_zoneinfo
  152. def test_make_naive_zoneinfo(self):
  153. self.assertEqual(
  154. timezone.make_naive(datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=PARIS_ZI), PARIS_ZI),
  155. datetime.datetime(2011, 9, 1, 12, 20, 30)
  156. )
  157. self.assertEqual(
  158. timezone.make_naive(datetime.datetime(2011, 9, 1, 12, 20, 30, fold=1, tzinfo=PARIS_ZI), PARIS_ZI),
  159. datetime.datetime(2011, 9, 1, 12, 20, 30, fold=1)
  160. )
  161. def test_make_aware_pytz_ambiguous(self):
  162. # 2:30 happens twice, once before DST ends and once after
  163. ambiguous = datetime.datetime(2015, 10, 25, 2, 30)
  164. with self.assertRaises(pytz.AmbiguousTimeError):
  165. timezone.make_aware(ambiguous, timezone=CET)
  166. std = timezone.make_aware(ambiguous, timezone=CET, is_dst=False)
  167. dst = timezone.make_aware(ambiguous, timezone=CET, is_dst=True)
  168. self.assertEqual(std - dst, datetime.timedelta(hours=1))
  169. self.assertEqual(std.tzinfo.utcoffset(std), datetime.timedelta(hours=1))
  170. self.assertEqual(dst.tzinfo.utcoffset(dst), datetime.timedelta(hours=2))
  171. @needs_zoneinfo
  172. def test_make_aware_zoneinfo_ambiguous(self):
  173. # 2:30 happens twice, once before DST ends and once after
  174. ambiguous = datetime.datetime(2015, 10, 25, 2, 30)
  175. std = timezone.make_aware(ambiguous.replace(fold=1), timezone=PARIS_ZI)
  176. dst = timezone.make_aware(ambiguous, timezone=PARIS_ZI)
  177. self.assertEqual(
  178. std.astimezone(UTC) - dst.astimezone(UTC),
  179. datetime.timedelta(hours=1)
  180. )
  181. self.assertEqual(std.utcoffset(), datetime.timedelta(hours=1))
  182. self.assertEqual(dst.utcoffset(), datetime.timedelta(hours=2))
  183. def test_make_aware_pytz_non_existent(self):
  184. # 2:30 never happened due to DST
  185. non_existent = datetime.datetime(2015, 3, 29, 2, 30)
  186. with self.assertRaises(pytz.NonExistentTimeError):
  187. timezone.make_aware(non_existent, timezone=CET)
  188. std = timezone.make_aware(non_existent, timezone=CET, is_dst=False)
  189. dst = timezone.make_aware(non_existent, timezone=CET, is_dst=True)
  190. self.assertEqual(std - dst, datetime.timedelta(hours=1))
  191. self.assertEqual(std.tzinfo.utcoffset(std), datetime.timedelta(hours=1))
  192. self.assertEqual(dst.tzinfo.utcoffset(dst), datetime.timedelta(hours=2))
  193. @needs_zoneinfo
  194. def test_make_aware_zoneinfo_non_existent(self):
  195. # 2:30 never happened due to DST
  196. non_existent = datetime.datetime(2015, 3, 29, 2, 30)
  197. std = timezone.make_aware(non_existent, PARIS_ZI)
  198. dst = timezone.make_aware(non_existent.replace(fold=1), PARIS_ZI)
  199. self.assertEqual(
  200. std.astimezone(UTC) - dst.astimezone(UTC),
  201. datetime.timedelta(hours=1)
  202. )
  203. self.assertEqual(std.utcoffset(), datetime.timedelta(hours=1))
  204. self.assertEqual(dst.utcoffset(), datetime.timedelta(hours=2))
  205. def test_get_default_timezone(self):
  206. self.assertEqual(timezone.get_default_timezone_name(), 'America/Chicago')
  207. def test_get_default_timezone_utc(self):
  208. with override_settings(USE_TZ=True, TIME_ZONE='UTC'):
  209. self.assertIs(timezone.get_default_timezone(), timezone.utc)
  210. def test_fixedoffset_timedelta(self):
  211. delta = datetime.timedelta(hours=1)
  212. self.assertEqual(timezone.get_fixed_timezone(delta).utcoffset(None), delta)
  213. def test_fixedoffset_negative_timedelta(self):
  214. delta = datetime.timedelta(hours=-2)
  215. self.assertEqual(timezone.get_fixed_timezone(delta).utcoffset(None), delta)