From c50d8161ac251ee0c9cad6012bc7ceab4f899d6f Mon Sep 17 00:00:00 2001 From: Ihar Hrachyshka Date: Mon, 2 Mar 2015 13:24:58 +0100 Subject: [PATCH] timeutils: avoid passing leap second to datetime Python datetime module does not support leap seconds [1]. The best thing we can do without switching API to using another time module is to avoid passing leap seconds to underlying python module. Note: next leap second is scheduled for June 30 [2]. [1]: http://bugs.python.org/issue23574 [2]: http://www.usatoday.com/story/tech/2015/01/08/computer-chaos-feares/21433363/ Closes-Bug: #1427212 Change-Id: I7ffd8121cb20f3742e8e9cd7d4b8f0f15fa6ca9a --- oslo_utils/tests/test_timeutils.py | 12 ++++++++++++ oslo_utils/timeutils.py | 9 ++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/oslo_utils/tests/test_timeutils.py b/oslo_utils/tests/test_timeutils.py index cacb0ef4..b3c6e3b5 100644 --- a/oslo_utils/tests/test_timeutils.py +++ b/oslo_utils/tests/test_timeutils.py @@ -191,6 +191,18 @@ class TimeUtilsTest(test_base.BaseTestCase): backagain = timeutils.unmarshall_time(binary) self.assertEqual(now, backagain) + def test_unmarshall_time_leap_second(self): + leap_dict = dict(day=30, month=6, year=2015, + hour=23, minute=59, + second=timeutils._MAX_DATETIME_SEC + 1, + microsecond=0) + leap_time = timeutils.unmarshall_time(leap_dict) + + leap_dict.update(second=timeutils._MAX_DATETIME_SEC) + expected = timeutils.unmarshall_time(leap_dict) + + self.assertEqual(expected, leap_time) + def test_delta_seconds(self): before = timeutils.utcnow() after = before + datetime.timedelta(days=7, seconds=59, diff --git a/oslo_utils/timeutils.py b/oslo_utils/timeutils.py index 437b729b..52453600 100644 --- a/oslo_utils/timeutils.py +++ b/oslo_utils/timeutils.py @@ -31,6 +31,8 @@ _ISO8601_TIME_FORMAT_SUBSECOND = '%Y-%m-%dT%H:%M:%S.%f' _ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S' PERFECT_TIME_FORMAT = _ISO8601_TIME_FORMAT_SUBSECOND +_MAX_DATETIME_SEC = 59 + # Use monotonic time in stopwatches if we can get at it... # # PEP @ https://www.python.org/dev/peps/pep-0418/ @@ -211,12 +213,17 @@ def marshall_now(now=None): def unmarshall_time(tyme): """Unmarshall a datetime dict.""" + + # NOTE(ihrachys): datetime does not support leap seconds, + # so the best thing we can do for now is dropping them + # http://bugs.python.org/issue23574 + second = min(tyme['second'], _MAX_DATETIME_SEC) return datetime.datetime(day=tyme['day'], month=tyme['month'], year=tyme['year'], hour=tyme['hour'], minute=tyme['minute'], - second=tyme['second'], + second=second, microsecond=tyme['microsecond'])