Package suds :: Package sax :: Module date
[hide private]
[frames] | no frames]

Source Code for Module suds.sax.date

  1  # This program is free software; you can redistribute it and/or modify 
  2  # it under the terms of the (LGPL) GNU Lesser General Public License as 
  3  # published by the Free Software Foundation; either version 3 of the  
  4  # License, or (at your option) any later version. 
  5  # 
  6  # This program is distributed in the hope that it will be useful, 
  7  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
  8  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  9  # GNU Library Lesser General Public License for more details at 
 10  # ( http://www.gnu.org/licenses/lgpl.html ). 
 11  # 
 12  # You should have received a copy of the GNU Lesser General Public License 
 13  # along with this program; if not, write to the Free Software 
 14  # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
 15  # written by: Nathan Van Gheem (vangheem@gmail.com) 
 16   
 17  """ 
 18  The I{xdate} module provides classes for converstion 
 19  between XML dates and python objects. 
 20  """ 
 21   
 22  from logging import getLogger 
 23  from suds import * 
 24  from suds.xsd import * 
 25  import time 
 26  import datetime as dt 
 27  import re 
 28   
 29  log = getLogger(__name__) 
30 31 32 -class Date:
33 """ 34 An XML date object. 35 Supported formats: 36 - YYYY-MM-DD 37 - YYYY-MM-DD(z|Z) 38 - YYYY-MM-DD+06:00 39 - YYYY-MM-DD-06:00 40 @ivar date: The object value. 41 @type date: B{datetime}.I{date} 42 """
43 - def __init__(self, date):
44 """ 45 @param date: The value of the object. 46 @type date: (date|str) 47 @raise ValueError: When I{date} is invalid. 48 """ 49 if isinstance(date, dt.date): 50 self.date = date 51 return 52 if isinstance(date, basestring): 53 self.date = self.__parse(date) 54 return 55 raise ValueError, type(date)
56
57 - def year(self):
58 """ 59 Get the I{year} component. 60 @return: The year. 61 @rtype: int 62 """ 63 return self.date.year
64
65 - def month(self):
66 """ 67 Get the I{month} component. 68 @return: The month. 69 @rtype: int 70 """ 71 return self.date.month
72
73 - def day(self):
74 """ 75 Get the I{day} component. 76 @return: The day. 77 @rtype: int 78 """ 79 return self.date.day
80
81 - def __parse(self, s):
82 """ 83 Parse the string date. 84 Supported formats: 85 - YYYY-MM-DD 86 - YYYY-MM-DD(z|Z) 87 - YYYY-MM-DD+06:00 88 - YYYY-MM-DD-06:00 89 Although, the TZ is ignored because it's meaningless 90 without the time, right? 91 @param s: A date string. 92 @type s: str 93 @return: A date object. 94 @rtype: I{date} 95 """ 96 try: 97 year, month, day = s[:10].split('-', 2) 98 year = int(year) 99 month = int(month) 100 day = int(day) 101 return dt.date(year, month, day) 102 except: 103 log.debug(s, exec_info=True) 104 raise ValueError, 'Invalid format "%s"' % s
105
106 - def __str__(self):
107 return unicode(self)
108
109 - def __unicode__(self):
110 return self.date.isoformat()
111
112 113 -class Time:
114 """ 115 An XML time object. 116 Supported formats: 117 - HH:MI:SS 118 - HH:MI:SS(z|Z) 119 - HH:MI:SS.ms 120 - HH:MI:SS.ms(z|Z) 121 - HH:MI:SS(+|-)06:00 122 - HH:MI:SS.ms(+|-)06:00 123 @ivar tz: The timezone 124 @type tz: L{Timezone} 125 @ivar date: The object value. 126 @type date: B{datetime}.I{time} 127 """ 128
129 - def __init__(self, time, adjusted=True):
130 """ 131 @param time: The value of the object. 132 @type time: (time|str) 133 @param adjusted: Adjust for I{local} Timezone. 134 @type adjusted: boolean 135 @raise ValueError: When I{time} is invalid. 136 """ 137 self.tz = Timezone() 138 if isinstance(time, dt.time): 139 self.time = time 140 return 141 if isinstance(time, basestring): 142 self.time = self.__parse(time) 143 if adjusted: 144 self.__adjust() 145 return 146 raise ValueError, type(time)
147
148 - def hour(self):
149 """ 150 Get the I{hour} component. 151 @return: The hour. 152 @rtype: int 153 """ 154 return self.time.hour
155
156 - def minute(self):
157 """ 158 Get the I{minute} component. 159 @return: The minute. 160 @rtype: int 161 """ 162 return self.time.minute
163
164 - def second(self):
165 """ 166 Get the I{seconds} component. 167 @return: The seconds. 168 @rtype: int 169 """ 170 return self.time.second
171
172 - def microsecond(self):
173 """ 174 Get the I{microsecond} component. 175 @return: The microsecond. 176 @rtype: int 177 """ 178 return self.time.microsecond
179
180 - def __adjust(self):
181 """ 182 Adjust for TZ offset. 183 """ 184 if hasattr(self, 'offset'): 185 today = dt.date.today() 186 delta = self.tz.adjustment(self.offset) 187 d = dt.datetime.combine(today, self.time) 188 d = ( d + delta ) 189 self.time = d.time()
190
191 - def __parse(self, s):
192 """ 193 Parse the string date. 194 Patterns: 195 - HH:MI:SS 196 - HH:MI:SS(z|Z) 197 - HH:MI:SS.ms 198 - HH:MI:SS.ms(z|Z) 199 - HH:MI:SS(+|-)06:00 200 - HH:MI:SS.ms(+|-)06:00 201 @param s: A time string. 202 @type s: str 203 @return: A time object. 204 @rtype: B{datetime}.I{time} 205 """ 206 try: 207 offset = None 208 part = Timezone.split(s) 209 hour, minute, second = part[0].split(':', 2) 210 hour = int(hour) 211 minute = int(minute) 212 second, ms = self.__second(second) 213 if len(part) == 2: 214 self.offset = self.__offset(part[1]) 215 if ms is None: 216 return dt.time(hour, minute, second) 217 else: 218 return dt.time(hour, minute, second, ms) 219 except: 220 log.debug(s, exec_info=True) 221 raise ValueError, 'Invalid format "%s"' % s
222
223 - def __second(self, s):
224 """ 225 Parse the seconds and microseconds. 226 The microseconds are truncated to 999999 due to a restriction in 227 the python datetime.datetime object. 228 @param s: A string representation of the seconds. 229 @type s: str 230 @return: Tuple of (sec,ms) 231 @rtype: tuple. 232 """ 233 part = s.split('.') 234 if len(part) > 1: 235 return (int(part[0]), int(part[1][:6])) 236 else: 237 return (int(part[0]), None)
238
239 - def __offset(self, s):
240 """ 241 Parse the TZ offset. 242 @param s: A string representation of the TZ offset. 243 @type s: str 244 @return: The signed offset in hours. 245 @rtype: str 246 """ 247 if len(s) == len('-00:00'): 248 return int(s[:3]) 249 if len(s) == 0: 250 return self.tz.local 251 if len(s) == 1: 252 return 0 253 raise Exception()
254
255 - def __str__(self):
256 return unicode(self)
257
258 - def __unicode__(self):
259 time = self.time.isoformat() 260 if self.tz.local: 261 return '%s%+.2d:00' % (time, self.tz.local) 262 else: 263 return '%sZ' % time
264
265 266 -class DateTime(Date,Time):
267 """ 268 An XML time object. 269 Supported formats: 270 - YYYY-MM-DDB{T}HH:MI:SS 271 - YYYY-MM-DDB{T}HH:MI:SS(z|Z) 272 - YYYY-MM-DDB{T}HH:MI:SS.ms 273 - YYYY-MM-DDB{T}HH:MI:SS.ms(z|Z) 274 - YYYY-MM-DDB{T}HH:MI:SS(+|-)06:00 275 - YYYY-MM-DDB{T}HH:MI:SS.ms(+|-)06:00 276 @ivar datetime: The object value. 277 @type datetime: B{datetime}.I{datedate} 278 """
279 - def __init__(self, date):
280 """ 281 @param date: The value of the object. 282 @type date: (datetime|str) 283 @raise ValueError: When I{tm} is invalid. 284 """ 285 if isinstance(date, dt.datetime): 286 Date.__init__(self, date.date()) 287 Time.__init__(self, date.time()) 288 self.datetime = \ 289 dt.datetime.combine(self.date, self.time) 290 return 291 if isinstance(date, basestring): 292 part = date.split('T') 293 Date.__init__(self, part[0]) 294 Time.__init__(self, part[1], 0) 295 self.datetime = \ 296 dt.datetime.combine(self.date, self.time) 297 self.__adjust() 298 return 299 raise ValueError, type(date)
300
301 - def __adjust(self):
302 """ 303 Adjust for TZ offset. 304 """ 305 if not hasattr(self, 'offset'): 306 return 307 delta = self.tz.adjustment(self.offset) 308 try: 309 d = ( self.datetime + delta ) 310 self.datetime = d 311 self.date = d.date() 312 self.time = d.time() 313 except OverflowError: 314 log.warn('"%s" caused overflow, not-adjusted', self.datetime)
315
316 - def __str__(self):
317 return unicode(self)
318
319 - def __unicode__(self):
320 s = [] 321 s.append(Date.__unicode__(self)) 322 s.append(Time.__unicode__(self)) 323 return 'T'.join(s)
324
325 326 -class UTC(DateTime):
327 """ 328 Represents current UTC time. 329 """ 330
331 - def __init__(self, date=None):
332 if date is None: 333 date = dt.datetime.utcnow() 334 DateTime.__init__(self, date) 335 self.tz.local = 0
336
337 338 -class Timezone:
339 """ 340 Timezone object used to do TZ conversions 341 @cvar local: The (A) local TZ offset. 342 @type local: int 343 @cvar patten: The regex patten to match TZ. 344 @type patten: re.Pattern 345 """ 346 347 pattern = re.compile('([zZ])|([\-\+][0-9]{2}:[0-9]{2})') 348 349 LOCAL = ( 0-time.timezone/60/60 ) + time.daylight 350
351 - def __init__(self, offset=None):
352 if offset is None: 353 offset = self.LOCAL 354 self.local = offset
355 356 @classmethod
357 - def split(cls, s):
358 """ 359 Split the TZ from string. 360 @param s: A string containing a timezone 361 @type s: basestring 362 @return: The split parts. 363 @rtype: tuple 364 """ 365 m = cls.pattern.search(s) 366 if m is None: 367 return (s,) 368 x = m.start(0) 369 return (s[:x], s[x:])
370
371 - def adjustment(self, offset):
372 """ 373 Get the adjustment to the I{local} TZ. 374 @return: The delta between I{offset} and local TZ. 375 @rtype: B{datetime}.I{timedelta} 376 """ 377 delta = ( self.local - offset ) 378 return dt.timedelta(hours=delta)
379