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