343 lines
9.8 KiB
Python
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
|
|
|