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