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

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