00001
00002
00003 '''Typecodes for dates and times.
00004 '''
00005
00006 from ZSI import _copyright, _floattypes, _inttypes, _get_idstr, EvaluateException
00007 from ZSI.TC import TypeCode, SimpleType
00008 from ZSI.wstools.Namespaces import SCHEMA
00009 import operator, re, time as _time
00010 from time import mktime as _mktime, localtime as _localtime, gmtime as _gmtime
00011 from datetime import tzinfo as _tzinfo, timedelta as _timedelta,\
00012 datetime as _datetime
00013 from math import modf as _modf
00014
00015 _niltime = [
00016 0, 0, 0,
00017 0, 0, 0,
00018 0, 0, 0
00019 ]
00020
00021
00022 _zero = _timedelta(0)
00023 _dstoffset = _stdoffset = _timedelta(seconds=-_time.timezone)
00024 if _time.daylight: _dstoffset = _timedelta(seconds=-_time.altzone)
00025 _dstdiff = _dstoffset - _stdoffset
00026
00027
00028 class _localtimezone(_tzinfo):
00029 """ """
00030 def dst(self, dt):
00031 """datetime -> DST offset in minutes east of UTC."""
00032 tt = _localtime(_mktime((dt.year, dt.month, dt.day,
00033 dt.hour, dt.minute, dt.second, dt.weekday(), 0, -1)))
00034 if tt.tm_isdst > 0: return _dstdiff
00035 return _zero
00036
00037
00038
00039
00040 def tzname(self, dt):
00041 """datetime -> string name of time zone."""
00042 tt = _localtime(_mktime((dt.year, dt.month, dt.day,
00043 dt.hour, dt.minute, dt.second, dt.weekday(), 0, -1)))
00044 return _time.tzname[tt.tm_isdst > 0]
00045
00046 def utcoffset(self, dt):
00047 """datetime -> minutes east of UTC (negative for west of UTC)."""
00048 tt = _localtime(_mktime((dt.year, dt.month, dt.day,
00049 dt.hour, dt.minute, dt.second, dt.weekday(), 0, -1)))
00050 if tt.tm_isdst > 0: return _dstoffset
00051 return _stdoffset
00052
00053 class _fixedoffset(_tzinfo):
00054 """Fixed offset in minutes east from UTC.
00055
00056 A class building tzinfo objects for fixed-offset time zones.
00057 Note that _fixedoffset(0, "UTC") is a different way to build a
00058 UTC tzinfo object.
00059 """
00060
00061 def __init__(self, offset):
00062 self.__offset = _timedelta(minutes=offset)
00063
00064
00065 def dst(self, dt):
00066 """datetime -> DST offset in minutes east of UTC."""
00067 return _zero
00068
00069 def tzname(self, dt):
00070 """datetime -> string name of time zone."""
00071
00072 return "server"
00073
00074 def utcoffset(self, dt):
00075 """datetime -> minutes east of UTC (negative for west of UTC)."""
00076 return self.__offset
00077
00078
00079 def _dict_to_tuple(d):
00080 '''Convert a dictionary to a time tuple. Depends on key values in the
00081 regexp pattern!
00082 '''
00083
00084
00085
00086
00087
00088
00089
00090 retval = _niltime[:]
00091 for k,i in ( ('Y', 0), ('M', 1), ('D', 2), ('h', 3), ('m', 4), ):
00092 v = d.get(k)
00093 if v: retval[i] = int(v)
00094
00095 v = d.get('s')
00096 if v:
00097 msec,sec = _modf(float(v))
00098 retval[6],retval[5] = int(round(msec*1000)), int(sec)
00099
00100 v = d.get('tz')
00101 if v and v != 'Z':
00102 h,m = map(int, v.split(':'))
00103
00104
00105 offset=_localtimezone().utcoffset(_datetime.now())
00106 local_offset_hour = offset.seconds/3600
00107 local_offset_min = (offset.seconds%3600)%60
00108 if local_offset_hour > 12:
00109 local_offset_hour -= 24
00110
00111 if local_offset_hour != h or local_offset_min != m:
00112 if h<0:
00113
00114
00115 foff = _fixedoffset(-((abs(h)*60+m)))
00116 else:
00117
00118
00119 foff = _fixedoffset((abs(h)*60+m))
00120
00121 dt = _datetime(retval[0],retval[1],retval[2],retval[3],retval[4],
00122 retval[5],0,foff)
00123
00124
00125 localdt=dt.astimezone(_localtimezone())
00126 retval[0] = localdt.year
00127 retval[1] = localdt.month
00128 retval[2] = localdt.day
00129 retval[3] = localdt.hour
00130 retval[4] = localdt.minute
00131 retval[5] = localdt.second
00132
00133 if d.get('neg', 0):
00134 retval[0:5] = map(operator.__neg__, retval[0:5])
00135 return tuple(retval)
00136
00137
00138 class Duration(SimpleType):
00139 '''Time duration.
00140 '''
00141 parselist = [ (None,'duration') ]
00142 lex_pattern = re.compile('^' r'(?P<neg>-?)P' \
00143 r'((?P<Y>\d+)Y)?' r'((?P<M>\d+)M)?' r'((?P<D>\d+)D)?' \
00144 r'(?P<T>T?)' r'((?P<h>\d+)H)?' r'((?P<m>\d+)M)?' \
00145 r'((?P<s>\d*(\.\d+)?)S)?' '$')
00146 type = (SCHEMA.XSD3, 'duration')
00147
00148
00149 def text_to_data(self, text, elt, ps):
00150 '''convert text into typecode specific data.
00151 '''
00152 if text is None:
00153 return None
00154 m = Duration.lex_pattern.match(text)
00155 if m is None:
00156 raise EvaluateException('Illegal duration', ps.Backtrace(elt))
00157 d = m.groupdict()
00158 if d['T'] and (d['h'] is None and d['m'] is None and d['s'] is None):
00159 raise EvaluateException('Duration has T without time')
00160 try:
00161 retval = _dict_to_tuple(d)
00162 except ValueError, e:
00163 raise EvaluateException(str(e))
00164
00165 if self.pyclass is not None:
00166 return self.pyclass(retval)
00167 return retval
00168
00169 def get_formatted_content(self, pyobj):
00170 if type(pyobj) in _floattypes or type(pyobj) in _inttypes:
00171 pyobj = _gmtime(pyobj)
00172
00173 d = {}
00174 pyobj = tuple(pyobj)
00175 if 1 in map(lambda x: x < 0, pyobj[0:6]):
00176 pyobj = map(abs, pyobj)
00177 neg = '-'
00178 else:
00179 neg = ''
00180
00181 val = '%sP%dY%dM%dDT%dH%dM%dS' % \
00182 ( neg, pyobj[0], pyobj[1], pyobj[2], pyobj[3], pyobj[4], pyobj[5])
00183
00184 return val
00185
00186
00187 class Gregorian(SimpleType):
00188 '''Gregorian times.
00189 '''
00190 lex_pattern = tag = format = None
00191
00192 def text_to_data(self, text, elt, ps):
00193 '''convert text into typecode specific data.
00194 '''
00195 if text is None:
00196 return None
00197
00198 m = self.lex_pattern.match(text)
00199 if not m:
00200 raise EvaluateException('Bad Gregorian: %s' %text, ps.Backtrace(elt))
00201 try:
00202 retval = _dict_to_tuple(m.groupdict())
00203 except ValueError, e:
00204
00205 raise
00206
00207 if self.pyclass is not None:
00208 return self.pyclass(retval)
00209 return retval
00210
00211 def get_formatted_content(self, pyobj):
00212 if type(pyobj) in _floattypes or type(pyobj) in _inttypes:
00213 pyobj = _gmtime(pyobj)
00214
00215 d = {}
00216 pyobj = tuple(pyobj)
00217 if 1 in map(lambda x: x < 0, pyobj[0:6]):
00218 pyobj = map(abs, pyobj)
00219 d['neg'] = '-'
00220 else:
00221 d['neg'] = ''
00222
00223 ms = pyobj[6]
00224 if not ms or not hasattr(self, 'format_ms'):
00225 d = { 'Y': pyobj[0], 'M': pyobj[1], 'D': pyobj[2],
00226 'h': pyobj[3], 'm': pyobj[4], 's': pyobj[5], }
00227 return self.format % d
00228
00229 if ms > 999:
00230 raise ValueError, 'milliseconds must be a integer between 0 and 999'
00231
00232 d = { 'Y': pyobj[0], 'M': pyobj[1], 'D': pyobj[2],
00233 'h': pyobj[3], 'm': pyobj[4], 's': pyobj[5], 'ms':ms, }
00234 return self.format_ms % d
00235
00236
00237 class gDateTime(Gregorian):
00238 '''A date and time.
00239 '''
00240 parselist = [ (None,'dateTime') ]
00241 lex_pattern = re.compile('^' r'(?P<neg>-?)' \
00242 '(?P<Y>\d{4,})-' r'(?P<M>\d\d)-' r'(?P<D>\d\d)' 'T' \
00243 r'(?P<h>\d\d):' r'(?P<m>\d\d):' r'(?P<s>\d*(\.\d+)?)' \
00244 r'(?P<tz>(Z|([-+]\d\d:\d\d))?)' '$')
00245 tag, format = 'dateTime', '%(Y)04d-%(M)02d-%(D)02dT%(h)02d:%(m)02d:%(s)02dZ'
00246 format_ms = format[:-1] + '.%(ms)03dZ'
00247 type = (SCHEMA.XSD3, 'dateTime')
00248
00249 class gDate(Gregorian):
00250 '''A date.
00251 '''
00252 parselist = [ (None,'date') ]
00253 lex_pattern = re.compile('^' r'(?P<neg>-?)' \
00254 '(?P<Y>\d{4,})-' r'(?P<M>\d\d)-' r'(?P<D>\d\d)' \
00255 r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
00256 tag, format = 'date', '%(Y)04d-%(M)02d-%(D)02dZ'
00257 type = (SCHEMA.XSD3, 'date')
00258
00259 class gYearMonth(Gregorian):
00260 '''A date.
00261 '''
00262 parselist = [ (None,'gYearMonth') ]
00263 lex_pattern = re.compile('^' r'(?P<neg>-?)' \
00264 '(?P<Y>\d{4,})-' r'(?P<M>\d\d)' \
00265 r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
00266 tag, format = 'gYearMonth', '%(Y)04d-%(M)02dZ'
00267 type = (SCHEMA.XSD3, 'gYearMonth')
00268
00269 class gYear(Gregorian):
00270 '''A date.
00271 '''
00272 parselist = [ (None,'gYear') ]
00273 lex_pattern = re.compile('^' r'(?P<neg>-?)' \
00274 '(?P<Y>\d{4,})' \
00275 r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
00276 tag, format = 'gYear', '%(Y)04dZ'
00277 type = (SCHEMA.XSD3, 'gYear')
00278
00279 class gMonthDay(Gregorian):
00280 '''A gMonthDay.
00281 '''
00282 parselist = [ (None,'gMonthDay') ]
00283 lex_pattern = re.compile('^' r'(?P<neg>-?)' \
00284 r'--(?P<M>\d\d)-' r'(?P<D>\d\d)' \
00285 r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
00286 tag, format = 'gMonthDay', '---%(M)02d-%(D)02dZ'
00287 type = (SCHEMA.XSD3, 'gMonthDay')
00288
00289
00290 class gDay(Gregorian):
00291 '''A gDay.
00292 '''
00293 parselist = [ (None,'gDay') ]
00294 lex_pattern = re.compile('^' r'(?P<neg>-?)' \
00295 r'---(?P<D>\d\d)' \
00296 r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
00297 tag, format = 'gDay', '---%(D)02dZ'
00298 type = (SCHEMA.XSD3, 'gDay')
00299
00300 class gMonth(Gregorian):
00301 '''A gMonth.
00302 '''
00303 parselist = [ (None,'gMonth') ]
00304 lex_pattern = re.compile('^' r'(?P<neg>-?)' \
00305 r'---(?P<M>\d\d)' \
00306 r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
00307 tag, format = 'gMonth', '---%(M)02dZ'
00308 type = (SCHEMA.XSD3, 'gMonth')
00309
00310 class gTime(Gregorian):
00311 '''A time.
00312 '''
00313 parselist = [ (None,'time') ]
00314 lex_pattern = re.compile('^' r'(?P<neg>-?)' \
00315 r'(?P<h>\d\d):' r'(?P<m>\d\d):' r'(?P<s>\d*(\.\d+)?)' \
00316 r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
00317 tag, format = 'time', '%(h)02d:%(m)02d:%(s)02dZ'
00318 format_ms = format[:-1] + '.%(ms)03dZ'
00319 type = (SCHEMA.XSD3, 'time')
00320
00321 if __name__ == '__main__': print _copyright