Package Gnumed :: Package pycommon :: Module gmDateTime
[frames] | no frames]

Source Code for Module Gnumed.pycommon.gmDateTime

   1  __doc__ = """ 
   2  GNUmed date/time handling. 
   3   
   4  This modules provides access to date/time handling 
   5  and offers an fuzzy timestamp implementation 
   6   
   7  It utilizes 
   8   
   9          - Python time 
  10          - Python datetime 
  11          - mxDateTime 
  12   
  13  Note that if you want locale-aware formatting you need to call 
  14   
  15          locale.setlocale(locale.LC_ALL, '') 
  16   
  17  somewhere before importing this script. 
  18   
  19  Note regarding UTC offsets 
  20  -------------------------- 
  21   
  22  Looking from Greenwich: 
  23          WEST (IOW "behind"): negative values 
  24          EAST (IOW "ahead"):  positive values 
  25   
  26  This is in compliance with what datetime.tzinfo.utcoffset() 
  27  does but NOT what time.altzone/time.timezone do ! 
  28   
  29  This module also implements a class which allows the 
  30  programmer to define the degree of fuzziness, uncertainty 
  31  or imprecision of the timestamp contained within. 
  32   
  33  This is useful in fields such as medicine where only partial 
  34  timestamps may be known for certain events. 
  35  """ 
  36  #=========================================================================== 
  37  __version__ = "$Revision: 1.34 $" 
  38  __author__ = "K. Hilbert <Karsten.Hilbert@gmx.net>" 
  39  __license__ = "GPL (details at http://www.gnu.org)" 
  40   
  41  # stdlib 
  42  import sys, datetime as pyDT, time, os, re as regex, locale, logging 
  43   
  44   
  45  # 3rd party 
  46  import mx.DateTime as mxDT 
  47  import psycopg2                                         # this will go once datetime has timezone classes 
  48   
  49   
  50  if __name__ == '__main__': 
  51          sys.path.insert(0, '../../') 
  52  from Gnumed.pycommon import gmI18N 
  53   
  54   
  55  _log = logging.getLogger('gm.datetime') 
  56  _log.info(__version__) 
  57  _log.info(u'mx.DateTime version: %s', mxDT.__version__) 
  58   
  59  dst_locally_in_use = None 
  60  dst_currently_in_effect = None 
  61   
  62  current_local_utc_offset_in_seconds = None 
  63  current_local_timezone_interval = None 
  64  current_local_iso_numeric_timezone_string = None 
  65  current_local_timezone_name = None 
  66  py_timezone_name = None 
  67  py_dst_timezone_name = None 
  68   
  69  cLocalTimezone = psycopg2.tz.LocalTimezone                                      # remove as soon as datetime supports timezone classes 
  70  cFixedOffsetTimezone = psycopg2.tz.FixedOffsetTimezone          # remove as soon as datetime supports timezone classes 
  71  gmCurrentLocalTimezone = 'gmCurrentLocalTimezone not initialized' 
  72   
  73   
  74  (       acc_years, 
  75          acc_months, 
  76          acc_weeks, 
  77          acc_days, 
  78          acc_hours, 
  79          acc_minutes, 
  80          acc_seconds, 
  81          acc_subseconds 
  82  ) = range(1,9) 
  83   
  84  _accuracy_strings = { 
  85          1: 'years', 
  86          2: 'months', 
  87          3: 'weeks', 
  88          4: 'days', 
  89          5: 'hours', 
  90          6: 'minutes', 
  91          7: 'seconds', 
  92          8: 'subseconds' 
  93  } 
  94   
  95  gregorian_month_length = { 
  96          1: 31, 
  97          2: 28,          # FIXME: make leap year aware 
  98          3: 31, 
  99          4: 30, 
 100          5: 31, 
 101          6: 30, 
 102          7: 31, 
 103          8: 31, 
 104          9: 30, 
 105          10: 31, 
 106          11: 30, 
 107          12: 31 
 108  } 
 109   
 110  avg_days_per_gregorian_year = 365 
 111  avg_days_per_gregorian_month = 30 
 112  avg_seconds_per_day = 24 * 60 * 60 
 113  days_per_week = 7 
 114   
 115  #=========================================================================== 
 116  # module init 
 117  #--------------------------------------------------------------------------- 
