mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-01-27 00:26:33 -06:00
Generalized Time parser and tests, for use in krbPasswordExpiration
This commit is contained in:
parent
584baa7ee2
commit
3afd023c3a
@ -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
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user