diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d31bbf3..8aad926d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ All notable changes to this project will be documented in this file. - Load settings from environment variables - Add "List-Unsubscribe" header to alert and report emails +### Bug Fixes +- During DST transition, handle ambiguous dates as pre-transition + ## 1.2.0 - 2018-10-20 diff --git a/hc/api/models.py b/hc/api/models.py index a6e62f99..ad8a9871 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -116,7 +116,7 @@ class Check(models.Model): last_naive = timezone.make_naive(self.last_ping) it = croniter(self.schedule, last_naive) next_naive = it.get_next(datetime) - return timezone.make_aware(next_naive, is_dst=False) + return timezone.make_aware(next_naive, is_dst=True) def get_status(self, now=None): """ Return "up" if the check is up or in grace, otherwise "down". """ diff --git a/hc/front/tests/test_cron_preview.py b/hc/front/tests/test_cron_preview.py index cc06c2ab..571a3aeb 100644 --- a/hc/front/tests/test_cron_preview.py +++ b/hc/front/tests/test_cron_preview.py @@ -1,4 +1,8 @@ +from datetime import datetime + from hc.test import BaseTestCase +from mock import patch +import pytz class CronPreviewTestCase(BaseTestCase): @@ -30,3 +34,21 @@ class CronPreviewTestCase(BaseTestCase): def test_it_rejects_get(self): r = self.client.get("/checks/cron_preview/", {}) self.assertEqual(r.status_code, 405) + + @patch("hc.front.views.timezone.now") + def test_it_handles_dst_transition(self, mock_now): + # Consider year 2018, Riga, Latvia: + # The daylight-saving-time ends at 4AM on October 28. + # At that time, the clock is turned back one hour. + # So, on that date, 3AM happens *twice* and saying + # "3AM on October 28" is ambiguous. + mock_now.return_value = datetime(2018, 10, 26, tzinfo=pytz.UTC) + + # This schedule will hit the ambiguous date. Cron preview must + # be able to handle this: + payload = { + "schedule": "0 3 * * *", + "tz": "Europe/Riga" + } + r = self.client.post("/checks/cron_preview/", payload) + self.assertNotContains(r, "Invalid cron expression", status_code=200) diff --git a/hc/front/views.py b/hc/front/views.py index b95e747d..e6c312a5 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -281,7 +281,7 @@ def cron_preview(request): it = croniter(schedule, now_naive) for i in range(0, 6): naive = it.get_next(datetime) - aware = timezone.make_aware(naive) + aware = timezone.make_aware(naive, is_dst=True) ctx["dates"].append((naive, aware)) except UnknownTimeZoneError: ctx["bad_tz"] = True