118 -def init():
119 120 _log.debug('mx.DateTime.now(): [%s]' % mxDT.now()) 121 _log.debug('datetime.now() : [%s]' % pyDT.datetime.now()) 122 _log.debug('time.localtime() : [%s]' % str(time.localtime())) 123 _log.debug('time.gmtime() : [%s]' % str(time.gmtime())) 124 125 try: 126 _log.debug('$TZ: [%s]' % os.environ['TZ']) 127 except KeyError: 128 _log.debug('$TZ not defined') 129 130 _log.debug('time.daylight: [%s] (whether or not DST is locally used at all)' % time.daylight) 131 _log.debug('time.timezone: [%s] seconds' % time.timezone) 132 _log.debug('time.altzone : [%s] seconds' % time.altzone) 133 _log.debug('time.tzname : [%s / %s] (non-DST / DST)' % time.tzname) 134 _log.debug('mx.DateTime.now().gmtoffset(): [%s]' % mxDT.now().gmtoffset()) 135 136 global py_timezone_name 137 py_timezone_name = time.tzname[0].decode(gmI18N.get_encoding(), 'replace') 138 139 global py_dst_timezone_name 140 py_dst_timezone_name = time.tzname[1].decode(gmI18N.get_encoding(), 'replace') 141 142 global dst_locally_in_use 143 dst_locally_in_use = (time.daylight != 0) 144 145 global dst_currently_in_effect 146 dst_currently_in_effect = bool(time.localtime()[8]) 147 _log.debug('DST currently in effect: [%s]' % dst_currently_in_effect) 148 149 if (not dst_locally_in_use) and dst_currently_in_effect: 150 _log.error('system inconsistency: DST not in use - but DST currently in effect ?') 151 152 global current_local_utc_offset_in_seconds 153 msg = 'DST currently%sin effect: using UTC offset of [%s] seconds instead of [%s] seconds' 154 if dst_currently_in_effect: 155 current_local_utc_offset_in_seconds = time.altzone * -1 156 _log.debug(msg % (' ', time.altzone * -1, time.timezone * -1)) 157 else: 158 current_local_utc_offset_in_seconds = time.timezone * -1 159 _log.debug(msg % (' not ', time.timezone * -1, time.altzone * -1)) 160 161 if current_local_utc_offset_in_seconds > 0: 162 _log.debug('UTC offset is positive, assuming EAST of Greenwich (clock is "ahead")') 163 elif current_local_utc_offset_in_seconds < 0: 164 _log.debug('UTC offset is negative, assuming WEST of Greenwich (clock is "behind")') 165 else: 166 _log.debug('UTC offset is ZERO, assuming Greenwich Time') 167 168 global current_local_timezone_interval 169 current_local_timezone_interval = mxDT.now().gmtoffset() 170 _log.debug('ISO timezone: [%s] (taken from mx.DateTime.now().gmtoffset())' % current_local_timezone_interval) 171 172 global current_local_iso_numeric_timezone_string 173 current_local_iso_numeric_timezone_string = str(current_local_timezone_interval).replace(',', '.') 174 175 global current_local_timezone_name 176 try: 177 current_local_timezone_name = os.environ['TZ'].decode(gmI18N.get_encoding(), 'replace') 178 except KeyError: 179 if dst_currently_in_effect: 180 current_local_timezone_name = time.tzname[1].decode(gmI18N.get_encoding(), 'replace') 181 else: 182 current_local_timezone_name = time.tzname[0].decode(gmI18N.get_encoding(), 'replace') 183 184 # do some magic to convert Python's timezone to a valid ISO timezone 185 # is this safe or will it return things like 13.5 hours ? 186 #_default_client_timezone = "%+.1f" % (-tz / 3600.0) 187 #_log.info('assuming default client time zone of [%s]' % _default_client_timezone) 188 189 global gmCurrentLocalTimezone 190 gmCurrentLocalTimezone = cFixedOffsetTimezone ( 191 offset = (current_local_utc_offset_in_seconds / 60), 192 name = current_local_iso_numeric_timezone_string 193 )
194 #===========================================================================
195 -def pydt_now_here():
196 """Returns NOW @ HERE (IOW, in the local timezone.""" 197 return pyDT.datetime.now(gmCurrentLocalTimezone)
198 #=========================================================================== 199 # wxPython conversions 200 #---------------------------------------------------------------------------
201 -def wxDate2py_dt(wxDate=None):
202 if not wxDate.IsValid(): 203 raise ArgumentError (u'invalid wxDate: %s-%s-%s %s:%s %s.%s', 204 wxDate.GetYear(), 205 wxDate.GetMonth(), 206 wxDate.GetDay(), 207 wxDate.GetHour(), 208 wxDate.GetMinute(), 209 wxDate.GetSecond(), 210 wxDate.GetMillisecond() 211 ) 212 213 try: 214 return pyDT.datetime ( 215 year = wxDate.GetYear(), 216 month = wxDate.GetMonth() + 1, 217 day = wxDate.GetDay(), 218 tzinfo = gmCurrentLocalTimezone 219 ) 220 except: 221 _log.debug (u'error converting wxDateTime to Python: %s-%s-%s %s:%s %s.%s', 222 wxDate.GetYear(), 223 wxDate.GetMonth(), 224 wxDate.GetDay(), 225 wxDate.GetHour(), 226 wxDate.GetMinute(), 227 wxDate.GetSecond(), 228 wxDate.GetMillisecond() 229 ) 230 raise
231 #---------------------------------------------------------------------------
232 -def py_dt2wxDate(py_dt=None, wx=None):
233 wxdt = wx.DateTime() 234 wxdt.SetYear(py_dt.year) 235 wxdt.SetMonth(py_dt.month-1) 236 wxdt.SetDay(py_dt.day) 237 return wxdt
238 #=========================================================================== 239 # interval related 240 #---------------------------------------------------------------------------
241 -def format_interval(interval=None, accuracy_wanted=acc_seconds):
242 243 years, days = divmod(interval.days, avg_days_per_gregorian_year) 244 months, days = divmod(days, avg_days_per_gregorian_month) 245 weeks, days = divmod(days, days_per_week) 246 days, secs = divmod((days * avg_seconds_per_day) + interval.seconds, avg_seconds_per_day) 247 hours, secs = divmod(secs, 3600) 248 mins, secs = divmod(secs, 60) 249 250 tmp = u'' 251 252 if years > 0: 253 tmp += u'%s%s' % (int(years), _('interval_format_tag::years::y')[-1:]) 254 255 if accuracy_wanted < acc_months: 256 return tmp.strip() 257 258 if months > 0: 259 tmp += u' %s%s' % (int(months), _('interval_format_tag::months::m')[-1:]) 260 261 if accuracy_wanted < acc_weeks: 262 return tmp.strip() 263 264 if weeks > 0: 265 tmp += u' %s%s' % (int(weeks), _('interval_format_tag::weeks::w')[-1:]) 266 267 if accuracy_wanted < acc_days: 268 return tmp.strip() 269 270 if days > 0: 271 tmp += u' %s%s' % (int(days), _('interval_format_tag::days::d')[-1:]) 272 273 if accuracy_wanted < acc_hours: 274 return tmp.strip() 275 276 if hours > 0: 277 tmp += u' %s/24' % int(hours) 278 279 if accuracy_wanted < acc_minutes: 280 return tmp.strip() 281 282 if mins > 0: 283 tmp += u' %s/60' % int(mins) 284 285 if accuracy_wanted < acc_seconds: 286 return tmp.strip() 287 288 if secs > 0: 289 tmp += u' %s/60' % int(secs) 290 291 return tmp.strip()
292 #---------------------------------------------------------------------------
293 -def format_interval_medically(interval=None):
294 """Formats an interval. 295 296 This isn't mathematically correct but close enough for display. 297 """ 298 # FIXME: i18n for abbrevs 299 300 # more than 1 year ? 301 if interval.days > 363: 302 years, days = divmod(interval.days, 364) 303 leap_days, tmp = divmod(years, 4) 304 months, day = divmod((days + leap_days), 30.33) 305 if int(months) == 0: 306 return "%sy" % int(years) 307 return "%sy %sm" % (int(years), int(months)) 308 309 # more than 30 days / 1 month ? 310 if interval.days > 30: 311 months, days = divmod(interval.days, 30.33) 312 weeks, days = divmod(days, 7) 313 if int(weeks + days) == 0: 314 result = '%smo' % int(months) 315 else: 316 result = '%sm' % int(months) 317 if int(weeks) != 0: 318 result += ' %sw' % int(weeks) 319 if int(days) != 0: 320 result += ' %sd' % int(days) 321 return result 322 323 # between 7 and 30 days ? 324 if interval.days > 7: 325 return "%sd" % interval.days 326 327 # between 1 and 7 days ? 328 if interval.days > 0: 329 hours, seconds = divmod(interval.seconds, 3600) 330 if hours == 0: 331 return '%sd' % interval.days 332 return "%sd (%sh)" % (interval.days, int(hours)) 333 334 # between 5 hours and 1 day 335 if interval.seconds > (5*3600): 336 return "%sh" % int(interval.seconds // 3600) 337 338 # between 1 and 5 hours 339 if interval.seconds > 3600: 340 hours, seconds = divmod(interval.seconds, 3600) 341 minutes = seconds // 60 342 if minutes == 0: 343 return '%sh' % int(hours) 344 return "%s:%02d" % (int(hours), int(minutes)) 345 346 # minutes only 347 if interval.seconds > (5*60): 348 return "0:%02d" % (int(interval.seconds // 60)) 349 350 # seconds 351 minutes, seconds = divmod(interval.seconds, 60) 352 if minutes == 0: 353 return '%ss' % int(seconds) 354 if seconds == 0: 355 return '0:%02d' % int(minutes) 356 return "%s.%ss" % (int(minutes), int(seconds))
357 #---------------------------------------------------------------------------
358 -def str2interval(str_interval=None):
359 360 unit_keys = { 361 'year': _('yYaA_keys_year'), 362 'month': _('mM_keys_month'), 363 'week': _('wW_keys_week'), 364 'day': _('dD_keys_day'), 365 'hour': _('hH_keys_hour') 366 } 367 368 str_interval = str_interval.strip() 369 370 # "(~)35(yY)" - at age 35 years 371 keys = '|'.join(list(unit_keys['year'].replace('_keys_year', u''))) 372 if regex.match(u'^~*(\s|\t)*\d+(%s)*$' % keys, str_interval, flags = regex.LOCALE | regex.UNICODE): 373 return pyDT.timedelta(days = (int(regex.findall(u'\d+', str_interval, flags = regex.LOCALE | regex.UNICODE)[0]) * avg_days_per_gregorian_year)) 374 375 # "(~)12mM" - at age 12 months 376 keys = '|'.join(list(unit_keys['month'].replace('_keys_month', u''))) 377 if regex.match(u'^~*(\s|\t)*\d+(\s|\t)*(%s)+$' % keys, str_interval, flags = regex.LOCALE | regex.UNICODE): 378 years, months = divmod ( 379 int(regex.findall(u'\d+', str_interval, flags = regex.LOCALE | regex.UNICODE)[0]), 380 12 381 ) 382 return pyDT.timedelta(days = ((years * avg_days_per_gregorian_year) + (months * avg_days_per_gregorian_month))) 383 384 # weeks 385 keys = '|'.join(list(unit_keys['week'].replace('_keys_week', u''))) 386 if regex.match(u'^~*(\s|\t)*\d+(\s|\t)*(%s)+$' % keys, str_interval, flags = regex.LOCALE | regex.UNICODE): 387 return pyDT.timedelta(weeks = int(regex.findall(u'\d+', str_interval, flags = regex.LOCALE | regex.UNICODE)[0])) 388 389 # days 390 keys = '|'.join(list(unit_keys['day'].replace('_keys_day', u''))) 391 if regex.match(u'^~*(\s|\t)*\d+(\s|\t)*(%s)+$' % keys, str_interval, flags = regex.LOCALE | regex.UNICODE): 392 return pyDT.timedelta(days = int(regex.findall(u'\d+', str_interval, flags = regex.LOCALE | regex.UNICODE)[0])) 393 394 # hours 395 keys = '|'.join(list(unit_keys['hour'].replace('_keys_hour', u''))) 396 if regex.match(u'^~*(\s|\t)*\d+(\s|\t)*(%s)+$' % keys, str_interval, flags = regex.LOCALE | regex.UNICODE): 397 return pyDT.timedelta(hours = int(regex.findall(u'\d+', str_interval, flags = regex.LOCALE | regex.UNICODE)[0])) 398 399 # x/12 - months 400 if regex.match(u'^~*(\s|\t)*\d+(\s|\t)*/(\s|\t)*12$', str_interval, flags = regex.LOCALE | regex.UNICODE): 401 years, months = divmod ( 402 int(regex.findall(u'\d+', str_interval, flags = regex.LOCALE | regex.UNICODE)[0]), 403 12 404 ) 405 return pyDT.timedelta(days = ((years * avg_days_per_gregorian_year) + (months * avg_days_per_gregorian_month))) 406 407 # x/52 - weeks 408 if regex.match(u'^~*(\s|\t)*\d+(\s|\t)*/(\s|\t)*52$', str_interval, flags = regex.LOCALE | regex.UNICODE): 409 # return pyDT.timedelta(days = (int(regex.findall('\d+', str_interval)[0]) * days_per_week)) 410 return pyDT.timedelta(weeks = int(regex.findall(u'\d+', str_interval, flags = regex.LOCALE | regex.UNICODE)[0])) 411 412 # x/7 - days 413 if regex.match(u'^~*(\s|\t)*\d+(\s|\t)*/(\s|\t)*7$', str_interval, flags = regex.LOCALE | regex.UNICODE): 414 return pyDT.timedelta(days = int(regex.findall(u'\d+', str_interval, flags = regex.LOCALE | regex.UNICODE)[0])) 415 416 # x/24 - hours 417 if regex.match(u'^~*(\s|\t)*\d+(\s|\t)*/(\s|\t)*24$', str_interval, flags = regex.LOCALE | regex.UNICODE): 418 return pyDT.timedelta(hours = int(regex.findall(u'\d+', str_interval, flags = regex.LOCALE | regex.UNICODE)[0])) 419 420 # x/60 - minutes 421 if regex.match(u'^~*(\s|\t)*\d+(\s|\t)*/(\s|\t)*60$', str_interval, flags = regex.LOCALE | regex.UNICODE): 422 return pyDT.timedelta(minutes = int(regex.findall(u'\d+', str_interval, flags = regex.LOCALE | regex.UNICODE)[0])) 423 424 # nYnM - years, months 425 keys_year = '|'.join(list(unit_keys['year'].replace('_keys_year', u''))) 426 keys_month = '|'.join(list(unit_keys['month'].replace('_keys_month', u''))) 427 if regex.match(u'^~*(\s|\t)*\d+(%s|\s|\t)+\d+(\s|\t)*(%s)+$' % (keys_year, keys_month), str_interval, flags = regex.LOCALE | regex.UNICODE): 428 parts = regex.findall(u'\d+', str_interval, flags = regex.LOCALE | regex.UNICODE) 429 years, months = divmod(int(parts[1]), 12) 430 years += int(parts[0]) 431 return pyDT.timedelta(days = ((years * avg_days_per_gregorian_year) + (months * avg_days_per_gregorian_month))) 432 433 # nMnW - months, weeks 434 keys_month = '|'.join(list(unit_keys['month'].replace('_keys_month', u''))) 435 keys_week = '|'.join(list(unit_keys['week'].replace('_keys_week', u''))) 436 if regex.match(u'^~*(\s|\t)*\d+(%s|\s|\t)+\d+(\s|\t)*(%s)+$' % (keys_month, keys_week), str_interval, flags = regex.LOCALE | regex.UNICODE): 437 parts = regex.findall(u'\d+', str_interval, flags = regex.LOCALE | regex.UNICODE) 438 months, weeks = divmod(int(parts[1]), 4) 439 months += int(parts[0]) 440 return pyDT.timedelta(days = ((months * avg_days_per_gregorian_month) + (weeks * days_per_week))) 441 442 return None
443 444 #=========================================================================== 445 # string -> timestamp parsers 446 #---------------------------------------------------------------------------
447 -def __explicit_offset(str2parse, offset_chars=None):
448 """ 449 Default is 'hdwm': 450 h - hours 451 d - days 452 w - weeks 453 m - months 454 y - years 455 456 This also defines the significance of the order of the characters. 457 """ 458 if offset_chars is None: 459 offset_chars = _('hdwmy (single character date offset triggers)')[:5].lower() 460 461 # "+/-XXd/w/m/t" 462 if not regex.match(u"^(\s|\t)*(\+|-)?(\s|\t)*\d{1,2}(\s|\t)*[%s](\s|\t)*$" % offset_chars, str2parse, flags = regex.LOCALE | regex.UNICODE): 463 return [] 464 val = int(regex.findall(u'\d{1,2}', str2parse, flags = regex.LOCALE | regex.UNICODE)[0]) 465 offset_char = regex.findall(u'[%s]' % offset_chars, str2parse, flags = regex.LOCALE | regex.UNICODE)[0].lower() 466 467 now = mxDT.now() 468 enc = gmI18N.get_encoding() 469 470 # allow past ? 471 is_future = True 472 if str2parse.find('-') > -1: 473 is_future = False 474 475 ts = None 476 # hours 477 if offset_char == offset_chars[0]: 478 if is_future: 479 ts = now + mxDT.RelativeDateTime(hours = val) 480 label = _('in %d hour(s) - %s') % (val, ts.strftime('%H:%M')) 481 else: 482 ts = now - mxDT.RelativeDateTime(hours = val) 483 label = _('%d hour(s) ago - %s') % (val, ts.strftime('%H:%M')) 484 accuracy = acc_subseconds 485 # days 486 elif offset_char == offset_chars[1]: 487 if is_future: 488 ts = now + mxDT.RelativeDateTime(days = val) 489 label = _('in %d day(s) - %s') % (val, ts.strftime('%A, %Y-%m-%d').decode(enc)) 490 else: 491 ts = now - mxDT.RelativeDateTime(days = val) 492 label = _('%d day(s) ago - %s') % (val, ts.strftime('%A, %Y-%m-%d').decode(enc)) 493 accuracy = acc_days 494 # weeks 495 elif offset_char == offset_chars[2]: 496 if is_future: 497 ts = now + mxDT.RelativeDateTime(weeks = val) 498 label = _('in %d week(s) - %s') % (val, ts.strftime('%A, %Y-%m-%d').decode(enc)) 499 else: 500 ts = now - mxDT.RelativeDateTime(weeks = val) 501 label = _('%d week(s) ago - %s)') % (val, ts.strftime('%A, %Y-%m-%d').decode(enc)) 502 accuracy = acc_days 503 # months 504 elif offset_char == offset_chars[3]: 505 if is_future: 506 ts = now + mxDT.RelativeDateTime(months = val) 507 label = _('in %d month(s) - %s') % (val, ts.strftime('%A, %Y-%m-%d').decode(enc)) 508 else: 509 ts = now - mxDT.RelativeDateTime(months = val) 510 label = _('%d month(s) ago - %s') % (val, ts.strftime('%A, %Y-%m-%d').decode(enc)) 511 accuracy = acc_days 512 # years 513 elif offset_char == offset_chars[4]: 514 if is_future: 515 ts = now + mxDT.RelativeDateTime(years = val) 516 label = _('in %d year(s) - %s') % (val, ts.strftime('%A, %Y-%m-%d').decode(enc)) 517 else: 518 ts = now - mxDT.RelativeDateTime(years = val) 519 label = _('%d year(s) ago - %s') % (val, ts.strftime('%A, %Y-%m-%d').decode(enc)) 520 accuracy = acc_months 521 522 if ts is None: 523 return [] 524 525 tmp = { 526 'data': cFuzzyTimestamp(timestamp = ts, accuracy = accuracy), 527 'label': label 528 } 529 return [tmp]
530 #---------------------------------------------------------------------------
531 -def __single_char(str2parse, trigger_chars=None):
532 """This matches on single characters. 533 534 Spaces and tabs are discarded. 535 536 Default is 'ndmy': 537 n - now 538 d - toDay 539 m - toMorrow Someone please suggest a synonym ! 540 y - yesterday 541 542 This also defines the significance of the order of the characters. 543 """ 544 if trigger_chars is None: 545 trigger_chars = _('ndmy (single character date triggers)')[:4].lower() 546 547 if not regex.match(u'^(\s|\t)*[%s]{1}(\s|\t)*$' % trigger_chars, str2parse, flags = regex.LOCALE | regex.UNICODE): 548 return [] 549 val = str2parse.strip().lower() 550 551 now = mxDT.now() 552 enc = gmI18N.get_encoding() 553 554 # FIXME: handle uebermorgen/vorgestern ? 555 556 # right now 557 if val == trigger_chars[0]: 558 ts = now 559 return [{ 560 'data': cFuzzyTimestamp ( 561 timestamp = ts, 562 accuracy = acc_subseconds 563 ), 564 'label': _('right now (%s, %s)') % (ts.strftime('%A').decode(enc), ts) 565 }] 566 567 # today 568 if val == trigger_chars[1]: 569 return [{ 570 'data': cFuzzyTimestamp ( 571 timestamp = now, 572 accuracy = acc_days 573 ), 574 'label': _('today (%s)') % now.strftime('%A, %Y-%m-%d').decode(enc) 575 }] 576 577 # tomorrow 578 if val == trigger_chars[2]: 579 ts = now + mxDT.RelativeDateTime(days = +1) 580 return [{ 581 'data': cFuzzyTimestamp ( 582 timestamp = ts, 583 accuracy = acc_days 584 ), 585 'label': _('tomorrow (%s)') % ts.strftime('%A, %Y-%m-%d').decode(enc) 586 }] 587 588 # yesterday 589 if val == trigger_chars[3]: 590 ts = now + mxDT.RelativeDateTime(days = -1) 591 return [{ 592 'data': cFuzzyTimestamp ( 593 timestamp = ts, 594 accuracy = acc_days 595 ), 596 'label': _('yesterday (%s)') % ts.strftime('%A, %Y-%m-%d').decode(enc) 597 }] 598 599 return []
600 #---------------------------------------------------------------------------
601 -def __single_slash(str2parse):
602 """Expand fragments containing a single slash. 603 604 "5/" 605 - 2005/ (2000 - 2025) 606 - 1995/ (1990 - 1999) 607 - Mai/current year 608 - Mai/next year 609 - Mai/last year 610 - Mai/200x 611 - Mai/20xx 612 - Mai/199x 613 - Mai/198x 614 - Mai/197x 615 - Mai/19xx 616 """ 617 matches = [] 618 now = mxDT.now() 619 if regex.match(u"^(\s|\t)*\d{1,2}(\s|\t)*/+(\s|\t)*$", str2parse, flags = regex.LOCALE | regex.UNICODE): 620 val = int(regex.findall(u'\d+', str2parse, flags = regex.LOCALE | regex.UNICODE)[0]) 621 622 if val < 100 and val >= 0: 623 matches.append ({ 624 'data': None, 625 'label': '%s/' % (val + 1900) 626 }) 627 628 if val < 26 and val >= 0: 629 matches.append ({ 630 'data': None, 631 'label': '%s/' % (val + 2000) 632 }) 633 634 if val < 10 and val >= 0: 635 matches.append ({ 636 'data': None, 637 'label': '%s/' % (val + 1990) 638 }) 639 640 if val < 13 and val > 0: 641 matches.append ({ 642 'data': cFuzzyTimestamp(timestamp = now, accuracy = acc_months), 643 'label': '%.2d/%s' % (val, now.year) 644 }) 645 ts = now + mxDT.RelativeDateTime(years = 1) 646 matches.append ({ 647 'data': cFuzzyTimestamp(timestamp = ts, accuracy = acc_months), 648 'label': '%.2d/%s' % (val, ts.year) 649 }) 650 ts = now + mxDT.RelativeDateTime(years = -1) 651 matches.append ({ 652 'data': cFuzzyTimestamp(timestamp = ts, accuracy = acc_months), 653 'label': '%.2d/%s' % (val, ts.year) 654 }) 655 matches.append ({ 656 'data': None, 657 'label': '%.2d/200' % val 658 }) 659 matches.append ({ 660 'data': None, 661 'label': '%.2d/20' % val 662 }) 663 matches.append ({ 664 'data': None, 665 'label': '%.2d/199' % val 666 }) 667 matches.append ({ 668 'data': None, 669 'label': '%.2d/198' % val 670 }) 671 matches.append ({ 672 'data': None, 673 'label': '%.2d/197' % val 674 }) 675 matches.append ({ 676 'data': None, 677 'label': '%.2d/19' % val 678 }) 679 680 elif regex.match(u"^(\s|\t)*\d{1,2}(\s|\t)*/+(\s|\t)*\d{4}(\s|\t)*$", str2parse, flags = regex.LOCALE | regex.UNICODE): 681 parts = regex.findall(u'\d+', str2parse, flags = regex.LOCALE | regex.UNICODE) 682 fts = cFuzzyTimestamp ( 683 timestamp = mxDT.now() + mxDT.RelativeDateTime(year = int(parts[1]), month = int(parts[0])), 684 accuracy = acc_months 685 ) 686 matches.append ({ 687 'data': fts, 688 'label': fts.format_accurately() 689 }) 690 691 return matches
692 #---------------------------------------------------------------------------
693 -def __numbers_only(str2parse):
694 """This matches on single numbers. 695 696 Spaces or tabs are discarded. 697 """ 698 if not regex.match(u"^(\s|\t)*\d{1,4}(\s|\t)*$", str2parse, flags = regex.LOCALE | regex.UNICODE): 699 return [] 700 701 # strftime() returns str but in the localized encoding, 702 # so we may need to decode that to unicode 703 enc = gmI18N.get_encoding() 704 now = mxDT.now() 705 val = int(regex.findall(u'\d{1,4}', str2parse, flags = regex.LOCALE | regex.UNICODE)[0]) 706 707 matches = [] 708 709 # that year 710 if (1850 < val) and (val < 2100): 711 ts = now + mxDT.RelativeDateTime(year = val) 712 target_date = cFuzzyTimestamp ( 713 timestamp = ts, 714 accuracy = acc_years 715 ) 716 tmp = { 717 'data': target_date, 718 'label': '%s' % target_date 719 } 720 matches.append(tmp) 721 722 # day X of this month 723 if val <= gregorian_month_length[now.month]: 724 ts = now + mxDT.RelativeDateTime(day = val) 725 target_date = cFuzzyTimestamp ( 726 timestamp = ts, 727 accuracy = acc_days 728 ) 729 tmp = { 730 'data': target_date, 731 'label': _('%d. of %s (this month) - a %s') % (val, ts.strftime('%B').decode(enc), ts.strftime('%A').decode(enc)) 732 } 733 matches.append(tmp) 734 735 # day X of next month 736 if val <= gregorian_month_length[(now + mxDT.RelativeDateTime(months = 1)).month]: 737 ts = now + mxDT.RelativeDateTime(months = 1, day = val) 738 target_date = cFuzzyTimestamp ( 739 timestamp = ts, 740 accuracy = acc_days 741 ) 742 tmp = { 743 'data': target_date, 744 'label': _('%d. of %s (next month) - a %s') % (val, ts.strftime('%B').decode(enc), ts.strftime('%A').decode(enc)) 745 } 746 matches.append(tmp) 747 748 # day X of last month 749 if val <= gregorian_month_length[(now + mxDT.RelativeDateTime(months = -1)).month]: 750 ts = now + mxDT.RelativeDateTime(months = -1, day = val) 751 target_date = cFuzzyTimestamp ( 752 timestamp = ts, 753 accuracy = acc_days 754 ) 755 tmp = { 756 'data': target_date, 757 'label': _('%d. of %s (last month) - a %s') % (val, ts.strftime('%B').decode(enc), ts.strftime('%A').decode(enc)) 758 } 759 matches.append(tmp) 760 761 # X days from now 762 if val <= 400: # more than a year ahead in days ?? nah ! 763 ts = now + mxDT.RelativeDateTime(days = val) 764 target_date = cFuzzyTimestamp ( 765 timestamp = ts 766 ) 767 tmp = { 768 'data': target_date, 769 'label': _('in %d day(s) - %s') % (val, target_date.timestamp.strftime('%A, %Y-%m-%d').decode(enc)) 770 } 771 matches.append(tmp) 772 773 # X weeks from now 774 if val <= 50: # pregnancy takes about 40 weeks :-) 775 ts = now + mxDT.RelativeDateTime(weeks = val) 776 target_date = cFuzzyTimestamp ( 777 timestamp = ts 778 ) 779 tmp = { 780 'data': target_date, 781 'label': _('in %d week(s) - %s') % (val, target_date.timestamp.strftime('%A, %Y-%m-%d').decode(enc)) 782 } 783 matches.append(tmp) 784 785 # month X of ... 786 if val < 13: 787 # ... this year 788 ts = now + mxDT.RelativeDateTime(month = val) 789 target_date = cFuzzyTimestamp ( 790 timestamp = ts, 791 accuracy = acc_months 792 ) 793 tmp = { 794 'data': target_date, 795 'label': _('%s (%s this year)') % (target_date, ts.strftime('%B').decode(enc)) 796 } 797 matches.append(tmp) 798 799 # ... next year 800 ts = now + mxDT.RelativeDateTime(years = 1, month = val) 801 target_date = cFuzzyTimestamp ( 802 timestamp = ts, 803 accuracy = acc_months 804 ) 805 tmp = { 806 'data': target_date, 807 'label': _('%s (%s next year)') % (target_date, ts.strftime('%B').decode(enc)) 808 } 809 matches.append(tmp) 810 811 # ... last year 812 ts = now + mxDT.RelativeDateTime(years = -1, month = val) 813 target_date = cFuzzyTimestamp ( 814 timestamp = ts, 815 accuracy = acc_months 816 ) 817 tmp = { 818 'data': target_date, 819 'label': _('%s (%s last year)') % (target_date, ts.strftime('%B').decode(enc)) 820 } 821 matches.append(tmp) 822 823 # fragment expansion 824 matches.append ({ 825 'data': None, 826 'label': '%s/200' % val 827 }) 828 matches.append ({ 829 'data': None, 830 'label': '%s/199' % val 831 }) 832 matches.append ({ 833 'data': None, 834 'label': '%s/198' % val 835 }) 836 matches.append ({ 837 'data': None, 838 'label': '%s/19' % val 839 }) 840 841 # day X of ... 842 if val < 8: 843 # ... this week 844 ts = now + mxDT.RelativeDateTime(weekday = (val-1, 0)) 845 target_date = cFuzzyTimestamp ( 846 timestamp = ts, 847 accuracy = acc_days 848 ) 849 tmp = { 850 'data': target_date, 851 'label': _('%s this week (%s of %s)') % (ts.strftime('%A').decode(enc), ts.day, ts.strftime('%B').decode(enc)) 852 } 853 matches.append(tmp) 854 855 # ... next week 856 ts = now + mxDT.RelativeDateTime(weeks = +1, weekday = (val-1, 0)) 857 target_date = cFuzzyTimestamp ( 858 timestamp = ts, 859 accuracy = acc_days 860 ) 861 tmp = { 862 'data': target_date, 863 'label': _('%s next week (%s of %s)') % (ts.strftime('%A').decode(enc), ts.day, ts.strftime('%B').decode(enc)) 864 } 865 matches.append(tmp) 866 867 # ... last week 868 ts = now + mxDT.RelativeDateTime(weeks = -1, weekday = (val-1, 0)) 869 target_date = cFuzzyTimestamp ( 870 timestamp = ts, 871 accuracy = acc_days 872 ) 873 tmp = { 874 'data': target_date, 875 'label': _('%s last week (%s of %s)') % (ts.strftime('%A').decode(enc), ts.day, ts.strftime('%B').decode(enc)) 876 } 877 matches.append(tmp) 878 879 if val < 100: 880 matches.append ({ 881 'data': None, 882 'label': '%s/' % (1900 + val) 883 }) 884 885 if val == 200: 886 tmp = { 887 'data': cFuzzyTimestamp(timestamp = now, accuracy = acc_days), 888 'label': '%s' % target_date 889 } 890 matches.append(tmp) 891 matches.append ({ 892 'data': cFuzzyTimestamp(timestamp = now, accuracy = acc_months), 893 'label': '%.2d/%s' % (now.month, now.year) 894 }) 895 matches.append ({ 896 'data': None, 897 'label': '%s/' % now.year 898 }) 899 matches.append ({ 900 'data': None, 901 'label': '%s/' % (now.year + 1) 902 }) 903 matches.append ({ 904 'data': None, 905 'label': '%s/' % (now.year - 1) 906 }) 907 908 if val < 200 and val >= 190: 909 for i in range(10): 910 matches.append ({ 911 'data': None, 912 'label': '%s%s/' % (val, i) 913 }) 914 915 return matches
916 #---------------------------------------------------------------------------
917 -def __single_dot(str2parse):
918 """Expand fragments containing a single dot. 919 920 Standard colloquial date format in Germany: day.month.year 921 922 "14." 923 - 14th current month this year 924 - 14th next month this year 925 """ 926 if not regex.match(u"^(\s|\t)*\d{1,2}\.{1}(\s|\t)*$", str2parse, flags = regex.LOCALE | regex.UNICODE): 927 return [] 928 929 val = int(regex.findall(u'\d+', str2parse, flags = regex.LOCALE | regex.UNICODE)[0]) 930 now = mxDT.now() 931 enc = gmI18N.get_encoding() 932 933 matches = [] 934 935 # day X of this month 936 ts = now + mxDT.RelativeDateTime(day = val) 937 if val > 0 and val <= gregorian_month_length[ts.month]: 938 matches.append ({ 939 'data': cFuzzyTimestamp(timestamp = ts, accuracy = acc_days), 940 'label': '%s.%s.%s - a %s this month' % (ts.day, ts.month, ts.year, ts.strftime('%A').decode(enc)) 941 }) 942 943 # day X of next month 944 ts = now + mxDT.RelativeDateTime(day = val, months = +1) 945 if val > 0 and val <= gregorian_month_length[ts.month]: 946 matches.append ({ 947 'data': cFuzzyTimestamp(timestamp = ts, accuracy = acc_days), 948 'label': '%s.%s.%s - a %s next month' % (ts.day, ts.month, ts.year, ts.strftime('%A').decode(enc)) 949 }) 950 951 # day X of last month 952 ts = now + mxDT.RelativeDateTime(day = val, months = -1) 953 if val > 0 and val <= gregorian_month_length[ts.month]: 954 matches.append ({ 955 'data': cFuzzyTimestamp(timestamp = ts, accuracy = acc_days), 956 'label': '%s.%s.%s - a %s last month' % (ts.day, ts.month, ts.year, ts.strftime('%A').decode(enc)) 957 }) 958 959 return matches
960 #---------------------------------------------------------------------------
961 -def str2fuzzy_timestamp_matches(str2parse=None, default_time=None, patterns=None):
962 """ 963 Turn a string into candidate fuzzy timestamps and auto-completions the user is likely to type. 964 965 You MUST have called locale.setlocale(locale.LC_ALL, '') 966 somewhere in your code previously. 967 968 @param default_time: if you want to force the time part of the time 969 stamp to a given value and the user doesn't type any time part 970 this value will be used 971 @type default_time: an mx.DateTime.DateTimeDelta instance 972 973 @param patterns: list of [time.strptime compatible date/time pattern, accuracy] 974 @type patterns: list 975 """ 976 matches = __single_dot(str2parse) 977 matches.extend(__numbers_only(str2parse)) 978 matches.extend(__single_slash(str2parse)) 979 matches.extend(__single_char(str2parse)) 980 matches.extend(__explicit_offset(str2parse)) 981 982 # try mxDT parsers 983 if mxDT is not None: 984 try: 985 # date ? 986 date_only = mxDT.Parser.DateFromString ( 987 text = str2parse, 988 formats = ('euro', 'iso', 'us', 'altus', 'altiso', 'lit', 'altlit', 'eurlit') 989 ) 990 # time, too ? 991 time_part = mxDT.Parser.TimeFromString(text = str2parse) 992 datetime = date_only + time_part 993 if datetime == date_only: 994 accuracy = acc_days 995 if isinstance(default_time, mxDT.DateTimeDeltaType): 996 datetime = date_only + default_time 997 accuracy = acc_minutes 998 else: 999 accuracy = acc_subseconds 1000 fts = cFuzzyTimestamp ( 1001 timestamp = datetime, 1002 accuracy = accuracy 1003 ) 1004 matches.append ({ 1005 'data': fts, 1006 'label': fts.format_accurately() 1007 }) 1008 except (ValueError, mxDT.RangeError): 1009 pass 1010 1011 if patterns is None: 1012 patterns = [] 1013 1014 patterns.append(['%Y.%m.%d', acc_days]) 1015 patterns.append(['%Y/%m/%d', acc_days]) 1016 1017 for pattern in patterns: 1018 try: 1019 fts = cFuzzyTimestamp ( 1020 timestamp = pyDT.datetime.fromtimestamp(time.mktime(time.strptime(str2parse, pattern[0]))), 1021 accuracy = pattern[1] 1022 ) 1023 matches.append ({ 1024 'data': fts, 1025 'label': fts.format_accurately() 1026 }) 1027 except AttributeError: 1028 # strptime() only available starting with Python 2.5 1029 break 1030 except OverflowError: 1031 # time.mktime() cannot handle dates older than a platform-dependant limit :-( 1032 continue 1033 except ValueError: 1034 # C-level overflow 1035 continue 1036 1037 return matches
1038 #=========================================================================== 1039 # fuzzy timestamp class 1040 #---------------------------------------------------------------------------
1041 -class cFuzzyTimestamp:
1042 1043 # FIXME: add properties for year, month, ... 1044 1045 """A timestamp implementation with definable inaccuracy. 1046 1047 This class contains an mxDateTime.DateTime instance to 1048 hold the actual timestamp. It adds an accuracy attribute 1049 to allow the programmer to set the precision of the 1050 timestamp. 1051 1052 The timestamp will have to be initialzed with a fully 1053 precise value (which may, of course, contain partially 1054 fake data to make up for missing values). One can then 1055 set the accuracy value to indicate up to which part of 1056 the timestamp the data is valid. Optionally a modifier 1057 can be set to indicate further specification of the 1058 value (such as "summer", "afternoon", etc). 1059 1060 accuracy values: 1061 1: year only 1062 ... 1063 7: everything including milliseconds value 1064 1065 Unfortunately, one cannot directly derive a class from mx.DateTime.DateTime :-( 1066 """ 1067 #-----------------------------------------------------------------------
1068 - def __init__(self, timestamp=None, accuracy=acc_subseconds, modifier=''):
1069 1070 if timestamp is None: 1071 timestamp = mxDT.now() 1072 accuracy = acc_subseconds 1073 modifier = '' 1074 1075 if isinstance(timestamp, pyDT.datetime): 1076 timestamp = mxDT.DateTime(timestamp.year, timestamp.month, timestamp.day, timestamp.hour, timestamp.minute, timestamp.second) 1077 1078 if type(timestamp) != mxDT.DateTimeType: 1079 raise TypeError, '%s.__init__(): <timestamp> must be of mx.DateTime.DateTime or datetime.datetime type' % self.__class__.__name__ 1080 1081 self.timestamp = timestamp 1082 1083 if (accuracy < 1) or (accuracy > 8): 1084 raise ValueError, '%s.__init__(): <accuracy> must be between 1 and 7' % self.__class__.__name__ 1085 self.accuracy = accuracy 1086 1087 self.modifier = modifier
1088 1089 #----------------------------------------------------------------------- 1090 # magic API 1091 #-----------------------------------------------------------------------
1092 - def __str__(self):
1093 """Return string representation meaningful to a user, also for %s formatting.""" 1094 return self.format_accurately()
1095 #-----------------------------------------------------------------------
1096 - def __repr__(self):
1097 """Return string meaningful to a programmer to aid in debugging.""" 1098 tmp = '<[%s]: timestamp [%s], accuracy [%s] (%s), modifier [%s] at %s>' % ( 1099 self.__class__.__name__, 1100 repr(self.timestamp), 1101 self.accuracy, 1102 _accuracy_strings[self.accuracy], 1103 self.modifier, 1104 id(self) 1105 ) 1106 return tmp
1107 #----------------------------------------------------------------------- 1108 # external API 1109 #-----------------------------------------------------------------------
1110 - def strftime(self, format_string):
1111 if self.accuracy == 7: 1112 return self.timestamp.strftime(format_string) 1113 return self.format_accurately()
1114 #-----------------------------------------------------------------------
1115 - def Format(self, format_string):
1116 return self.strftime(format_string)
1117 #-----------------------------------------------------------------------
1118 - def format_accurately(self):
1119 if self.accuracy == acc_years: 1120 return unicode(self.timestamp.year) 1121 1122 if self.accuracy == acc_months: 1123 return unicode(self.timestamp.strftime('%m/%Y')) # FIXME: use 3-letter month ? 1124 1125 if self.accuracy == acc_days: 1126 return unicode(self.timestamp.strftime('%Y-%m-%d')) 1127 1128 if self.accuracy == acc_hours: 1129 return unicode(self.timestamp.strftime("%Y-%m-%d %I%p")) 1130 1131 if self.accuracy == acc_minutes: 1132 return unicode(self.timestamp.strftime("%Y-%m-%d %H:%M")) 1133 1134 if self.accuracy == acc_seconds: 1135 return unicode(self.timestamp.strftime("%Y-%m-%d %H:%M:%S")) 1136 1137 if self.accuracy == acc_subseconds: 1138 return unicode(self.timestamp) 1139 1140 raise ValueError, '%s.format_accurately(): <accuracy> (%s) must be between 1 and 7' % ( 1141 self.__class__.__name__, 1142 self.accuracy 1143 )
1144 #-----------------------------------------------------------------------
1145 - def get_mxdt(self):
1146 return self.timestamp
1147 #-----------------------------------------------------------------------
1148 - def get_pydt(self):
1149 try: 1150 gmtoffset = self.timestamp.gmtoffset() 1151 except mxDT.Error: 1152 # Windows cannot deal with dates < 1970, so 1153 # when that happens switch to now() 1154 now = mxDT.now() 1155 gmtoffset = now.gmtoffset() 1156 tz = cFixedOffsetTimezone(gmtoffset.minutes, self.timestamp.tz) 1157 secs, msecs = divmod(self.timestamp.second, 1) 1158 ts = pyDT.datetime ( 1159 year = self.timestamp.year, 1160 month = self.timestamp.month, 1161 day = self.timestamp.day, 1162 hour = self.timestamp.hour, 1163 minute = self.timestamp.minute, 1164 second = secs, 1165 microsecond = msecs, 1166 tzinfo = tz 1167 ) 1168 return ts
1169 #=========================================================================== 1170 # main 1171 #--------------------------------------------------------------------------- 1172 if __name__ == '__main__': 1173 1174 intervals_as_str = [ 1175 '7', '12', ' 12', '12 ', ' 12 ', ' 12 ', '0', '~12', '~ 12', ' ~ 12', ' ~ 12 ', 1176 '12a', '12 a', '12 a', '12j', '12J', '12y', '12Y', ' ~ 12 a ', '~0a', 1177 '12m', '17 m', '12 m', '17M', ' ~ 17 m ', ' ~ 3 / 12 ', '7/12', '0/12', 1178 '12w', '17 w', '12 w', '17W', ' ~ 17 w ', ' ~ 15 / 52', '2/52', '0/52', 1179 '12d', '17 d', '12 t', '17D', ' ~ 17 T ', ' ~ 12 / 7', '3/7', '0/7', 1180 '12h', '17 h', '12 H', '17H', ' ~ 17 h ', ' ~ 36 / 24', '7/24', '0/24', 1181 ' ~ 36 / 60', '7/60', '190/60', '0/60', 1182 '12a1m', '12 a 1 M', '12 a17m', '12j 12m', '12J7m', '12y7m', '12Y7M', ' ~ 12 a 37 m ', '~0a0m', 1183 '10m1w', 1184 'invalid interval input' 1185 ] 1186 #-----------------------------------------------------------------------
1187 - def test_format_interval():
1188 for tmp in intervals_as_str: 1189 intv = str2interval(str_interval = tmp) 1190 for acc in _accuracy_strings.keys(): 1191 print '[%s]: "%s" -> "%s"' % (acc, tmp, format_interval(intv, acc))
1192 #-----------------------------------------------------------------------
1193 - def test_format_interval_medically():
1194 1195 intervals = [ 1196 pyDT.timedelta(seconds = 1), 1197 pyDT.timedelta(seconds = 5), 1198 pyDT.timedelta(seconds = 30), 1199 pyDT.timedelta(seconds = 60), 1200 pyDT.timedelta(seconds = 94), 1201 pyDT.timedelta(seconds = 120), 1202 pyDT.timedelta(minutes = 5), 1203 pyDT.timedelta(minutes = 30), 1204 pyDT.timedelta(minutes = 60), 1205 pyDT.timedelta(minutes = 90), 1206 pyDT.timedelta(minutes = 120), 1207 pyDT.timedelta(minutes = 200), 1208 pyDT.timedelta(minutes = 400), 1209 pyDT.timedelta(minutes = 600), 1210 pyDT.timedelta(minutes = 800), 1211 pyDT.timedelta(minutes = 1100), 1212 pyDT.timedelta(minutes = 2000), 1213 pyDT.timedelta(minutes = 3500), 1214 pyDT.timedelta(minutes = 4000), 1215 pyDT.timedelta(hours = 1), 1216 pyDT.timedelta(hours = 2), 1217 pyDT.timedelta(hours = 4), 1218 pyDT.timedelta(hours = 8), 1219 pyDT.timedelta(hours = 12), 1220 pyDT.timedelta(hours = 20), 1221 pyDT.timedelta(hours = 23), 1222 pyDT.timedelta(hours = 24), 1223 pyDT.timedelta(hours = 25), 1224 pyDT.timedelta(hours = 30), 1225 pyDT.timedelta(hours = 48), 1226 pyDT.timedelta(hours = 98), 1227 pyDT.timedelta(hours = 120), 1228 pyDT.timedelta(days = 1), 1229 pyDT.timedelta(days = 2), 1230 pyDT.timedelta(days = 4), 1231 pyDT.timedelta(days = 16), 1232 pyDT.timedelta(days = 29), 1233 pyDT.timedelta(days = 30), 1234 pyDT.timedelta(days = 31), 1235 pyDT.timedelta(days = 37), 1236 pyDT.timedelta(days = 40), 1237 pyDT.timedelta(days = 47), 1238 pyDT.timedelta(days = 126), 1239 pyDT.timedelta(days = 127), 1240 pyDT.timedelta(days = 128), 1241 pyDT.timedelta(days = 300), 1242 pyDT.timedelta(days = 359), 1243 pyDT.timedelta(days = 360), 1244 pyDT.timedelta(days = 361), 1245 pyDT.timedelta(days = 362), 1246 pyDT.timedelta(days = 363), 1247 pyDT.timedelta(days = 364), 1248 pyDT.timedelta(days = 365), 1249 pyDT.timedelta(days = 366), 1250 pyDT.timedelta(days = 367), 1251 pyDT.timedelta(days = 400), 1252 pyDT.timedelta(weeks = 52 * 30), 1253 pyDT.timedelta(weeks = 52 * 79, days = 33) 1254 ] 1255 1256 idx = 1 1257 for intv in intervals: 1258 print '%s) %s -> %s' % (idx, intv, format_interval_medically(intv)) 1259 idx += 1
1260 #-----------------------------------------------------------------------
1261 - def test_str2interval():
1262 print "testing str2interval()" 1263 print "----------------------" 1264 1265 for interval_as_str in intervals_as_str: 1266 print "input: <%s>" % interval_as_str 1267 print " ==>", str2interval(str_interval=interval_as_str) 1268 1269 return True
1270 #-------------------------------------------------
1271 - def test_date_time():
1272 print "DST currently in effect:", dst_currently_in_effect 1273 print "current UTC offset:", current_local_utc_offset_in_seconds, "seconds" 1274 print "current timezone (interval):", current_local_timezone_interval 1275 print "current timezone (ISO conformant numeric string):", current_local_iso_numeric_timezone_string 1276 print "local timezone class:", cLocalTimezone 1277 print "" 1278 tz = cLocalTimezone() 1279 print "local timezone instance:", tz 1280 print " (total) UTC offset:", tz.utcoffset(pyDT.datetime.now()) 1281 print " DST adjustment:", tz.dst(pyDT.datetime.now()) 1282 print " timezone name:", tz.tzname(pyDT.datetime.now()) 1283 print "" 1284 print "current local timezone:", gmCurrentLocalTimezone 1285 print " (total) UTC offset:", gmCurrentLocalTimezone.utcoffset(pyDT.datetime.now()) 1286 print " DST adjustment:", gmCurrentLocalTimezone.dst(pyDT.datetime.now()) 1287 print " timezone name:", gmCurrentLocalTimezone.tzname(pyDT.datetime.now()) 1288 print "" 1289 print "now here:", pydt_now_here() 1290 print ""
1291 #-------------------------------------------------
1292 - def test_str2fuzzy_timestamp_matches():
1293 print "testing function str2fuzzy_timestamp_matches" 1294 print "--------------------------------------------" 1295 1296 val = None 1297 while val != 'exit': 1298 val = raw_input('Enter date fragment ("exit" quits): ') 1299 matches = str2fuzzy_timestamp_matches(str2parse = val) 1300 for match in matches: 1301 print 'label shown :', match['label'] 1302 print 'data attached:', match['data'] 1303 print "" 1304 print "---------------"
1305 #-------------------------------------------------
1306 - def test_cFuzzyTimeStamp():
1307 print "testing fuzzy timestamp class" 1308 print "-----------------------------" 1309 1310 ts = mxDT.now() 1311 print "mx.DateTime timestamp", type(ts) 1312 print " print ... :", ts 1313 print " print '%%s' %% ...: %s" % ts 1314 print " str() :", str(ts) 1315 print " repr() :", repr(ts) 1316 1317 fts = cFuzzyTimestamp() 1318 print "\nfuzzy timestamp <%s '%s'>" % ('class', fts.__class__.__name__) 1319 for accuracy in range(1,8): 1320 fts.accuracy = accuracy 1321 print " accuracy : %s (%s)" % (accuracy, _accuracy_strings[accuracy]) 1322 print " format_accurately:", fts.format_accurately() 1323 print " strftime() :", fts.strftime('%c') 1324 print " print ... :", fts 1325 print " print '%%s' %% ... : %s" % fts 1326 print " str() :", str(fts) 1327 print " repr() :", repr(fts) 1328 raw_input('press ENTER to continue')
1329 #-------------------------------------------------
1330 - def test_get_pydt():
1331 print "testing platform for handling dates before 1970" 1332 print "-----------------------------------------------" 1333 ts = mxDT.DateTime(1935, 4, 2) 1334 fts = cFuzzyTimestamp(timestamp=ts) 1335 print "fts :", fts 1336 print "fts.get_pydt():", fts.get_pydt()
1337 #------------------------------------------------- 1338 if len(sys.argv) > 1 and sys.argv[1] == "test": 1339 1340 # GNUmed libs 1341 gmI18N.activate_locale() 1342 gmI18N.install_domain('gnumed') 1343 1344 init() 1345 1346 #test_date_time() 1347 #test_str2fuzzy_timestamp_matches() 1348 #test_cFuzzyTimeStamp() 1349 #test_get_pydt() 1350 #test_str2interval() 1351 #test_format_interval() 1352 test_format_interval_medically() 1353 1354 #=========================================================================== 1355