Package Gnumed :: Package timelinelib :: Package calendar :: Package gregorian :: Module timetype
[frames] | no frames]

Source Code for Module Gnumed.timelinelib.calendar.gregorian.timetype

  1  # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018  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  from datetime import datetime 
 20  import re 
 21   
 22  from timelinelib.calendar.gregorian.gregorian import GregorianDateTime 
 23  from timelinelib.calendar.gregorian.monthnames import abbreviated_name_of_month 
 24  from timelinelib.calendar.gregorian.time import GregorianDelta 
 25  from timelinelib.calendar.gregorian.time import GregorianTime 
 26  from timelinelib.calendar.gregorian.time import SECONDS_IN_DAY 
 27  from timelinelib.calendar.gregorian.weekdaynames import abbreviated_name_of_weekday 
 28  from timelinelib.calendar.timetype import TimeType 
 29  from timelinelib.canvas.data import TimeOutOfRangeLeftError 
 30  from timelinelib.canvas.data import TimeOutOfRangeRightError 
 31  from timelinelib.canvas.data import TimePeriod 
 32  from timelinelib.canvas.data import time_period_center 
 33  from timelinelib.canvas.drawing.interface import Strip 
 34   
 35   
 36  BC = _("BC") 
37 38 39 -class GregorianTimeType(TimeType):
40
41 - def __eq__(self, other):
42 return isinstance(other, GregorianTimeType)
43
44 - def __ne__(self, other):
45 return not (self == other)
46
47 - def time_string(self, time):
48 return "%d-%02d-%02d %02d:%02d:%02d" % GregorianDateTime.from_time(time).to_tuple()
49
50 - def parse_time(self, time_string):
51 match = re.search(r"^(-?\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)$", time_string) 52 if match: 53 year = int(match.group(1)) 54 month = int(match.group(2)) 55 day = int(match.group(3)) 56 hour = int(match.group(4)) 57 minute = int(match.group(5)) 58 second = int(match.group(6)) 59 try: 60 return GregorianDateTime(year, month, day, hour, minute, second).to_time() 61 except ValueError: 62 raise ValueError("Invalid time, time string = '%s'" % time_string) 63 else: 64 raise ValueError("Time not on correct format = '%s'" % time_string)
65
66 - def get_navigation_functions(self):
67 return [ 68 (_("Go to &Today") + "\tCtrl+T", go_to_today_fn), 69 (_("Go to &Date...") + "\tCtrl+G", go_to_date_fn), 70 ("SEP", None), 71 (_("Backward") + "\tPgUp", backward_fn), 72 (_("Forward") + "\tPgDn", forward_fn), 73 (_("Forward One Wee&k") + "\tCtrl+K", forward_one_week_fn), 74 (_("Back One &Week") + "\tCtrl+W", backward_one_week_fn), 75 (_("Forward One Mont&h") + "\tCtrl+H", forward_one_month_fn), 76 (_("Back One &Month") + "\tCtrl+M", backward_one_month_fn), 77 (_("Forward One Yea&r") + "\tCtrl+R", forward_one_year_fn), 78 (_("Back One &Year") + "\tCtrl+Y", backward_one_year_fn), 79 ("SEP", None), 80 (_("Fit Millennium"), fit_millennium_fn), 81 (_("Fit Century"), create_strip_fitter(StripCentury)), 82 (_("Fit Decade"), create_strip_fitter(StripDecade)), 83 (_("Fit Year"), create_strip_fitter(StripYear)), 84 (_("Fit Month"), create_strip_fitter(StripMonth)), 85 (_("Fit Week"), fit_week_fn), 86 (_("Fit Day"), create_strip_fitter(StripDay)), 87 ]
88
89 - def format_period(self, time_period):
90 """Returns a unicode string describing the time period.""" 91 def label_with_time(time): 92 return "%s %s" % (label_without_time(time), time_label(time))
93 94 def label_without_time(time): 95 gregorian_datetime = GregorianDateTime.from_time(time) 96 return "%s %s %s" % ( 97 gregorian_datetime.day, 98 abbreviated_name_of_month(gregorian_datetime.month), 99 format_year(gregorian_datetime.year) 100 )
101 102 def time_label(time): 103 return "%02d:%02d" % time.get_time_of_day()[:-1] 104 if time_period.is_period(): 105 if has_nonzero_time(time_period): 106 label = "%s to %s" % (label_with_time(time_period.start_time), 107 label_with_time(time_period.end_time)) 108 else: 109 label = "%s to %s" % (label_without_time(time_period.start_time), 110 label_without_time(time_period.end_time)) 111 else: 112 if has_nonzero_time(time_period): 113 label = "%s" % label_with_time(time_period.start_time) 114 else: 115 label = "%s" % label_without_time(time_period.start_time) 116 return label 117
118 - def format_delta(self, delta):
119 days = abs(delta.get_days()) 120 seconds = abs(delta.seconds) - days * SECONDS_IN_DAY 121 delta_format = (YEARS, DAYS, HOURS, MINUTES, SECONDS) 122 return DurationFormatter([days, seconds]).format(delta_format)
123
124 - def get_min_time(self):
125 return GregorianTime.min()
126
127 - def get_max_time(self):
128 return GregorianTime(5369833, 0)
129
130 - def choose_strip(self, metrics, appearance):
131 """ 132 Return a tuple (major_strip, minor_strip) for current time period and 133 window size. 134 """ 135 day_period = TimePeriod(GregorianTime(0, 0), GregorianTime(1, 0)) 136 one_day_width = metrics.calc_exact_width(day_period) 137 if one_day_width > 20000: 138 return (StripHour(), StripMinute()) 139 elif one_day_width > 600: 140 return (StripDay(), StripHour()) 141 elif one_day_width > 45: 142 return (StripWeek(appearance), StripWeekday()) 143 elif one_day_width > 25: 144 return (StripMonth(), StripDay()) 145 elif one_day_width > 1.5: 146 return (StripYear(), StripMonth()) 147 elif one_day_width > 0.12: 148 return (StripDecade(), StripYear()) 149 elif one_day_width > 0.012: 150 return (StripCentury(), StripDecade()) 151 else: 152 return (StripCentury(), StripCentury())
153
154 - def get_default_time_period(self):
155 return time_period_center(self.now(), GregorianDelta.from_days(30))
156
157 - def supports_saved_now(self):
158 return False
159
160 - def set_saved_now(self, time):
161 ()
162
163 - def now(self):
164 py = datetime.now() 165 gregorian = GregorianDateTime( 166 py.year, 167 py.month, 168 py.day, 169 py.hour, 170 py.minute, 171 py.second 172 ) 173 return gregorian.to_time()
174
175 - def get_min_zoom_delta(self):
176 return (GregorianDelta.from_seconds(60), _("Can't zoom deeper than 1 minute"))
177
178 - def get_name(self):
179 return "gregoriantime"
180
181 - def get_duplicate_functions(self):
182 return [ 183 (_("Day"), move_period_num_days), 184 (_("Week"), move_period_num_weeks), 185 (_("Month"), move_period_num_months), 186 (_("Year"), move_period_num_years), 187 ]
188
189 - def is_special_day(self, time):
190 return False
191
192 - def is_weekend_day(self, time):
193 return self.get_day_of_week(time) in (5, 6)
194
195 - def get_day_of_week(self, time):
196 return time.julian_day % 7
197
198 - def create_time_picker(self, parent, *args, **kwargs):
199 from timelinelib.calendar.gregorian.timepicker.datetime import GregorianDateTimePicker 200 return GregorianDateTimePicker(parent, *args, **kwargs)
201
202 - def create_period_picker(self, parent, *args, **kwargs):
203 from timelinelib.calendar.gregorian.timepicker.period import GregorianPeriodPicker 204 return GregorianPeriodPicker(parent, *args, **kwargs)
205
206 207 -def go_to_today_fn(main_frame, current_period, navigation_fn):
208 navigation_fn(lambda tp: tp.center(GregorianTimeType().now()))
209
210 211 -def go_to_date_fn(main_frame, current_period, navigation_fn):
212 def navigate_to(time): 213 navigation_fn(lambda tp: tp.center(time))
214 main_frame.display_time_editor_dialog( 215 GregorianTimeType(), current_period.mean_time(), navigate_to, _("Go to Date")) 216
217 218 -def backward_fn(main_frame, current_period, navigation_fn):
219 _move_page_smart(current_period, navigation_fn, -1)
220
221 222 -def forward_fn(main_frame, current_period, navigation_fn):
223 _move_page_smart(current_period, navigation_fn, 1)
224
225 226 -def _move_page_smart(current_period, navigation_fn, direction):
227 if _whole_number_of_years(current_period): 228 _move_page_years(current_period, navigation_fn, direction) 229 elif _whole_number_of_months(current_period): 230 _move_page_months(current_period, navigation_fn, direction) 231 else: 232 navigation_fn(lambda tp: tp.move_delta(direction * current_period.delta()))
233
234 235 -def _whole_number_of_years(period):
236 """ 237 >>> from timelinelib.test.utils import gregorian_period 238 239 >>> _whole_number_of_years(gregorian_period("1 Jan 2013", "1 Jan 2014")) 240 True 241 242 >>> _whole_number_of_years(gregorian_period("1 Jan 2013", "1 Jan 2015")) 243 True 244 245 >>> _whole_number_of_years(gregorian_period("1 Feb 2013", "1 Feb 2014")) 246 False 247 248 >>> _whole_number_of_years(gregorian_period("1 Jan 2013", "1 Feb 2014")) 249 False 250 """ 251 return (GregorianDateTime.from_time(period.start_time).is_first_day_in_year() and 252 GregorianDateTime.from_time(period.end_time).is_first_day_in_year() and 253 _calculate_year_diff(period) > 0)
254
255 256 -def _move_page_years(curret_period, navigation_fn, direction):
257 def navigate(tp): 258 year_delta = direction * _calculate_year_diff(curret_period) 259 gregorian_start = GregorianDateTime.from_time(curret_period.start_time) 260 gregorian_end = GregorianDateTime.from_time(curret_period.end_time) 261 new_start_year = gregorian_start.year + year_delta 262 new_end_year = gregorian_end.year + year_delta 263 try: 264 new_start = gregorian_start.replace(year=new_start_year).to_time() 265 new_end = gregorian_end.replace(year=new_end_year).to_time() 266 if new_end > GregorianTimeType().get_max_time(): 267 raise ValueError() 268 if new_start < GregorianTimeType().get_min_time(): 269 raise ValueError() 270 except ValueError: 271 if direction < 0: 272 raise TimeOutOfRangeLeftError() 273 else: 274 raise TimeOutOfRangeRightError() 275 return tp.update(new_start, new_end)
276 navigation_fn(navigate) 277
278 279 -def _calculate_year_diff(period):
280 return (GregorianDateTime.from_time(period.end_time).year - 281 GregorianDateTime.from_time(period.start_time).year)
282
283 284 -def _whole_number_of_months(period):
285 """ 286 >>> from timelinelib.test.utils import gregorian_period 287 288 >>> _whole_number_of_months(gregorian_period("1 Jan 2013", "1 Jan 2014")) 289 True 290 291 >>> _whole_number_of_months(gregorian_period("1 Jan 2013", "1 Mar 2014")) 292 True 293 294 >>> _whole_number_of_months(gregorian_period("2 Jan 2013", "2 Mar 2014")) 295 False 296 297 >>> _whole_number_of_months(gregorian_period("1 Jan 2013 12:00", "1 Mar 2014")) 298 False 299 """ 300 start, end = GregorianDateTime.from_time(period.start_time), GregorianDateTime.from_time(period.end_time) 301 start_months = start.year * 12 + start.month 302 end_months = end.year * 12 + end.month 303 month_diff = end_months - start_months 304 return (start.is_first_of_month() and 305 end.is_first_of_month() and 306 month_diff > 0)
307
308 309 -def _move_page_months(curret_period, navigation_fn, direction):
310 def navigate(tp): 311 start = GregorianDateTime.from_time(curret_period.start_time) 312 end = GregorianDateTime.from_time(curret_period.end_time) 313 start_months = start.year * 12 + start.month 314 end_months = end.year * 12 + end.month 315 month_diff = end_months - start_months 316 month_delta = month_diff * direction 317 new_start_year, new_start_month = _months_to_year_and_month(start_months + month_delta) 318 new_end_year, new_end_month = _months_to_year_and_month(end_months + month_delta) 319 try: 320 new_start = start.replace(year=new_start_year, month=new_start_month) 321 new_end = end.replace(year=new_end_year, month=new_end_month) 322 start = new_start.to_time() 323 end = new_end.to_time() 324 if end > GregorianTimeType().get_max_time(): 325 raise ValueError() 326 if start < GregorianTimeType().get_min_time(): 327 raise ValueError() 328 except ValueError: 329 if direction < 0: 330 raise TimeOutOfRangeLeftError() 331 else: 332 raise TimeOutOfRangeRightError() 333 return tp.update(start, end)
334 navigation_fn(navigate) 335
336 337 -def _months_to_year_and_month(months):
338 years = int(months // 12) 339 month = months - years * 12 340 if month == 0: 341 month = 12 342 years -= 1 343 return years, month
344
345 346 -def forward_one_week_fn(main_frame, current_period, navigation_fn):
347 wk = GregorianDelta.from_days(7) 348 navigation_fn(lambda tp: tp.move_delta(wk))
349
350 351 -def backward_one_week_fn(main_frame, current_period, navigation_fn):
352 wk = GregorianDelta.from_days(7) 353 navigation_fn(lambda tp: tp.move_delta(-1 * wk))
354 379
380 381 -def forward_one_month_fn(main_frame, current_period, navigation_fn):
382 navigate_month_step(current_period, navigation_fn, 1)
383
384 385 -def backward_one_month_fn(main_frame, current_period, navigation_fn):
386 navigate_month_step(current_period, navigation_fn, -1)
387
388 389 -def forward_one_year_fn(main_frame, current_period, navigation_fn):
390 yr = GregorianDelta.from_days(365) 391 navigation_fn(lambda tp: tp.move_delta(yr))
392
393 394 -def backward_one_year_fn(main_frame, current_period, navigation_fn):
395 yr = GregorianDelta.from_days(365) 396 navigation_fn(lambda tp: tp.move_delta(-1 * yr))
397
398 399 -def fit_millennium_fn(main_frame, current_period, navigation_fn):
400 mean = GregorianDateTime.from_time(current_period.mean_time()) 401 if mean.year > get_millenium_max_year(): 402 year = get_millenium_max_year() 403 else: 404 year = max(get_min_year_containing_jan_1(), int(mean.year // 1000) * 1000) 405 start = GregorianDateTime.from_ymd(year, 1, 1).to_time() 406 end = GregorianDateTime.from_ymd(year + 1000, 1, 1).to_time() 407 navigation_fn(lambda tp: tp.update(start, end))
408
409 410 -def get_min_year_containing_jan_1():
411 return GregorianDateTime.from_time(GregorianTimeType().get_min_time()).year + 1
412
413 414 -def get_millenium_max_year():
415 return GregorianDateTime.from_time(GregorianTimeType().get_max_time()).year - 1000
416
417 418 -def fit_week_fn(main_frame, current_period, navigation_fn):
419 mean = GregorianDateTime.from_time(current_period.mean_time()) 420 start = GregorianDateTime.from_ymd(mean.year, mean.month, mean.day).to_time() 421 weekday = GregorianTimeType().get_day_of_week(start) 422 start = start - GregorianDelta.from_days(weekday) 423 if not main_frame.week_starts_on_monday(): 424 start = start - GregorianDelta.from_days(1) 425 end = start + GregorianDelta.from_days(7) 426 navigation_fn(lambda tp: tp.update(start, end))
427
428 429 -def create_strip_fitter(strip_cls):
430 def fit(main_frame, current_period, navigation_fn): 431 def navigate(time_period): 432 strip = strip_cls() 433 start = strip.start(current_period.mean_time()) 434 end = strip.increment(start) 435 return time_period.update(start, end)
436 navigation_fn(navigate) 437 return fit 438
439 440 -class StripCentury(Strip):
441 442 """ 443 Year Name | Year integer | Decade name 444 ----------+--------------+------------ 445 .. | .. | 446 200 BC | -199 | 200s BC (100 years) 447 ----------+--------------+------------ 448 199 BC | -198 | 449 ... | ... | 100s BC (100 years) 450 100 BC | -99 | 451 ----------+--------------+------------ 452 99 BC | -98 | 453 ... | ... | 0s BC (only 99 years) 454 1 BC | 0 | 455 ----------+--------------+------------ 456 1 | 1 | 457 ... | ... | 0s (only 99 years) 458 99 | 99 | 459 ----------+--------------+------------ 460 100 | 100 | 461 .. | .. | 100s (100 years) 462 199 | 199 | 463 ----------+--------------+------------ 464 200 | 200 | 200s (100 years) 465 .. | .. | 466 """ 467
468 - def label(self, time, major=False):
469 if major: 470 gregorian_time = GregorianDateTime.from_time(time) 471 return self._format_century( 472 self._century_number( 473 self._century_start_year(gregorian_time.year) 474 ), 475 gregorian_time.is_bc() 476 ) 477 else: 478 return ""
479
480 - def start(self, time):
481 return GregorianDateTime.from_ymd( 482 self._century_start_year(GregorianDateTime.from_time(time).year), 483 1, 484 1 485 ).to_time()
486
487 - def increment(self, time):
488 gregorian_time = GregorianDateTime.from_time(time) 489 return gregorian_time.replace( 490 year=self._next_century_start_year(gregorian_time.year) 491 ).to_time()
492
493 - def _century_number(self, century_start_year):
494 if century_start_year > 99: 495 return century_start_year 496 elif century_start_year >= -98: 497 return 0 498 else: # century_start_year < -98: 499 return self._century_number(-century_start_year - 98)
500
501 - def _next_century_start_year(self, start_year):
502 return start_year + self._century_year_len(start_year)
503
504 - def _century_year_len(self, start_year):
505 if start_year in [-98, 1]: 506 return 99 507 else: 508 return 100
509
510 - def _format_century(self, century_number, is_bc):
511 if is_bc: 512 return "{century}s {bc}".format(century=century_number, bc=BC) 513 else: 514 return "{century}s".format(century=century_number)
515
516 - def _century_start_year(self, year):
517 if year > 99: 518 return year - int(year) % 100 519 elif year >= 1: 520 return 1 521 elif year >= -98: 522 return -98 523 else: # year < -98 524 return -self._century_start_year(-year + 1) - 98
525
526 527 -class StripDecade(Strip):
528 529 """ 530 Year Name | Year integer | Decade name 531 ----------+--------------+------------ 532 .. | .. | 533 20 BC | -19 | 20s BC (10 years) 534 ----------+--------------+------------ 535 19 BC | -18 | 536 18 BC | -17 | 537 17 BC | -16 | 538 16 BC | -15 | 539 15 BC | -14 | 10s BC (10 years) 540 14 BC | -13 | 541 13 BC | -12 | 542 12 BC | -11 | 543 11 BC | -10 | 544 10 BC | -9 | 545 ----------+--------------+------------ 546 9 BC | -8 | 547 8 BC | -7 | 548 7 BC | -6 | 549 6 BC | -5 | 550 5 BC | -4 | 0s BC (only 9 years) 551 4 BC | -3 | 552 3 BC | -2 | 553 2 BC | -1 | 554 1 BC | 0 | 555 ----------+--------------+------------ 556 1 | 1 | 557 2 | 2 | 558 3 | 3 | 559 4 | 4 | 560 5 | 5 | 0s (only 9 years) 561 6 | 6 | 562 7 | 7 | 563 8 | 8 | 564 9 | 9 | 565 ----------+--------------+------------ 566 10 | 10 | 567 11 | 11 | 568 12 | 12 | 569 13 | 13 | 570 14 | 14 | 571 15 | 15 | 10s (10 years) 572 16 | 16 | 573 17 | 17 | 574 18 | 18 | 575 19 | 19 | 576 ----------+--------------+------------ 577 20 | 20 | 20s (10 years) 578 .. | .. | 579 """ 580
581 - def __init__(self):
582 self.skip_s_in_decade_text = False
583
584 - def label(self, time, major=False):
585 gregorian_time = GregorianDateTime.from_time(time) 586 return self._format_decade( 587 self._decade_number(self._decade_start_year(gregorian_time.year)), 588 gregorian_time.is_bc() 589 )
590
591 - def start(self, time):
592 return GregorianDateTime.from_ymd( 593 self._decade_start_year(GregorianDateTime.from_time(time).year), 594 1, 595 1 596 ).to_time()
597
598 - def increment(self, time):
599 gregorian_time = GregorianDateTime.from_time(time) 600 return gregorian_time.replace( 601 year=self._next_decacde_start_year(gregorian_time.year) 602 ).to_time()
603
604 - def set_skip_s_in_decade_text(self, value):
605 self.skip_s_in_decade_text = value
606
607 - def _format_decade(self, decade_number, is_bc):
608 parts = [] 609 parts.append("{0}".format(decade_number)) 610 if not self.skip_s_in_decade_text: 611 parts.append("s") 612 if is_bc: 613 parts.append(" ") 614 parts.append(BC) 615 return "".join(parts)
616
617 - def _decade_start_year(self, year):
618 if year > 9: 619 return int(year) - (int(year) % 10) 620 elif year >= 1: 621 return 1 622 elif year >= -8: 623 return -8 624 else: # year < -8 625 return -self._decade_start_year(-year + 1) - 8
626
627 - def _next_decacde_start_year(self, start_year):
628 return start_year + self._decade_year_len(start_year)
629
630 - def _decade_year_len(self, start_year):
631 if self._decade_number(start_year) == 0: 632 return 9 633 else: 634 return 10
635
636 - def _decade_number(self, start_year):
637 if start_year > 9: 638 return start_year 639 elif start_year >= -8: 640 return 0 641 else: # start_year < -8 642 return self._decade_number(-start_year - 8)
643
644 645 -class StripYear(Strip):
646
647 - def label(self, time, major=False):
649
650 - def start(self, time):
651 gregorian_time = GregorianDateTime.from_time(time) 652 new_gregorian = GregorianDateTime.from_ymd(gregorian_time.year, 1, 1) 653 return new_gregorian.to_time()
654
655 - def increment(self, time):
656 gregorian_time = GregorianDateTime.from_time(time) 657 return gregorian_time.replace(year=gregorian_time.year + 1).to_time()
658
659 660 -class StripMonth(Strip):
661
662 - def label(self, time, major=False):
663 time = GregorianDateTime.from_time(time) 664 if major: 665 return "%s %s" % (abbreviated_name_of_month(time.month), 666 format_year(time.year)) 667 return abbreviated_name_of_month(time.month)
668
669 - def start(self, time):
670 gregorian_time = GregorianDateTime.from_time(time) 671 return GregorianDateTime.from_ymd( 672 gregorian_time.year, 673 gregorian_time.month, 674 1 675 ).to_time()
676
677 - def increment(self, time):
681
682 683 -class StripDay(Strip):
684
685 - def label(self, time, major=False):
686 time = GregorianDateTime.from_time(time) 687 if major: 688 return "%s %s %s" % (time.day, 689 abbreviated_name_of_month(time.month), 690 format_year(time.year)) 691 return str(time.day)
692
693 - def start(self, time):
694 gregorian_time = GregorianDateTime.from_time(time) 695 return GregorianDateTime.from_ymd( 696 gregorian_time.year, 697 gregorian_time.month, 698 gregorian_time.day 699 ).to_time()
700
701 - def increment(self, time):
702 return time + GregorianDelta.from_days(1)
703
704 - def is_day(self):
705 return True
706
707 708 -class StripWeek(Strip):
709
710 - def __init__(self, appearance):
711 Strip.__init__(self) 712 self.appearance = appearance
713
714 - def label(self, time, major=False):
715 if major: 716 first_weekday = self.start(time) 717 next_first_weekday = self.increment(first_weekday) 718 last_weekday = next_first_weekday - GregorianDelta.from_days(1) 719 range_string = self._time_range_string(first_weekday, last_weekday) 720 if self.appearance.get_week_start() == "monday": 721 return (_("Week") + " %s (%s)") % ( 722 GregorianDateTime.from_time(time).week_number, 723 range_string 724 ) 725 else: 726 # It is sunday (don't know what to do about week numbers here) 727 return range_string 728 # This strip should never be used as minor 729 return ""
730
731 - def _time_range_string(self, start, end):
732 start = GregorianDateTime.from_time(start) 733 end = GregorianDateTime.from_time(end) 734 if start.year == end.year: 735 if start.month == end.month: 736 return "%s-%s %s %s" % (start.day, end.day, 737 abbreviated_name_of_month(start.month), 738 format_year(start.year)) 739 return "%s %s-%s %s %s" % (start.day, 740 abbreviated_name_of_month(start.month), 741 end.day, 742 abbreviated_name_of_month(end.month), 743 format_year(start.year)) 744 return "%s %s %s-%s %s %s" % (start.day, 745 abbreviated_name_of_month(start.month), 746 format_year(start.year), 747 end.day, 748 abbreviated_name_of_month(end.month), 749 format_year(end.year))
750
751 - def start(self, time):
752 if self.appearance.get_week_start() == "monday": 753 days_to_subtract = GregorianTimeType().get_day_of_week(time) 754 else: 755 # It is sunday 756 days_to_subtract = (GregorianTimeType().get_day_of_week(time) + 1) % 7 757 return GregorianTime(time.julian_day - days_to_subtract, 0)
758
759 - def increment(self, time):
760 return time + GregorianDelta.from_days(7)
761
762 763 -class StripWeekday(Strip):
764
765 - def label(self, time, major=False):
766 day_of_week = GregorianTimeType().get_day_of_week(time) 767 if major: 768 time = GregorianDateTime.from_time(time) 769 return "%s %s %s %s" % (abbreviated_name_of_weekday(day_of_week), 770 time.day, 771 abbreviated_name_of_month(time.month), 772 format_year(time.year)) 773 return (abbreviated_name_of_weekday(day_of_week) + 774 " %s" % GregorianDateTime.from_time(time).day)
775
776 - def start(self, time):
777 gregorian_time = GregorianDateTime.from_time(time) 778 new_gregorian = GregorianDateTime.from_ymd(gregorian_time.year, gregorian_time.month, gregorian_time.day) 779 return new_gregorian.to_time()
780
781 - def increment(self, time):
782 return time + GregorianDelta.from_days(1)
783
784 - def is_day(self):
785 return True
786
787 788 -class StripHour(Strip):
789
790 - def label(self, time, major=False):
791 time = GregorianDateTime.from_time(time) 792 if major: 793 return "%s %s %s: %sh" % (time.day, abbreviated_name_of_month(time.month), 794 format_year(time.year), time.hour) 795 return str(time.hour)
796
797 - def start(self, time):
798 (hours, _, _) = time.get_time_of_day() 799 return GregorianTime(time.julian_day, hours * 60 * 60)
800
801 - def increment(self, time):
802 return time + GregorianDelta.from_seconds(60 * 60)
803
804 805 -class StripMinute(Strip):
806
807 - def label(self, time, major=False):
808 time = GregorianDateTime.from_time(time) 809 if major: 810 return "%s %s %s: %s:%s" % (time.day, abbreviated_name_of_month(time.month), 811 format_year(time.year), time.hour, time.minute) 812 return str(time.minute)
813
814 - def start(self, time):
815 (hours, minutes, _) = time.get_time_of_day() 816 return GregorianTime(time.julian_day, minutes * 60 + hours * 60 * 60)
817
818 - def increment(self, time):
819 return time + GregorianDelta.from_seconds(60)
820
821 822 -def format_year(year):
823 if year <= 0: 824 return "%d %s" % ((1 - year), BC) 825 else: 826 return str(year)
827
828 829 -def move_period_num_days(period, num):
830 delta = GregorianDelta.from_days(1) * num 831 start_time = period.start_time + delta 832 end_time = period.end_time + delta 833 return TimePeriod(start_time, end_time)
834
835 836 -def move_period_num_weeks(period, num):
837 delta = GregorianDelta.from_days(7) * num 838 start_time = period.start_time + delta 839 end_time = period.end_time + delta 840 return TimePeriod(start_time, end_time)
841
842 843 -def move_period_num_months(period, num):
844 def move_time(time): 845 gregorian_time = GregorianDateTime.from_time(time) 846 new_month = gregorian_time.month + num 847 new_year = gregorian_time.year 848 while new_month < 1: 849 new_month += 12 850 new_year -= 1 851 while new_month > 12: 852 new_month -= 12 853 new_year += 1 854 return gregorian_time.replace(year=new_year, month=new_month).to_time()
855 try: 856 return TimePeriod( 857 move_time(period.start_time), 858 move_time(period.end_time) 859 ) 860 except ValueError: 861 return None 862
863 864 -def move_period_num_years(period, num):
865 try: 866 delta = num 867 start_year = GregorianDateTime.from_time(period.start_time).year 868 end_year = GregorianDateTime.from_time(period.end_time).year 869 start_time = GregorianDateTime.from_time(period.start_time).replace(year=start_year + delta) 870 end_time = GregorianDateTime.from_time(period.end_time).replace(year=end_year + delta) 871 return TimePeriod(start_time.to_time(), end_time.to_time()) 872 except ValueError: 873 return None
874
875 876 -def has_nonzero_time(time_period):
877 return (time_period.start_time.seconds != 0 or 878 time_period.end_time.seconds != 0)
879
880 881 -class DurationType(object):
882
883 - def __init__(self, name, single_name, value_fn, remainder_fn):
884 self._name = name 885 self._single_name = single_name 886 self._value_fn = value_fn 887 self._remainder_fn = remainder_fn
888 889 @property
890 - def name(self):
891 return self._name
892 893 @property
894 - def single_name(self):
895 return self._single_name
896 897 @property
898 - def value_fn(self):
899 return self._value_fn
900 901 @property
902 - def remainder_fn(self):
903 return self._remainder_fn
904 905 906 YEARS = DurationType(_('years'), _('year'), 907 lambda ds: ds[0] // 365, 908 lambda ds: (ds[0] % 365, ds[1])) 909 MONTHS = DurationType(_('months'), _('month'), 910 lambda ds: ds[0] // 30, 911 lambda ds: (ds[0] % 30, ds[1])) 912 WEEKS = DurationType(_('weeks'), _('week'), 913 lambda ds: ds[0] // 7, 914 lambda ds: (ds[0] % 7, ds[1])) 915 DAYS = DurationType(_('days'), _('day'), 916 lambda ds: ds[0], 917 lambda ds: (0, ds[1])) 918 HOURS = DurationType(_('hours'), _('hour'), 919 lambda ds: ds[0] * 24 + ds[1] // 3600, 920 lambda ds: (0, ds[1] % 3600)) 921 MINUTES = DurationType(_('minutes'), _('minute'), 922 lambda ds: ds[0] * 1440 + ds[1] // 60, 923 lambda ds: (0, ds[1] % 60)) 924 SECONDS = DurationType(_('seconds'), _('second'), 925 lambda ds: ds[0] * 86400 + ds[1], 926 lambda ds: (0, 0))
927 928 929 -class DurationFormatter(object):
930
931 - def __init__(self, duration):
932 """Duration is a list containing days and seconds.""" 933 self._duration = duration
934
935 - def format(self, duration_parts):
936 """ 937 Return a string describing a time duration. Such a string 938 can look like:: 939 940 2 years 1 month 3 weeks 941 942 The argument duration_parts is a tuple where each element 943 describes a duration type like YEARS, WEEKS etc. 944 """ 945 values = self._calc_duration_values(self._duration, duration_parts) 946 return self._format_parts(zip(values, duration_parts))
947
948 - def _calc_duration_values(self, duration, duration_parts):
949 values = [] 950 for duration_part in duration_parts: 951 value = duration_part.value_fn(duration) 952 duration[0], duration[1] = duration_part.remainder_fn(duration) 953 values.append(value) 954 return values
955
956 - def _format_parts(self, duration_parts):
957 durations = self._remov_zero_value_parts(duration_parts) 958 return " ". join(self._format_durations_parts(durations))
959
960 - def _remov_zero_value_parts(self, duration_parts):
961 return [duration for duration in duration_parts 962 if duration[0] > 0]
963
964 - def _format_durations_parts(self, durations):
965 return [self._format_part(duration_value, duration_type) for 966 duration_value, duration_type in durations]
967
968 - def _format_part(self, value, duration_type):
969 if value == 1: 970 heading = duration_type.single_name 971 else: 972 heading = duration_type.name 973 return '%d %s' % (value, heading)
974