test_time.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. from __future__ import absolute_import, unicode_literals
  2. from datetime import datetime, timedelta, tzinfo
  3. import pytest
  4. import pytz
  5. from case import Mock, patch
  6. from pytz import AmbiguousTimeError
  7. from celery.utils.iso8601 import parse_iso8601
  8. from celery.utils.time import (LocalTimezone, delta_resolution, ffwd,
  9. get_exponential_backoff_interval,
  10. humanize_seconds, localize, make_aware,
  11. maybe_iso8601, maybe_make_aware,
  12. maybe_timedelta, rate, remaining, timezone,
  13. utcoffset)
  14. class test_LocalTimezone:
  15. def test_daylight(self, patching):
  16. time = patching('celery.utils.time._time')
  17. time.timezone = 3600
  18. time.daylight = False
  19. x = LocalTimezone()
  20. assert x.STDOFFSET == timedelta(seconds=-3600)
  21. assert x.DSTOFFSET == x.STDOFFSET
  22. time.daylight = True
  23. time.altzone = 3600
  24. y = LocalTimezone()
  25. assert y.STDOFFSET == timedelta(seconds=-3600)
  26. assert y.DSTOFFSET == timedelta(seconds=-3600)
  27. assert repr(y)
  28. y._isdst = Mock()
  29. y._isdst.return_value = True
  30. assert y.utcoffset(datetime.now())
  31. assert not y.dst(datetime.now())
  32. y._isdst.return_value = False
  33. assert y.utcoffset(datetime.now())
  34. assert not y.dst(datetime.now())
  35. assert y.tzname(datetime.now())
  36. class test_iso8601:
  37. def test_parse_with_timezone(self):
  38. d = datetime.utcnow().replace(tzinfo=pytz.utc)
  39. assert parse_iso8601(d.isoformat()) == d
  40. # 2013-06-07T20:12:51.775877+00:00
  41. iso = d.isoformat()
  42. iso1 = iso.replace('+00:00', '-01:00')
  43. d1 = parse_iso8601(iso1)
  44. assert d1.tzinfo._minutes == -60
  45. iso2 = iso.replace('+00:00', '+01:00')
  46. d2 = parse_iso8601(iso2)
  47. assert d2.tzinfo._minutes == +60
  48. iso3 = iso.replace('+00:00', 'Z')
  49. d3 = parse_iso8601(iso3)
  50. assert d3.tzinfo == pytz.UTC
  51. @pytest.mark.parametrize('delta,expected', [
  52. (timedelta(days=2), datetime(2010, 3, 30, 0, 0)),
  53. (timedelta(hours=2), datetime(2010, 3, 30, 11, 0)),
  54. (timedelta(minutes=2), datetime(2010, 3, 30, 11, 50)),
  55. (timedelta(seconds=2), None),
  56. ])
  57. def test_delta_resolution(delta, expected):
  58. dt = datetime(2010, 3, 30, 11, 50, 58, 41065)
  59. assert delta_resolution(dt, delta) == expected or dt
  60. @pytest.mark.parametrize('seconds,expected', [
  61. (4 * 60 * 60 * 24, '4.00 days'),
  62. (1 * 60 * 60 * 24, '1.00 day'),
  63. (4 * 60 * 60, '4.00 hours'),
  64. (1 * 60 * 60, '1.00 hour'),
  65. (4 * 60, '4.00 minutes'),
  66. (1 * 60, '1.00 minute'),
  67. (4, '4.00 seconds'),
  68. (1, '1.00 second'),
  69. (4.3567631221, '4.36 seconds'),
  70. (0, 'now'),
  71. ])
  72. def test_humanize_seconds(seconds, expected):
  73. assert humanize_seconds(seconds) == expected
  74. def test_humanize_seconds__prefix():
  75. assert humanize_seconds(4, prefix='about ') == 'about 4.00 seconds'
  76. def test_maybe_iso8601_datetime():
  77. now = datetime.now()
  78. assert maybe_iso8601(now) is now
  79. @pytest.mark.parametrize('arg,expected', [
  80. (30, timedelta(seconds=30)),
  81. (30.6, timedelta(seconds=30.6)),
  82. (timedelta(days=2), timedelta(days=2)),
  83. ])
  84. def test_maybe_timedelta(arg, expected):
  85. assert maybe_timedelta(arg) == expected
  86. def test_remaining_relative():
  87. remaining(datetime.utcnow(), timedelta(hours=1), relative=True)
  88. class test_timezone:
  89. def test_get_timezone_with_pytz(self):
  90. assert timezone.get_timezone('UTC')
  91. def test_tz_or_local(self):
  92. assert timezone.tz_or_local() == timezone.local
  93. assert timezone.tz_or_local(timezone.utc)
  94. def test_to_local(self):
  95. assert timezone.to_local(make_aware(datetime.utcnow(), timezone.utc))
  96. assert timezone.to_local(datetime.utcnow())
  97. def test_to_local_fallback(self):
  98. assert timezone.to_local_fallback(
  99. make_aware(datetime.utcnow(), timezone.utc))
  100. assert timezone.to_local_fallback(datetime.utcnow())
  101. class test_make_aware:
  102. def test_tz_without_localize(self):
  103. tz = tzinfo()
  104. assert not hasattr(tz, 'localize')
  105. wtz = make_aware(datetime.utcnow(), tz)
  106. assert wtz.tzinfo == tz
  107. def test_when_has_localize(self):
  108. class tzz(tzinfo):
  109. raises = False
  110. def localize(self, dt, is_dst=None):
  111. self.localized = True
  112. if self.raises and is_dst is None:
  113. self.raised = True
  114. raise AmbiguousTimeError()
  115. return 1 # needed by min() in Python 3 (None not hashable)
  116. tz = tzz()
  117. make_aware(datetime.utcnow(), tz)
  118. assert tz.localized
  119. tz2 = tzz()
  120. tz2.raises = True
  121. make_aware(datetime.utcnow(), tz2)
  122. assert tz2.localized
  123. assert tz2.raised
  124. def test_maybe_make_aware(self):
  125. aware = datetime.utcnow().replace(tzinfo=timezone.utc)
  126. assert maybe_make_aware(aware)
  127. naive = datetime.utcnow()
  128. assert maybe_make_aware(naive)
  129. assert maybe_make_aware(naive).tzinfo is pytz.utc
  130. tz = pytz.timezone('US/Eastern')
  131. eastern = datetime.utcnow().replace(tzinfo=tz)
  132. assert maybe_make_aware(eastern).tzinfo is tz
  133. utcnow = datetime.utcnow()
  134. assert maybe_make_aware(utcnow, 'UTC').tzinfo is pytz.utc
  135. class test_localize:
  136. def test_tz_without_normalize(self):
  137. class tzz(tzinfo):
  138. def utcoffset(self, dt):
  139. return None # Mock no utcoffset specified
  140. tz = tzz()
  141. assert not hasattr(tz, 'normalize')
  142. assert localize(make_aware(datetime.utcnow(), tz), tz)
  143. def test_when_has_normalize(self):
  144. class tzz(tzinfo):
  145. raises = None
  146. def utcoffset(self, dt):
  147. return None
  148. def normalize(self, dt, **kwargs):
  149. self.normalized = True
  150. if self.raises and kwargs and kwargs.get('is_dst') is None:
  151. self.raised = True
  152. raise self.raises
  153. return 1 # needed by min() in Python 3 (None not hashable)
  154. tz = tzz()
  155. localize(make_aware(datetime.utcnow(), tz), tz)
  156. assert tz.normalized
  157. tz2 = tzz()
  158. tz2.raises = AmbiguousTimeError()
  159. localize(make_aware(datetime.utcnow(), tz2), tz2)
  160. assert tz2.normalized
  161. assert tz2.raised
  162. tz3 = tzz()
  163. tz3.raises = TypeError()
  164. localize(make_aware(datetime.utcnow(), tz3), tz3)
  165. assert tz3.normalized
  166. assert tz3.raised
  167. def test_localize_changes_utc_dt(self):
  168. now_utc_time = datetime.now(tz=pytz.utc)
  169. local_tz = pytz.timezone('US/Eastern')
  170. localized_time = localize(now_utc_time, local_tz)
  171. assert localized_time == now_utc_time
  172. def test_localize_aware_dt_idempotent(self):
  173. t = (2017, 4, 23, 21, 36, 59, 0)
  174. local_zone = pytz.timezone('America/New_York')
  175. local_time = datetime(*t)
  176. local_time_aware = datetime(*t, tzinfo=local_zone)
  177. alternate_zone = pytz.timezone('America/Detroit')
  178. localized_time = localize(local_time_aware, alternate_zone)
  179. assert localized_time == local_time_aware
  180. assert local_zone.utcoffset(
  181. local_time) == alternate_zone.utcoffset(local_time)
  182. localized_utc_offset = localized_time.tzinfo.utcoffset(local_time)
  183. assert localized_utc_offset == alternate_zone.utcoffset(local_time)
  184. assert localized_utc_offset == local_zone.utcoffset(local_time)
  185. @pytest.mark.parametrize('s,expected', [
  186. (999, 999),
  187. (7.5, 7.5),
  188. ('2.5/s', 2.5),
  189. ('1456/s', 1456),
  190. ('100/m', 100 / 60.0),
  191. ('10/h', 10 / 60.0 / 60.0),
  192. (0, 0),
  193. (None, 0),
  194. ('0/m', 0),
  195. ('0/h', 0),
  196. ('0/s', 0),
  197. ('0.0/s', 0),
  198. ])
  199. def test_rate_limit_string(s, expected):
  200. assert rate(s) == expected
  201. class test_ffwd:
  202. def test_repr(self):
  203. x = ffwd(year=2012)
  204. assert repr(x)
  205. def test_radd_with_unknown_gives_NotImplemented(self):
  206. x = ffwd(year=2012)
  207. assert x.__radd__(object()) == NotImplemented
  208. class test_utcoffset:
  209. def test_utcoffset(self, patching):
  210. _time = patching('celery.utils.time._time')
  211. _time.daylight = True
  212. assert utcoffset(time=_time) is not None
  213. _time.daylight = False
  214. assert utcoffset(time=_time) is not None
  215. class test_get_exponential_backoff_interval:
  216. @patch('random.randrange', lambda n: n - 2)
  217. def test_with_jitter(self):
  218. assert get_exponential_backoff_interval(
  219. factor=4,
  220. retries=3,
  221. maximum=100,
  222. full_jitter=True
  223. ) == 4 * (2 ** 3) - 1
  224. def test_without_jitter(self):
  225. assert get_exponential_backoff_interval(
  226. factor=4,
  227. retries=3,
  228. maximum=100,
  229. full_jitter=False
  230. ) == 4 * (2 ** 3)
  231. def test_bound_by_maximum(self):
  232. maximum_boundary = 100
  233. assert get_exponential_backoff_interval(
  234. factor=40,
  235. retries=3,
  236. maximum=maximum_boundary
  237. ) == maximum_boundary
  238. @patch('random.randrange', lambda n: n - 1)
  239. def test_negative_values(self):
  240. assert get_exponential_backoff_interval(
  241. factor=-40,
  242. retries=3,
  243. maximum=100
  244. ) == 0