Files
deb-python-pysaml2/src/saml2/time_util.py
2011-03-14 12:58:15 +01:00

343 lines
9.8 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2006 Google Inc.
# Copyright (C) 2009 Umeå University
#
# 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.
"""
Implements some usefull functions when dealing with validity of
different types of information.
"""
import re
import time
from datetime import timedelta
from datetime import datetime
TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
TIME_FORMAT_WITH_FRAGMENT = re.compile(
"^(\d{4,4}-\d{2,2}-\d{2,2}T\d{2,2}:\d{2,2}:\d{2,2})\.\d*Z$")
# ---------------------------------------------------------------------------
#I'm sure this is implemeted somewhere else cann't find it now though, so I
#made an attempt.
#Implemented according to
#http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/
#adding-durations-to-dateTimes
def f_quotient(arg0, arg1, arg2=0):
if arg2:
return int((arg0-arg1)/(arg2-arg1))
elif not arg0:
return 0
else:
return int(arg0/arg1)
def modulo(arg0, arg1, arg2=0):
if arg2:
return ((arg0 - arg1) % (arg2 - arg1)) + arg1
else:
return arg0 % arg1
DAYS_IN_MONTH = {
1: 31,
3: 31,
4: 30,
5: 31,
6: 30,
7: 31,
8: 31,
9: 30,
10: 31,
11: 30,
12: 31,
}
def days_in_february(year):
if not modulo(year, 400):
return 29
elif (modulo(year, 100) != 0) and (modulo(year, 4) == 0):
return 29
else:
return 28
def maximum_day_in_month_for(year_value, month_value):
month = modulo(month_value, 1, 13)
year = year_value + f_quotient(month_value, 1, 13)
try:
return DAYS_IN_MONTH[month]
except KeyError:
return days_in_february(year)
D_FORMAT = [
("Y", "tm_year"),
("M", "tm_mon"),
("D", "tm_mday"),
("T", None),
("H", "tm_hour"),
("M", "tm_min"),
("S", "tm_sec")
]
def parse_duration(duration):
# (-)PnYnMnDTnHnMnS
index = 0
if duration[0] == '-':
sign = '-'
index += 1
else:
sign = '+'
assert duration[index] == "P"
index += 1
dic = dict([(typ, 0) for (code, typ) in D_FORMAT])
for code, typ in D_FORMAT:
#print duration[index:], code
if duration[index] == '-':
raise Exception("Negation not allowed on individual items")
if code == "T":
if duration[index] == "T":
index += 1
if index == len(duration):
raise Exception("Not allowed to end with 'T'")
else:
raise Exception("Missing T")
else:
try:
mod = duration[index:].index(code)
try:
dic[typ] = int(duration[index:index+mod])
except ValueError:
if code == "S":
try:
dic[typ] = float(duration[index:index+mod])
except ValueError:
raise Exception("Not a float")
else:
raise Exception(
"Fractions not allow on anything byt seconds")
index = mod+index+1
except ValueError:
dic[typ] = 0
if index == len(duration):
break
return sign, dic
def add_duration(tid, duration):
(sign, dur) = parse_duration(duration)
if sign == '+':
#Months
temp = tid.tm_mon + dur["tm_mon"]
month = modulo(temp, 1, 13)
carry = f_quotient(temp, 1, 13)
#Years
year = tid.tm_year + dur["tm_year"] + carry
# seconds
temp = tid.tm_sec + dur["tm_sec"]
secs = modulo(temp, 60)
carry = f_quotient(temp, 60)
# minutes
temp = tid.tm_min + dur["tm_min"] + carry
minutes = modulo(temp, 60)
carry = f_quotient(temp, 60)
# hours
temp = tid.tm_hour + dur["tm_hour"] + carry
hour = modulo(temp, 60)
carry = f_quotient(temp, 60)
# days
if dur["tm_mday"] > maximum_day_in_month_for(year, month):
temp_days = maximum_day_in_month_for(year, month)
elif dur["tm_mday"] < 1:
temp_days = 1
else:
temp_days = dur["tm_mday"]
days = temp_days + tid.tm_mday + carry
while True:
if days < 1:
pass
elif days > maximum_day_in_month_for(year, month):
days = days - maximum_day_in_month_for(year, month)
carry = 1
else:
break
temp = month + carry
month = modulo(temp, 1, 13)
year = year + f_quotient(temp, 1, 13)
return time.localtime(time.mktime((year, month, days, hour, minutes,
secs, 0, 0, -1)))
else:
pass
# ---------------------------------------------------------------------------
def time_in_a_while(days=0, seconds=0, microseconds=0, milliseconds=0,
minutes=0, hours=0, weeks=0):
"""
format of timedelta:
timedelta([days[, seconds[, microseconds[, milliseconds[,
minutes[, hours[, weeks]]]]]]])
"""
now = datetime.utcnow()
delta = timedelta(*[days, seconds, microseconds, milliseconds, minutes,
hours, weeks])
soon = now + delta
return soon
def time_a_while_ago(days=0, seconds=0, microseconds=0, milliseconds=0,
minutes=0, hours=0, weeks=0):
"""
format of timedelta:
timedelta([days[, seconds[, microseconds[, milliseconds[,
minutes[, hours[, weeks]]]]]]])
"""
now = datetime.utcnow()
delta = timedelta(*[days, seconds, microseconds, milliseconds, minutes,
hours, weeks])
prev = now - delta
return prev
def in_a_while(days=0, seconds=0, microseconds=0, milliseconds=0,
minutes=0, hours=0, weeks=0, format=None):
"""
format of timedelta:
timedelta([days[, seconds[, microseconds[, milliseconds[,
minutes[, hours[, weeks]]]]]]])
"""
if not format:
format = TIME_FORMAT
return time_in_a_while(days, seconds, microseconds, milliseconds,
minutes, hours, weeks).strftime(format)
def a_while_ago(days=0, seconds=0, microseconds=0, milliseconds=0,
minutes=0, hours=0, weeks=0, format=None):
if not format:
format = TIME_FORMAT
return time_a_while_ago(days, seconds, microseconds, milliseconds,
minutes, hours, weeks).strftime(format)
# ---------------------------------------------------------------------------
def shift_time(dtime, shift):
""" Adds/deletes an integer amount of seconds from a datetime specification
:param dtime: The datatime specification
:param shift: The wanted time shift (+/-)
:return: A shifted datatime specification
"""
tstruct = dtime.timetuple()
tfl = time.mktime(tstruct)
tfl += shift
return datetime.utcfromtimestamp(tfl)
# ---------------------------------------------------------------------------
def str_to_time(timestr):
if not timestr:
return 0
try:
then = time.strptime(timestr, TIME_FORMAT)
except Exception: # assume it's a format problem
try:
elem = TIME_FORMAT_WITH_FRAGMENT.match(timestr)
except Exception, exc:
print "Exception: %s on %s" % (exc, timestr)
raise
then = time.strptime(elem.groups()[0]+"Z", TIME_FORMAT)
return then
def instant(format=None):
if not format:
format = TIME_FORMAT
return datetime.utcnow().strftime(format)
# ---------------------------------------------------------------------------
def utc_now():
return time.mktime(datetime.utcnow().timetuple())
# ---------------------------------------------------------------------------
def not_before(point):
if not point:
return True
then = str_to_time(point)
now = time.gmtime()
if now > then:
return True
else:
return False
# def not_on_or_after(point):
# if not point:
# return True
#
# then = str_to_time(point)
# now = time.gmtime()
#
# if now >= then:
# return False
# else:
# return True
def not_on_or_after(not_on_or_after):
if isinstance(not_on_or_after, time.struct_time):
not_on_or_after = time.mktime(not_on_or_after)
elif isinstance(not_on_or_after, basestring):
not_on_or_after = str_to_time(not_on_or_after)
if not not_on_or_after:
return True
now = utc_now()
if not_on_or_after and not_on_or_after < now:
#self.reset(subject_id, entity_id)
#raise ToOld("%s < %s" % (not_on_or_after, now))
return False
else:
return True
def valid( valid_until ):
""" Checks whether a valid_until specification is still valid
:param valid_until: The string representation of a time
:return: True if the time specified in valid_until is now or sometime
in the future. Otherwise False.
"""
if not valid_until:
return True
then = str_to_time( valid_until )
now = datetime.utcnow().timetuple()
if now <= then:
return True
else:
return False
def later_than(then, that):
then = str_to_time( then )
that = str_to_time( that )
return then >= that