oslo_utils.timeutils is deprecating isotime(). In reality they are deprecating some other things as well but Trove doesn't (currently) use any of those things. Much has been written on the subject of this deprecation. I think the proposal to merely replace isotime with datetime.datetime.isoformat() is a little simplistic. Well intentioned, but nonetheless I believe that it is simplistic. The primary issue I could find with oslo_utils.timeutils.isotime() was the fact that it was naive. I think it could well have been fixed in oslo_utils but for whatever reason(s) oslo decided not to want to go that route. The primary challenge from Trove's perspective is that I want to respect the existing API contract while at the same time get an implementation of time handling that is not identical in its flaws with oslo_utils.timeutils.isotime(). This change set attempts to address that by making trove.common.timeutils.isotime() that is aware. It also implements a utcnow_aware() function that is aware. ISO 8601 allows for four representations of timezone and those are <time>Z <time>[+-]hh:mm <time>[+-]hhmm <time>[+-]hh Trove conventionally used the first one, even if the time wasn't really a UTC time. That's one of the things being fixed here. In review cp16net asked whether this change removes the 'Z' at the end of time strings generated by the isotime() function. The answer is NO. The new isotime() function performs identical to the old and now deprecated function in oslo_utils.timeutils for UTC (Z) times. There was a utcnow() function in trove.common.utils which just wrapped datetime.datetime.utcnow(). That has been moved now to trove.common.timeutils with the other new time related functions. There were a couple of places in Trove where code was using datetime.now() which was not ideal. Those have been corrected now as well. Unit tests have been proposed for the new routines. Closes-Bug: #1532120 Change-Id: Ic5abf6669edd4f1a9fd62e61f437565aa887aebe
130 lines
3.7 KiB
Python
130 lines
3.7 KiB
Python
# Copyright 2016 Tesora Inc.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from datetime import datetime
|
|
from datetime import timedelta
|
|
from datetime import tzinfo
|
|
|
|
from trove.common import timeutils
|
|
from trove.tests.unittests import trove_testtools
|
|
|
|
|
|
class bogus_tzinfo(tzinfo):
|
|
"""A bogus tzinfo class"""
|
|
def utcoffset(self, dt):
|
|
return timedelta(hours=2)
|
|
|
|
def tzname(self, dt):
|
|
return "BOGUS"
|
|
|
|
def dst(self, dt):
|
|
return timedelta(hours=1)
|
|
|
|
|
|
class invalid_tzinfo(tzinfo):
|
|
"""A bogus tzinfo class"""
|
|
def utcoffset(self, dt):
|
|
return timedelta(hours=25)
|
|
|
|
def tzname(self, dt):
|
|
return "INVALID"
|
|
|
|
def dst(self, dt):
|
|
return timedelta(hours=25)
|
|
|
|
|
|
class TestTroveTimeutils(trove_testtools.TestCase):
|
|
|
|
def setUp(self):
|
|
super(TestTroveTimeutils, self).setUp()
|
|
|
|
def tearDown(self):
|
|
super(TestTroveTimeutils, self).tearDown()
|
|
|
|
def test_utcnow_tz(self):
|
|
dt = timeutils.utcnow()
|
|
|
|
self.assertIsNone(dt.tzinfo)
|
|
|
|
def test_utcnow_aware_tz(self):
|
|
dt = timeutils.utcnow_aware()
|
|
|
|
self.assertEqual(timedelta(0), dt.utcoffset())
|
|
self.assertEqual('Z', dt.tzname())
|
|
|
|
def test_isotime(self):
|
|
dt = timeutils.utcnow_aware()
|
|
|
|
expected = "%04d-%02d-%02dT%02d:%02d:%02dZ" % (
|
|
dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)
|
|
|
|
self.assertEqual(expected, timeutils.isotime(dt))
|
|
|
|
def test_isotime_subsecond(self):
|
|
dt = timeutils.utcnow_aware()
|
|
|
|
expected = "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ" % (
|
|
dt.year, dt.month, dt.day,
|
|
dt.hour, dt.minute, dt.second,
|
|
dt.microsecond)
|
|
|
|
self.assertEqual(expected, timeutils.isotime(dt, subsecond=True))
|
|
|
|
def test_isotime_unaware(self):
|
|
dt = timeutils.utcnow()
|
|
|
|
expected = "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ" % (
|
|
dt.year, dt.month, dt.day,
|
|
dt.hour, dt.minute, dt.second,
|
|
dt.microsecond)
|
|
|
|
self.assertEqual(expected, timeutils.isotime(dt, subsecond=True))
|
|
|
|
def test_isotime_unaware_subsecond(self):
|
|
dt = timeutils.utcnow()
|
|
|
|
expected = "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ" % (
|
|
dt.year, dt.month, dt.day,
|
|
dt.hour, dt.minute, dt.second,
|
|
dt.microsecond)
|
|
|
|
self.assertEqual(expected, timeutils.isotime(dt, subsecond=True))
|
|
|
|
def test_bogus_unaware(self):
|
|
dt = datetime.now(bogus_tzinfo())
|
|
|
|
expected = "%04d-%02d-%02dT%02d:%02d:%02d.%06d+02:00" % (
|
|
dt.year, dt.month, dt.day,
|
|
dt.hour, dt.minute, dt.second,
|
|
dt.microsecond)
|
|
|
|
self.assertEqual(expected, timeutils.isotime(dt, subsecond=True))
|
|
|
|
def test_bogus_unaware_subsecond(self):
|
|
dt = datetime.now(bogus_tzinfo())
|
|
|
|
expected = "%04d-%02d-%02dT%02d:%02d:%02d.%06d+02:00" % (
|
|
dt.year, dt.month, dt.day,
|
|
dt.hour, dt.minute, dt.second,
|
|
dt.microsecond)
|
|
|
|
self.assertEqual(expected, timeutils.isotime(dt, subsecond=True))
|
|
|
|
def test_throws_exception(self):
|
|
dt = datetime.now()
|
|
dt = dt.replace(tzinfo=invalid_tzinfo())
|
|
|
|
self.assertRaises(ValueError, timeutils.isotime, dt)
|