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