Package Gnumed :: Package timelinelib :: Package time :: Module wxtime
[frames] | no frames]

Source Code for Module Gnumed.timelinelib.time.wxtime

  1  # Copyright (C) 2009, 2010, 2011  Rickard Lindberg, Roger Lindberg 
  2  # 
  3  # This file is part of Timeline. 
  4  # 
  5  # Timeline is free software: you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation, either version 3 of the License, or 
  8  # (at your option) any later version. 
  9  # 
 10  # Timeline is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  # GNU General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License 
 16  # along with Timeline.  If not, see <http://www.gnu.org/licenses/>. 
 17   
 18   
 19  import sys 
 20  import re 
 21   
 22  import wx 
 23   
 24  from timelinelib.calendar.monthnames import abbreviated_name_of_month 
 25  from timelinelib.db.objects import TimePeriod 
 26  from timelinelib.db.objects import time_period_center 
 27  from timelinelib.drawing.interface import Strip 
 28  from timelinelib.drawing.utils import get_default_font 
 29  from timelinelib.time.typeinterface import TimeType 
 30   
 31   
 32  # To save computation power (used by `delta_to_microseconds`) 
 33  US_PER_SEC = 1000000 
 34  US_PER_HOUR = 60 * 60 * 1000 * 1000 
 35  US_PER_MINUTE = 60 * 1000 * 1000 
 36  US_PER_DAY = 24 * 60 * 60 * US_PER_SEC 
 37  MIN_YEAR = -4700 
 38  MAX_YEAR = 120000 
 39   
