diff options
author | Kevin McCarthy <kmccarth@redhat.com> | 2007-09-04 13:44:59 -0700 |
---|---|---|
committer | Kevin McCarthy <kmccarth@redhat.com> | 2007-09-04 13:44:59 -0700 |
commit | 3afd023c3aad0ade7ce391ba18dcd2c9c8e59426 (patch) | |
tree | 2f1b6a646e33f496d81aa80131d6b5ed44814055 | |
parent | 584baa7ee21f22db6978efc89de1f1491768fab5 (diff) | |
download | freeipa.git-3afd023c3aad0ade7ce391ba18dcd2c9c8e59426.tar.gz freeipa.git-3afd023c3aad0ade7ce391ba18dcd2c9c8e59426.tar.xz freeipa.git-3afd023c3aad0ade7ce391ba18dcd2c9c8e59426.zip |
Generalized Time parser and tests, for use in krbPasswordExpiration
-rw-r--r-- | ipa-python/ipautil.py | 98 | ||||
-rw-r--r-- | ipa-python/test/test_ipautil.py | 97 |
2 files changed, 195 insertions, 0 deletions
diff --git a/ipa-python/ipautil.py b/ipa-python/ipautil.py index f6d62f7a..2989b421 100644 --- a/ipa-python/ipautil.py +++ b/ipa-python/ipautil.py @@ -30,6 +30,7 @@ import stat from string import lower import re import xmlrpclib +import datetime def realm_to_suffix(realm_name): s = realm_name.split(".") @@ -233,3 +234,100 @@ def unwrap_binary_data(data): else: return data +class GeneralizedTimeZone(datetime.tzinfo): + """This class is a basic timezone wrapper for the offset specified + in a Generalized Time. It is dst-ignorant.""" + def __init__(self,offsetstr="Z"): + super(GeneralizedTimeZone, self).__init__() + + self.name = offsetstr + self.houroffset = 0 + self.minoffset = 0 + + if offsetstr == "Z": + self.houroffset = 0 + self.minoffset = 0 + else: + if (len(offsetstr) >= 3) and re.match(r'[-+]\d\d', offsetstr): + self.houroffset = int(offsetstr[0:3]) + offsetstr = offsetstr[3:] + if (len(offsetstr) >= 2) and re.match(r'\d\d', offsetstr): + self.minoffset = int(offsetstr[0:2]) + offsetstr = offsetstr[2:] + if len(offsetstr) > 0: + raise ValueError() + if self.houroffset < 0: + self.minoffset *= -1 + + def utcoffset(self, dt): + return datetime.timedelta(hours=self.houroffset, minutes=self.minoffset) + + def dst(self, dt): + return datetime.timedelta(0) + + def tzname(self, dt): + return self.name + + +def parse_generalized_time(timestr): + """Parses are Generalized Time string (as specified in X.680), + returning a datetime object. Generalized Times are stored inside + the krbPasswordExpiration attribute in LDAP. + + This method doesn't attempt to be perfect wrt timezones. If python + can't be bothered to implement them, how can we...""" + + if len(timestr) < 8: + return None + try: + date = timestr[:8] + time = timestr[8:] + + year = int(date[:4]) + month = int(date[4:6]) + day = int(date[6:8]) + + hour = min = sec = msec = 0 + tzone = None + + if (len(time) >= 2) and re.match(r'\d', time[0]): + hour = int(time[:2]) + time = time[2:] + if len(time) >= 2 and (time[0] == "," or time[0] == "."): + hour_fraction = "." + time = time[1:] + while (len(time) > 0) and re.match(r'\d', time[0]): + hour_fraction += time[0] + time = time[1:] + total_secs = int(float(hour_fraction) * 3600) + min, sec = divmod(total_secs, 60) + + if (len(time) >= 2) and re.match(r'\d', time[0]): + min = int(time[:2]) + time = time[2:] + if len(time) >= 2 and (time[0] == "," or time[0] == "."): + min_fraction = "." + time = time[1:] + while (len(time) > 0) and re.match(r'\d', time[0]): + min_fraction += time[0] + time = time[1:] + sec = int(float(min_fraction) * 60) + + if (len(time) >= 2) and re.match(r'\d', time[0]): + sec = int(time[:2]) + time = time[2:] + if len(time) >= 2 and (time[0] == "," or time[0] == "."): + sec_fraction = "." + time = time[1:] + while (len(time) > 0) and re.match(r'\d', time[0]): + sec_fraction += time[0] + time = time[1:] + msec = int(float(sec_fraction) * 1000000) + + if (len(time) > 0): + tzone = GeneralizedTimeZone(time) + + return datetime.datetime(year, month, day, hour, min, sec, msec, tzone) + + except ValueError: + return None diff --git a/ipa-python/test/test_ipautil.py b/ipa-python/test/test_ipautil.py index 54ff1dc2..2755f71e 100644 --- a/ipa-python/test/test_ipautil.py +++ b/ipa-python/test/test_ipautil.py @@ -21,6 +21,7 @@ import sys sys.path.insert(0, ".") import unittest +import datetime import ipautil @@ -207,6 +208,102 @@ class TestCIDict(unittest.TestCase): self.assert_(item in items) items.discard(item) +class TestTimeParser(unittest.TestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + def testSimple(self): + timestr = "20070803" + + time = ipautil.parse_generalized_time(timestr) + self.assertEqual(2007, time.year) + self.assertEqual(8, time.month) + self.assertEqual(3, time.day) + self.assertEqual(0, time.hour) + self.assertEqual(0, time.minute) + self.assertEqual(0, time.second) + + def testHourMinSec(self): + timestr = "20051213141205" + + time = ipautil.parse_generalized_time(timestr) + self.assertEqual(2005, time.year) + self.assertEqual(12, time.month) + self.assertEqual(13, time.day) + self.assertEqual(14, time.hour) + self.assertEqual(12, time.minute) + self.assertEqual(5, time.second) + + def testFractions(self): + timestr = "2003092208.5" + + time = ipautil.parse_generalized_time(timestr) + self.assertEqual(2003, time.year) + self.assertEqual(9, time.month) + self.assertEqual(22, time.day) + self.assertEqual(8, time.hour) + self.assertEqual(30, time.minute) + self.assertEqual(0, time.second) + + timestr = "199203301544,25" + + time = ipautil.parse_generalized_time(timestr) + self.assertEqual(1992, time.year) + self.assertEqual(3, time.month) + self.assertEqual(30, time.day) + self.assertEqual(15, time.hour) + self.assertEqual(44, time.minute) + self.assertEqual(15, time.second) + + timestr = "20060401185912,8" + + time = ipautil.parse_generalized_time(timestr) + self.assertEqual(2006, time.year) + self.assertEqual(4, time.month) + self.assertEqual(1, time.day) + self.assertEqual(18, time.hour) + self.assertEqual(59, time.minute) + self.assertEqual(12, time.second) + self.assertEqual(800000, time.microsecond) + + def testTimeZones(self): + timestr = "20051213141205Z" + + time = ipautil.parse_generalized_time(timestr) + self.assertEqual(0, time.tzinfo.houroffset) + self.assertEqual(0, time.tzinfo.minoffset) + offset = time.tzinfo.utcoffset(None) + self.assertEqual(0, offset.seconds) + + timestr = "20051213141205+0500" + + time = ipautil.parse_generalized_time(timestr) + self.assertEqual(5, time.tzinfo.houroffset) + self.assertEqual(0, time.tzinfo.minoffset) + offset = time.tzinfo.utcoffset(None) + self.assertEqual(5 * 60 * 60, offset.seconds) + + timestr = "20051213141205-0500" + + time = ipautil.parse_generalized_time(timestr) + self.assertEqual(-5, time.tzinfo.houroffset) + self.assertEqual(0, time.tzinfo.minoffset) + # NOTE - the offset is always positive - it's minutes + # _east_ of UTC + offset = time.tzinfo.utcoffset(None) + self.assertEqual((24 - 5) * 60 * 60, offset.seconds) + + timestr = "20051213141205-0930" + + time = ipautil.parse_generalized_time(timestr) + self.assertEqual(-9, time.tzinfo.houroffset) + self.assertEqual(-30, time.tzinfo.minoffset) + offset = time.tzinfo.utcoffset(None) + self.assertEqual(((24 - 9) * 60 * 60) - (30 * 60), offset.seconds) + if __name__ == '__main__': unittest.main() |