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

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