40 -class WxTimeType(TimeType):
41
42 - def __eq__(self, other):
43 return isinstance(other, WxTimeType)
44
45 - def __ne__(self, other):
46 return not (self == other)
47
48 - def time_string(self, time):
49 return time.Format("%Y-%m-%d %H:%M:%S")
50
51 - def parse_time(self, time_string):
52 match = re.search(r"^(-?\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)$", time_string) 53 if match: 54 year = int(match.group(1)) 55 month = int(match.group(2)) - 1 56 day = int(match.group(3)) 57 hour = int(match.group(4)) 58 minute = int(match.group(5)) 59 second = int(match.group(6)) 60 return try_to_create_wx_date_time_from_dmy(day, month, year, hour, minute, second) 61 else: 62 raise ValueError("Time not on correct format = '%s'" % time_string)
63
64 - def get_navigation_functions(self):
65 return [ 66 (_("Go to &Today\tCtrl+T"), go_to_today_fn), 67 (_("Go to D&ate...\tCtrl+G"), go_to_date_fn), 68 ("SEP", None), 69 (_("Backward\tPgUp"), backward_fn), 70 (_("Forward\tPgDn"), forward_fn), 71 (_("Forward One Wee&k\tCtrl+K"), forward_one_week_fn), 72 (_("Back One &Week\tCtrl+W"), backward_one_week_fn), 73 (_("Forward One Mont&h\tCtrl+h"), forward_one_month_fn), 74 (_("Back One &Month\tCtrl+M"), backward_one_month_fn), 75 (_("Forward One Yea&r\tCtrl+R"), forward_one_year_fn), 76 (_("Back One &Year\tCtrl+Y"), backward_one_year_fn), 77 ("SEP", None), 78 (_("Fit Millennium"), fit_millennium_fn), 79 (_("Fit Century"), fit_century_fn), 80 (_("Fit Decade"), fit_decade_fn), 81 (_("Fit Year"), fit_year_fn), 82 (_("Fit Month"), fit_month_fn), 83 (_("Fit Week"), fit_week_fn), 84 (_("Fit Day"), fit_day_fn), 85 ]
86
87 - def is_date_time_type(self):
88 return True
89
90 - def format_period(self, time_period):
91 """Returns a unicode string describing the time period.""" 92 def label_with_time(time): 93 return u"%s %s" % (label_without_time(time), time_label(time))
94 def label_without_time(time): 95 return "%s %s %s" % ( 96 time.Format("%d"), 97 abbreviated_name_of_month(int(time.Format("%m"))), 98 time.Format("%Y"))
99 def time_label(time): 100 return time.Format("%H:%M") 101 if time_period.is_period(): 102 if time_period.has_nonzero_time(): 103 label = u"%s to %s" % (label_with_time(time_period.start_time), 104 label_with_time(time_period.end_time)) 105 else: 106 label = u"%s to %s" % (label_without_time(time_period.start_time), 107 label_without_time(time_period.end_time)) 108 else: 109 if time_period.has_nonzero_time(): 110 label = u"%s" % label_with_time(time_period.start_time) 111 else: 112 label = u"%s" % label_without_time(time_period.start_time) 113 return label 114
115 - def format_delta(self, delta):
116 days = delta.days 117 hours = delta.hours - days * 24 118 minutes = delta.minutes - (days * 24 + hours) * 60 119 collector = [] 120 if days == 1: 121 collector.append(u"1 %s" % _("day")) 122 elif days > 1: 123 collector.append(u"%d %s" % (days, _("days"))) 124 if hours == 1: 125 collector.append(u"1 %s" % _("hour")) 126 elif hours > 1: 127 collector.append(u"%d %s" % (hours, _("hours"))) 128 if minutes == 1: 129 collector.append(u"1 %s" % _("minute")) 130 elif minutes > 1: 131 collector.append(u"%d %s" % (minutes, _("minutes"))) 132 delta_string = u" ".join(collector) 133 if delta_string == "": 134 delta_string = "0" 135 return delta_string
136
137 - def get_min_time(self):
138 min_time = wx.DateTimeFromDMY(1, 0, MIN_YEAR) 139 return (min_time, _("can't be before year %d") % (MIN_YEAR))
140
141 - def get_max_time(self):
142 max_time = wx.DateTimeFromDMY(1, 0, MAX_YEAR) 143 return (max_time, _("can't be after year %d") % (MAX_YEAR))
144
145 - def choose_strip(self, metrics, config):
146 """ 147 Return a tuple (major_strip, minor_strip) for current time period and 148 window size. 149 """ 150 today = metrics.time_period.start_time 151 tomorrow = today + wx.DateSpan.Day() 152 day_period = TimePeriod(self, today, tomorrow) 153 one_day_width = metrics.calc_exact_width(day_period) 154 if one_day_width > 600: 155 return (StripDay(), StripHour()) 156 elif one_day_width > 45: 157 return (StripWeek(config), StripWeekday()) 158 elif one_day_width > 25: 159 return (StripMonth(), StripDay()) 160 elif one_day_width > 1.5: 161 return (StripYear(), StripMonth()) 162 elif one_day_width > 0.12: 163 return (StripDecade(), StripYear()) 164 elif one_day_width > 0.012: 165 return (StripCentury(), StripDecade()) 166 else: 167 return (StripCentury(), StripCentury())
168
169 - def mult_timedelta(self, delta, num):
170 """Return a new timedelta that is `num` times larger than `delta`.""" 171 microsecs = delta_to_microseconds(delta) * num 172 delta = microseconds_to_delta(microsecs) 173 return delta
174
175 - def get_default_time_period(self):
176 return time_period_center(self, wx.DateTime.Now(), wx.TimeSpan.Days(30))
177
178 - def now(self):
179 return wx.DateTime.Now()
180
181 - def get_time_at_x(self, time_period, x_percent_of_width):
182 """Return the time at pixel `x`.""" 183 microsecs = delta_to_microseconds(time_period.delta()) 184 microsecs = microsecs * x_percent_of_width 185 return time_period.start_time + microseconds_to_delta(microsecs)
186
187 - def div_timedeltas(self, delta1, delta2):
188 """Return how many times delta2 fit in delta1.""" 189 # Since Python can handle infinitely large numbers, this solution works. It 190 # might however not be optimal. If you are clever, you should be able to 191 # treat the different parts individually. But this is simple. 192 total_us1 = delta_to_microseconds(delta1) 193 total_us2 = delta_to_microseconds(delta2) 194 # Make sure that the result is a floating point number 195 return float(total_us1) / float(total_us2)
196
197 - def get_max_zoom_delta(self):
198 max_zoom_delta = wx.TimeSpan.Days(1200 * 365) 199 return (max_zoom_delta, _("Can't zoom wider than 1200 years"))
200
201 - def get_min_zoom_delta(self):
202 return (wx.TimeSpan.Hour(), _("Can't zoom deeper than 1 hour"))
203
204 - def get_zero_delta(self):
205 return wx.TimeSpan()
206
207 - def time_period_has_nonzero_time(self, time_period):
208 nonzero_time = (not time_period.start_time.IsSameTime(wx.DateTimeFromHMS(0, 0, 0)) or 209 not time_period.end_time.IsSameTime(wx.DateTimeFromHMS(0, 0, 0)) ) 210 return nonzero_time
211
212 - def get_name(self):
213 return u"wxtime"
214
215 - def get_duplicate_functions(self):
216 return [ 217 (_("Day"), move_period_num_days), 218 (_("Week"), move_period_num_weeks), 219 (_("Month"), move_period_num_months), 220 (_("Year"), move_period_num_years), 221 ]
222
223 - def zoom_is_ok(self, delta):
224 return (delta.GetMilliseconds() > 3600000) or (delta.GetDays() > 0)
225
226 - def half_delta(self, delta):
227 microseconds = delta_to_microseconds(delta) / 2 228 return microseconds_to_delta(microseconds)
229
230 - def margin_delta(self, delta):
231 microseconds = delta_to_microseconds(delta) / 24 232 return microseconds_to_delta(microseconds)
233
234 - def event_date_string(self, time):
235 return time.Format("%Y-%m-%d")
236
237 - def event_time_string(self, time):
238 return time.Format("%H:%M")
239
240 - def eventtimes_equals(self, time1, time2):
241 s1 = "%s %s" % (self.event_date_string(time1), 242 self.event_date_string(time1)) 243 s2 = "%s %s" % (self.event_date_string(time2), 244 self.event_date_string(time2)) 245 return s1 == s2
246
247 - def adjust_for_bc_years(self, time):
248 if time.Year == 0: 249 return time + wx.DateSpan.Year() 250 else: 251 return time
252 253
254 -def go_to_today_fn(main_frame, current_period, navigation_fn):
255 navigation_fn(lambda tp: tp.center(wx.DateTime.Now()))
256 257
258 -def go_to_date_fn(main_frame, current_period, navigation_fn):
259 def navigate_to(time): 260 navigation_fn(lambda tp: tp.center(time))
261 main_frame.display_time_editor_dialog( 262 WxTimeType(), current_period.mean_time(), navigate_to, _("Go to Date")) 263 264
265 -def backward_fn(main_frame, current_period, navigation_fn):
266 move_page_smart(current_period, navigation_fn, -1)
267 268
269 -def forward_fn(main_frame, current_period, navigation_fn):
270 move_page_smart(current_period, navigation_fn, 1)
271 272
273 -def move_page_smart(current_period, navigation_fn, direction):
274 start, end = current_period.start_time, current_period.end_time 275 year_diff = end.Year - start.Year 276 start_months = start.Year * 12 + start.Month 277 end_months = end.Year * 12 + end.Month 278 month_diff = end_months - start_months 279 whole_years = start.Month == end.Month and start.Day == end.Day 280 whole_months = start.Day == 1 and end.Day == 1 281 direction_backward = direction < 0 282 # Whole years 283 if whole_years and year_diff > 0: 284 _move_smart_year(navigation_fn, direction, start, end) 285 # Whole months 286 elif whole_months and month_diff > 0: 287 _move_smart_month(navigation_fn, direction_backward, start, end) 288 # No need for smart delta 289 else: 290 navigation_fn(lambda tp: tp.move_delta(direction*current_period.delta()))
291 292
293 -def _move_smart_year(navigation_fn, direction, start, end):
294 year_diff = direction * (end.Year - start.Year) 295 start.SetYear(start.Year + year_diff) 296 end.SetYear(end.Year + year_diff) 297 navigation_fn(lambda tp: tp.update(start, end))
298 299
300 -def _months_to_year_and_month(months):
301 years = int(months / 12) 302 month = months - years * 12 303 if month == 12: 304 month = 0 305 years -=1 306 return years, month
307 308
309 -def _move_smart_month(navigation_fn, direction_backward, start, end):
310 if direction_backward: 311 _move_smart_month_backward(navigation_fn, start, end) 312 else: 313 _move_smart_month_forward(navigation_fn, start, end)
314 315
316 -def _move_smart_month_backward(navigation_fn, start, end):
317 start_months = start.Year * 12 + start.Month 318 end_months = end.Year * 12 + end.Month 319 month_diff = end_months - start_months 320 new_end = start 321 new_start_year, new_start_month = _months_to_year_and_month( 322 start_months - 323 month_diff) 324 new_start = wx.DateTimeFromDMY(start.Day, new_start_month, new_start_year, 325 start.Hour, start.Minute, start.Second) 326 navigation_fn(lambda tp: tp.update(new_start, new_end))
327 328
329 -def _move_smart_month_forward(navigation_fn, start, end):
330 start_months = start.Year * 12 + start.Month 331 end_months = end.Year * 12 + end.Month 332 month_diff = end_months - start_months 333 new_start = end 334 new_end_year, new_end_month = _months_to_year_and_month( 335 end_months + 336 month_diff) 337 new_end = wx.DateTimeFromDMY(end.Day, new_end_month, new_end_year, 338 end.Hour, end.Minute, end.Second) 339 navigation_fn(lambda tp: tp.update(new_start, new_end))
340 341
342 -def forward_one_week_fn(main_frame, current_period, navigation_fn):
343 week = wx.DateSpan.Week() 344 navigation_fn(lambda tp: tp.move_delta(week))
345 346
347 -def backward_one_week_fn(main_frame, current_period, navigation_fn):
348 week = wx.DateSpan.Week() 349 navigation_fn(lambda tp: tp.move_delta(-1 * week))
350 351 373 374
375 -def forward_one_month_fn(main_frame, current_period, navigation_fn):
376 navigate_month_step(current_period, navigation_fn, 1)
377 378
379 -def backward_one_month_fn(main_frame, current_period, navigation_fn):
380 navigate_month_step(current_period, navigation_fn, -1)
381 382
383 -def forward_one_year_fn(main_frame, current_period, navigation_fn):
384 yr = wx.DateSpan.Year() 385 navigation_fn(lambda tp: tp.move_delta(yr))
386 387
388 -def backward_one_year_fn(main_frame, current_period, navigation_fn):
389 yr = wx.DateSpan.Year() 390 navigation_fn(lambda tp: tp.move_delta(-1*yr))
391 392
393 -def fit_millennium_fn(main_frame, current_period, navigation_fn):
394 mean = current_period.mean_time() 395 start = wx.DateTimeFromDMY(1, 0, int(mean.Year/1000)*1000) 396 end = wx.DateTimeFromDMY(1, 0, int(mean.Year/1000)*1000 + 1000) 397 navigation_fn(lambda tp: tp.update(start, end))
398 399
400 -def fit_century_fn(main_frame, current_period, navigation_fn):
401 mean = current_period.mean_time() 402 start = wx.DateTimeFromDMY(1, 0, int(mean.Year/100)*100) 403 end = wx.DateTimeFromDMY(1, 0, int(mean.Year/100)*100 + 100) 404 navigation_fn(lambda tp: tp.update(start, end))
405 406
407 -def fit_decade_fn(main_frame, current_period, navigation_fn):
408 mean = current_period.mean_time() 409 start = wx.DateTimeFromDMY(1, 0, int(mean.Year/10)*10) 410 end = wx.DateTimeFromDMY(1, 0, int(mean.Year/10)*10+10) 411 navigation_fn(lambda tp: tp.update(start, end))
412 413
414 -def fit_year_fn(main_frame, current_period, navigation_fn):
415 mean = current_period.mean_time() 416 start = wx.DateTimeFromDMY(1, 0, mean.Year) 417 end = wx.DateTimeFromDMY(1, 0, mean.Year + 1) 418 navigation_fn(lambda tp: tp.update(start, end))
419 420
421 -def fit_month_fn(main_frame, current_period, navigation_fn):
422 mean = current_period.mean_time() 423 start = wx.DateTimeFromDMY(1, mean.Month, mean.Year) 424 if mean.Month == 11: 425 end = wx.DateTimeFromDMY(1, 0, mean.Year + 1) 426 else: 427 end = wx.DateTimeFromDMY(1, mean.Month + 1, mean.Year) 428 navigation_fn(lambda tp: tp.update(start, end))
429 430
431 -def fit_day_fn(main_frame, current_period, navigation_fn):
432 mean = current_period.mean_time() 433 start = wx.DateTimeFromDMY(mean.Day, mean.Month, mean.Year) 434 end = start + wx.DateSpan.Day() 435 navigation_fn(lambda tp: tp.update(start, end))
436 437
438 -def fit_week_fn(main_frame, current_period, navigation_fn):
439 mean = current_period.mean_time() 440 start = wx.DateTimeFromDMY(mean.Day, mean.Month, mean.Year) 441 start.SetToWeekDayInSameWeek(1) 442 if not main_frame.week_starts_on_monday(): 443 start = start - wx.DateSpan.Day() 444 end = start + wx.DateSpan.Days(7) 445 navigation_fn(lambda tp: tp.update(start, end))
446 447
448 -class StripCentury(Strip):
449
450 - def label(self, time, major=False):
451 if major: 452 # TODO: This only works for English. Possible to localize? 453 start_year = self._century_start_year(time.Year) 454 next_start_year = start_year + 100 455 return str(next_start_year / 100) + " century" 456 return ""
457
458 - def start(self, time):
459 return wx.DateTimeFromDMY(1, 0, max(self._century_start_year(time.Year), MIN_YEAR))
460
461 - def increment(self, time):
462 return time + wx.DateSpan.Years(100)
463
464 - def get_font(self, time_period):
465 return get_default_font(8)
466
467 - def _century_start_year(self, year):
468 return (int(year) / 100) * 100
469 470
471 -class StripDecade(Strip):
472
473 - def label(self, time, major=False):
474 # TODO: This only works for English. Possible to localize? 475 return str(self._decade_start_year(time.Year)) + "s"
476
477 - def start(self, time):
478 return wx.DateTimeFromDMY(1, 0, self._decade_start_year(time.Year))
479
480 - def increment(self, time):
481 return time + wx.DateSpan.Year() * 10
482
483 - def _decade_start_year(self, year):
484 return (int(year) / 10) * 10
485
486 - def get_font(self, time_period):
487 return get_default_font(8)
488 489
490 -class StripYear(Strip):
491
492 - def label(self, time, major=False):
493 return str(wx.DateTime.ConvertYearToBC(time.Year))
494
495 - def start(self, time):
496 return wx.DateTimeFromDMY(1, 0, time.Year)
497
498 - def increment(self, time):
499 return time + wx.DateSpan.Year()
500
501 - def get_font(self, time_period):
502 return get_default_font(8)
503 504
505 -class StripMonth(Strip):
506
507 - def label(self, time, major=False):
508 if major: 509 return "%s %s" % (time.GetMonthName(time.Month, time.Name_Abbr), time.Year) 510 return time.GetMonthName(time.Month, wx.DateTime.Name_Abbr)
511
512 - def start(self, time):
513 return wx.DateTimeFromDMY(1, time.Month, time.Year)
514
515 - def increment(self, time):
516 if time.Month < 11: 517 return wx.DateTimeFromDMY(1, time.Month + 1, time.Year, 0, 0) 518 else: 519 return wx.DateTimeFromDMY(1, 0, time.Year + 1, 0, 0)
520
521 - def get_font(self, time_period):
522 return get_default_font(8)
523 524
525 -class StripDay(Strip):
526
527 - def label(self, time, major=False):
528 if major: 529 month_name = time.GetMonthName(time.Month, time.Name_Abbr) 530 return "%s %s %s" % (time.Day, month_name, time.Year) 531 return str(time.Day)
532
533 - def start(self, time):
534 return wx.DateTimeFromDMY(time.Day, time.Month, time.Year)
535
536 - def increment(self, time):
537 return time + wx.DateSpan.Day()
538
539 - def get_font(self, time_period):
540 saturday_or_sunday = (0,6) 541 bold = False 542 if (time_period.start_time.GetWeekDay() in saturday_or_sunday): 543 bold = True 544 return get_default_font(8, bold)
545 546
547 -class StripWeek(Strip):
548
549 - def __init__(self, config):
550 Strip.__init__(self) 551 self.config = config
552
553 - def label(self, time, major=False):
554 if major: 555 # Example: Week 23 (1-7 Jan 2009) 556 first_weekday = self.start(time) 557 next_first_weekday = self.increment(first_weekday) 558 last_weekday = next_first_weekday - wx.DateSpan.Day() 559 range_string = self._time_range_string(first_weekday, last_weekday) 560 if self.config.week_start == "monday": 561 return (_("Week") + " %s (%s)") % (time.GetWeekOfYear(), range_string) 562 else: 563 # It is sunday (don't know what to do about week numbers here) 564 return range_string 565 # This strip should never be used as minor 566 return ""
567
568 - def start(self, time):
569 stripped_date = wx.DateTimeFromDMY(time.Day, time.Month, time.Year) 570 if self.config.week_start == "monday": 571 days_to_subtract = stripped_date.GetWeekDay() - 1 572 else: # It is sunday 573 days_to_subtract = stripped_date.GetWeekDay() % 7 574 return stripped_date - wx.DateSpan.Days(days_to_subtract)
575
576 - def increment(self, time):
577 return time + wx.DateSpan.Week()
578
579 - def get_font(self, time_period):
580 return get_default_font(8)
581
582 - def _time_range_string(self, time1, time2):
583 """ 584 Examples: 585 586 * 1-7 Jun 2009 587 * 28 Jun-3 Jul 2009 588 * 28 Jun 08-3 Jul 2009 589 """ 590 if time1.Year == time2.Year: 591 if time1.Month == time2.Month: 592 return "%s-%s %s %s" % (time1.Day, time2.Day, 593 time1.GetMonthName(time1.Month, time1.Name_Abbr), 594 time1.Year) 595 return "%s %s-%s %s %s" % (time1.Day, 596 time1.GetMonthName(time1.Month, time1.Name_Abbr), 597 time2.Day, 598 time2.GetMonthName(time2.Month, time2.Name_Abbr), 599 time1.Year) 600 return "%s %s %s-%s %s %s" % (time1.Day, 601 time1.GetMonthName(time1.Month, time1.Name_Abbr), 602 time1.Year, 603 time2.Day, 604 time2.GetMonthName(time2.Month, time2.Name_Abbr), 605 time2.Year)
606 607
608 -class StripWeekday(Strip):
609
610 - def label(self, time, major=False):
611 if major: 612 # This strip should never be used as major 613 return "" 614 return time.GetWeekDayName(time.GetWeekDay(), wx.DateTime.Name_Abbr)
615
616 - def start(self, time):
617 return wx.DateTimeFromDMY(time.Day, time.Month, time.Year)
618
619 - def increment(self, time):
620 return time + wx.DateSpan.Day()
621
622 - def get_font(self, time_period):
623 return get_default_font(8)
624 625
626 -class StripHour(Strip):
627
628 - def label(self, time, major=False):
629 if major: 630 # This strip should never be used as major 631 return "" 632 return str(time.Hour)
633
634 - def start(self, time):
635 start_time = wx.DateTimeFromDMY(time.Day, time.Month, time.Year, time.Hour) 636 return start_time
637
638 - def increment(self, time):
639 new_time = time + wx.TimeSpan.Hour() 640 return new_time
641
642 - def get_font(self, time_period):
643 return get_default_font(8)
644 645
646 -def microseconds_to_delta(microsecs):
647 # The wx.TimeSpan.Milliseconds(ms) take a wxLong argument instead 648 # of the expected wxLonLong. And that's why we can't code like 649 # ms = delta.GetMilliseconds() / 2 650 # return wx.TimeSpan.Milliseconds(ms) 651 counter = 0 652 milliseconds = microsecs / 1000 653 while abs(milliseconds) > sys.maxint: 654 milliseconds = milliseconds / 2 655 counter += 1 656 delta = wx.TimeSpan.Milliseconds(milliseconds) 657 while counter > 0: 658 delta = delta * 2; 659 counter -= 1 660 return delta
661 662
663 -def delta_to_microseconds(delta):
664 """Return the number of microseconds that the delta represents.""" 665 days = delta.GetDays() 666 hours = delta.GetHours() 667 minutes = delta.GetMinutes() 668 seconds = delta.GetSeconds() 669 milliseconds = delta.GetMilliseconds() 670 neg = False 671 if days < 0: 672 neg = True 673 days = -days 674 hours = -hours 675 minutes = -minutes 676 seconds = -seconds 677 milliseconds = - milliseconds 678 microsecs = days * US_PER_DAY 679 if hours >= 0: 680 microsecs = hours * US_PER_HOUR 681 if minutes >= 0: 682 microsecs = minutes * US_PER_MINUTE 683 if seconds >= 0: 684 microsecs = seconds * US_PER_SEC 685 if milliseconds >= 0: 686 microsecs = milliseconds * 1000 687 if neg: 688 microsecs = -microsecs 689 return microsecs
690 691
692 -def move_period_num_days(period, num):
693 return _move_period_time_span(period, wx.TimeSpan.Days(num))
694 695
696 -def move_period_num_weeks(period, num):
697 return _move_period_time_span(period, wx.TimeSpan.Days(7*num))
698 699
700 -def move_period_num_months(period, num):
701 new_start_time = period.start_time + wx.DateSpan.Months(num) 702 if new_start_time.Day != period.start_time.Day: 703 return None 704 return _move_period_time_span(period, new_start_time-period.start_time)
705 706
707 -def move_period_num_years(period, num):
708 new_start_time = period.start_time + wx.DateSpan.Years(num) 709 if (new_start_time.Month != period.start_time.Month or 710 new_start_time.Day != period.start_time.Day): 711 return None 712 return _move_period_time_span(period, new_start_time-period.start_time)
713 714
715 -def _move_period_time_span(period, time_span):
716 try: 717 new_start = period.start_time + time_span 718 new_end = period.end_time + time_span 719 return TimePeriod(period.time_type, new_start, new_end) 720 except ValueError: 721 return None
722 723
724 -def try_to_create_wx_date_time_from_dmy(day, month, year, hour=0, minute=0, second=0):
725 def fail_with_invalid_date(): 726 raise ValueError("Invalid date")
727 try: 728 datetime = wx.DateTimeFromDMY(day, month, year, hour, minute, second) 729 except AssertionError: 730 fail_with_invalid_date() 731 else: 732 if not datetime.IsValid(): 733 fail_with_invalid_date() 734 return datetime 735