Gnumed.timelinelib.calendar.bosparanian.timetype

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

# Source Code for Module Gnumed.timelinelib.calendar.bosparanian.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  import re
     20
     21  from timelinelib.calendar.bosparanian.bosparanian import BosparanianDateTime
     22  from timelinelib.calendar.bosparanian.monthnames import bosp_abbreviated_name_of_month
     23  from timelinelib.calendar.bosparanian.monthnames import bosp_name_of_month
     24  from timelinelib.calendar.bosparanian.time import BosparanianDelta
     25  from timelinelib.calendar.bosparanian.time import BosparanianTime
     26  from timelinelib.calendar.bosparanian.weekdaynames import bosp_abbreviated_name_of_weekday
     27  from timelinelib.calendar.gregorian.timetype import DAYS
     28  from timelinelib.calendar.gregorian.timetype import DurationFormatter
     29  from timelinelib.calendar.gregorian.timetype import HOURS
     30  from timelinelib.calendar.gregorian.timetype import MINUTES
     31  from timelinelib.calendar.gregorian.timetype import SECONDS
     32  from timelinelib.calendar.gregorian.timetype import SECONDS_IN_DAY
     33  from timelinelib.calendar.gregorian.timetype import YEARS
     34  from timelinelib.calendar.timetype import TimeType
     35  from timelinelib.canvas.data import TimeOutOfRangeLeftError
     36  from timelinelib.canvas.data import TimeOutOfRangeRightError
     37  from timelinelib.canvas.data import TimePeriod
     38  from timelinelib.canvas.data import time_period_center
     39  from timelinelib.canvas.drawing.interface import Strip
     40
     41
     42 -class BosparanianTimeType(TimeType):
     43
     44 -    def __init__(self):
     45          self.major_strip_is_decade = False
     46          self.saved_now = None
     47
     48 -    def __eq__(self, other):
     49          return isinstance(other, BosparanianTimeType)
     50
     51 -    def __ne__(self, other):
     52          return not (self == other)
     53
     54 -    def time_string(self, time):
     55          return "%d-%02d-%02d %02d:%02d:%02d" % BosparanianDateTime.from_time(time).to_tuple()
     56
     57 -    def parse_time(self, time_string):
     58          match = re.search(r"^(-?\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)$", time_string)
     59          if match:
     60              year = int(match.group(1))
     61              month = int(match.group(2))
     62              day = int(match.group(3))
     63              hour = int(match.group(4))
     64              minute = int(match.group(5))
     65              second = int(match.group(6))
     66              try:
     67                  return BosparanianDateTime(year, month, day, hour, minute, second).to_time()
     68              except ValueError:
     69                  raise ValueError("Invalid time, time string = '%s'" % time_string)
     70          else:
     71              raise ValueError("Time not on correct format = '%s'" % time_string)
     72
     96
     97 -    def format_period(self, time_period):
     98          """Returns a unicode string describing the time period."""
     99          def label_with_time(time):
    100              return "%s %s" % (label_without_time(time), time_label(time))
    101
    102          def label_without_time(time):
    103              bosparanian_datetime = BosparanianDateTime.from_time(time)
    104              return "%s %s %s" % (bosparanian_datetime.day, bosp_abbreviated_name_of_month(bosparanian_datetime.month), bosparanian_datetime.year)
    105
    106          def time_label(time):
    107              return "%02d:%02d" % time.get_time_of_day()[:-1]
    108          if time_period.is_period():
    109              if has_nonzero_time(time_period):
    110                  label = "%s to %s" % (label_with_time(time_period.start_time),
    111                                        label_with_time(time_period.end_time))
    112              else:
    113                  label = "%s to %s" % (label_without_time(time_period.start_time),
    114                                        label_without_time(time_period.end_time))
    115          else:
    116              if has_nonzero_time(time_period):
    117                  label = "%s" % label_with_time(time_period.start_time)
    118              else:
    119                  label = "%s" % label_without_time(time_period.start_time)
    120          return label
    121
    122 -    def format_delta(self, delta):
    123          days = abs(delta.get_days())
    124          seconds = abs(delta.seconds) - days * SECONDS_IN_DAY
    125          delta_format = (YEARS, DAYS, HOURS, MINUTES, SECONDS)
    126          return DurationFormatter([days, seconds]).format(delta_format)
    127
    128 -    def get_min_time(self):
    129          return BosparanianTime.min()
    130
    131 -    def get_max_time(self):
    132          return BosparanianTime(5369833, 0)
    133
    134 -    def choose_strip(self, metrics, appearance):
    135          """
    136          Return a tuple (major_strip, minor_strip) for current time period and
    137          window size.
    138          """
    139          day_period = TimePeriod(BosparanianTime(0, 0), BosparanianTime(1, 0))
    140          one_day_width = metrics.calc_exact_width(day_period)
    141          self.major_strip_is_decade = False
    142          if one_day_width > 20000:
    143              return (StripHour(), StripMinute())
    144          elif one_day_width > 600:
    145              return (StripDay(), StripHour())
    146          elif one_day_width > 60:
    147              return (StripMonth(), StripWeekday())
    148          elif one_day_width > 25:
    149              return (StripMonth(), StripDay())
    150          elif one_day_width > 10:
    151              return (StripMonth(), StripWeek())
    152          elif one_day_width > 1.75:
    153              return (StripYear(), StripMonth())
    154          elif one_day_width > 0.5:
    155              return (StripYear(), StripQuarter())
    156          elif one_day_width > 0.12:
    157              self.major_strip_is_decade = True
    158              return (StripDecade(), StripYear())
    159          elif one_day_width > 0.012:
    160              return (StripCentury(), StripDecade())
    161          else:
    162              return (StripCentury(), StripCentury())
    163
    164 -    def get_default_time_period(self):
    165          return time_period_center(self.now(), BosparanianDelta.from_days(30))
    166
    167 -    def supports_saved_now(self):
    168          return True
    169
    170 -    def set_saved_now(self, time):
    171          self.saved_now = time
    172
    173 -    def now(self):
    174          if self.saved_now is None:
    175              return BosparanianDateTime(1000, 1, 1, 12, 0, 0).to_time()
    176          return self.saved_now
    177
    178 -    def get_min_zoom_delta(self):
    179          return (BosparanianDelta.from_seconds(60), _("Can't zoom deeper than 1 minute"))
    180
    181 -    def get_name(self):
    182          return "bosparaniantime"
    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 self.get_day_of_week(time) == 3
    194
    195 -    def is_weekend_day(self, time):
    196          return self.get_day_of_week(time) in (0, 3)
    197
    198 -    def get_day_of_week(self, time):
    199          return time.julian_day % 7
    200
    201 -    def create_time_picker(self, parent, *args, **kwargs):
    202          from timelinelib.calendar.bosparanian.timepicker import BosparanianDateTimePicker
    203          return BosparanianDateTimePicker(parent, *args, **kwargs)
    204
    205 -    def create_period_picker(self, parent, *args, **kwargs):
    206          from timelinelib.calendar.bosparanian.periodpicker import BosparanianPeriodPicker
    207          return BosparanianPeriodPicker(parent, *args, **kwargs)
    208
    209
    210 -def open_now_date_editor(main_frame, current_period, navigation_fn):
    211      def navigate_to(time):
    212          navigation_fn(lambda tp: tp.center(time))
    213      main_frame.display_now_date_editor_dialog(navigate_to, _("Change Now Date"))
    214
    215
    216 -def go_to_date_fn(main_frame, current_period, navigation_fn):
    217      def navigate_to(time):
    218          navigation_fn(lambda tp: tp.center(time))
    219      main_frame.display_time_editor_dialog(
    220          BosparanianTimeType(), current_period.mean_time(), navigate_to, _("Go to Date"))
    221
    222
    223 -def backward_fn(main_frame, current_period, navigation_fn):
    224      _move_page_smart(current_period, navigation_fn, -1)
    225
    226
    227 -def forward_fn(main_frame, current_period, navigation_fn):
    228      _move_page_smart(current_period, navigation_fn, 1)
    229
    230
    231 -def _move_page_smart(current_period, navigation_fn, direction):
    232      if _whole_number_of_years(current_period):
    233          _move_page_years(current_period, navigation_fn, direction)
    234      elif _whole_number_of_months(current_period):
    235          _move_page_months(current_period, navigation_fn, direction)
    236      else:
    237          navigation_fn(lambda tp: tp.move_delta(direction * current_period.delta()))
    238
    239
    240 -def _whole_number_of_years(period):
    241      return (BosparanianDateTime.from_time(period.start_time).is_first_day_in_year() and
    242              BosparanianDateTime.from_time(period.end_time).is_first_day_in_year() and
    243              _calculate_year_diff(period) > 0)
    244
    245
    246 -def _move_page_years(curret_period, navigation_fn, direction):
    247      def navigate(tp):
    248          year_delta = direction * _calculate_year_diff(curret_period)
    249          bosparanian_start = BosparanianDateTime.from_time(curret_period.start_time)
    250          bosparanian_end = BosparanianDateTime.from_time(curret_period.end_time)
    251          new_start_year = bosparanian_start.year + year_delta
    252          new_end_year = bosparanian_end.year + year_delta
    253          try:
    254              new_start = bosparanian_start.replace(year=new_start_year).to_time()
    255              new_end = bosparanian_end.replace(year=new_end_year).to_time()
    256              if new_end > BosparanianTimeType().get_max_time():
    257                  raise ValueError()
    258              if new_start < BosparanianTimeType().get_min_time():
    259                  raise ValueError()
    260          except ValueError:
    261              if direction < 0:
    262                  raise TimeOutOfRangeLeftError()
    263              else:
    264                  raise TimeOutOfRangeRightError()
    265          return tp.update(new_start, new_end)
    266      navigation_fn(navigate)
    267
    268
    269 -def _calculate_year_diff(period):
    270      return (BosparanianDateTime.from_time(period.end_time).year -
    271              BosparanianDateTime.from_time(period.start_time).year)
    272
    273
    274 -def _whole_number_of_months(period):
    275      start, end = BosparanianDateTime.from_time(period.start_time), BosparanianDateTime.from_time(period.end_time)
    276      start_months = start.year * 13 + start.month
    277      end_months = end.year * 13 + end.month
    278      month_diff = end_months - start_months
    279      return (start.is_first_of_month() and
    280              end.is_first_of_month() and
    281              month_diff > 0)
    282
    283
    284 -def _move_page_months(curret_period, navigation_fn, direction):
    285      def navigate(tp):
    286          start = BosparanianDateTime.from_time(curret_period.start_time)
    287          end = BosparanianDateTime.from_time(curret_period.end_time)
    288          start_months = start.year * 13 + start.month
    289          end_months = end.year * 13 + end.month
    290          month_diff = end_months - start_months
    291          month_delta = month_diff * direction
    292          new_start_year, new_start_month = _months_to_year_and_month(start_months + month_delta)
    293          new_end_year, new_end_month = _months_to_year_and_month(end_months + month_delta)
    294          try:
    295              new_start = start.replace(year=new_start_year, month=new_start_month)
    296              new_end = end.replace(year=new_end_year, month=new_end_month)
    297              start = new_start.to_time()
    298              end = new_end.to_time()
    299              if end > BosparanianTimeType().get_max_time():
    300                  raise ValueError()
    301              if start < BosparanianTimeType().get_min_time():
    302                  raise ValueError()
    303          except ValueError:
    304              if direction < 0:
    305                  raise TimeOutOfRangeLeftError()
    306              else:
    307                  raise TimeOutOfRangeRightError()
    308          return tp.update(start, end)
    309      navigation_fn(navigate)
    310
    311
    312 -def _months_to_year_and_month(months):
    313      years = int(months // 13)
    314      month = months - years * 13
    315      if month == 0:
    316          month = 13
    317          years -= 1
    318      return years, month
    319
    320
    321 -def forward_one_week_fn(main_frame, current_period, navigation_fn):
    322      wk = BosparanianDelta.from_days(7)
    323      navigation_fn(lambda tp: tp.move_delta(wk))
    324
    325
    326 -def backward_one_week_fn(main_frame, current_period, navigation_fn):
    327      wk = BosparanianDelta.from_days(7)
    328      navigation_fn(lambda tp: tp.move_delta(-1 * wk))
    329
    330
    331 -def navigate_month_step(current_period, navigation_fn, direction):
    332      tm = current_period.mean_time()
    333      gt = BosparanianDateTime.from_time(tm)
    334      mv = BosparanianDelta.from_days(gt.days_in_month())
    335      navigation_fn(lambda tp: tp.move_delta(direction * mv))
    336
    337
    338 -def forward_one_month_fn(main_frame, current_period, navigation_fn):
    339      navigate_month_step(current_period, navigation_fn, 1)
    340
    341
    342 -def backward_one_month_fn(main_frame, current_period, navigation_fn):
    343      navigate_month_step(current_period, navigation_fn, -1)
    344
    345
    346 -def forward_one_year_fn(main_frame, current_period, navigation_fn):
    347      yr = BosparanianDelta.from_days(365)
    348      navigation_fn(lambda tp: tp.move_delta(yr))
    349
    350
    351 -def backward_one_year_fn(main_frame, current_period, navigation_fn):
    352      yr = BosparanianDelta.from_days(365)
    353      navigation_fn(lambda tp: tp.move_delta(-1 * yr))
    354
    355
    356 -def fit_millennium_fn(main_frame, current_period, navigation_fn):
    357      mean = BosparanianDateTime.from_time(current_period.mean_time())
    358      if mean.year > get_millenium_max_year():
    359          year = get_millenium_max_year()
    360      else:
    361          year = max(get_min_year_containing_praios_1(), int(mean.year // 1000) * 1000)
    362      start = BosparanianDateTime.from_ymd(year, 1, 1).to_time()
    363      end = BosparanianDateTime.from_ymd(year + 1000, 1, 1).to_time()
    364      navigation_fn(lambda tp: tp.update(start, end))
    365
    366
    367 -def get_min_year_containing_praios_1():
    368      return BosparanianDateTime.from_time(BosparanianTimeType().get_min_time()).year + 1
    369
    370
    371 -def get_millenium_max_year():
    372      return BosparanianDateTime.from_time(BosparanianTimeType().get_max_time()).year - 1000
    373
    374
    375 -def get_century_max_year():
    376      return BosparanianDateTime.from_time(BosparanianTimeType().get_max_time()).year - 100
    377
    378
    379 -def fit_century_fn(main_frame, current_period, navigation_fn):
    380      mean = BosparanianDateTime.from_time(current_period.mean_time())
    381      if mean.year > get_century_max_year():
    382          year = get_century_max_year()
    383      else:
    384          year = max(get_min_year_containing_praios_1(), int(mean.year // 100) * 100)
    385      start = BosparanianDateTime.from_ymd(year, 1, 1).to_time()
    386      end = BosparanianDateTime.from_ymd(year + 100, 1, 1).to_time()
    387      navigation_fn(lambda tp: tp.update(start, end))
    388
    389
    390 -def fit_decade_fn(main_frame, current_period, navigation_fn):
    391      mean = BosparanianDateTime.from_time(current_period.mean_time())
    392      start = BosparanianDateTime.from_ymd(int(mean.year // 10) * 10, 1, 1).to_time()
    393      end = BosparanianDateTime.from_ymd(int(mean.year // 10) * 10 + 10, 1, 1).to_time()
    394      navigation_fn(lambda tp: tp.update(start, end))
    395
    396
    397 -def fit_year_fn(main_frame, current_period, navigation_fn):
    398      mean = BosparanianDateTime.from_time(current_period.mean_time())
    399      start = BosparanianDateTime.from_ymd(mean.year, 1, 1).to_time()
    400      end = BosparanianDateTime.from_ymd(mean.year + 1, 1, 1).to_time()
    401      navigation_fn(lambda tp: tp.update(start, end))
    402
    403
    404 -def fit_month_fn(main_frame, current_period, navigation_fn):
    405      mean = BosparanianDateTime.from_time(current_period.mean_time())
    406      start = BosparanianDateTime.from_ymd(mean.year, mean.month, 1).to_time()
    407      if mean.month == 13:
    408          end = BosparanianDateTime.from_ymd(mean.year + 1, 1, 1).to_time()
    409      else:
    410          end = BosparanianDateTime.from_ymd(mean.year, mean.month + 1, 1).to_time()
    411      navigation_fn(lambda tp: tp.update(start, end))
    412
    413
    414 -def fit_day_fn(main_frame, current_period, navigation_fn):
    415      mean = BosparanianDateTime.from_time(current_period.mean_time())
    416      start = BosparanianDateTime.from_ymd(mean.year, mean.month, mean.day).to_time()
    417      end = start + BosparanianDelta.from_days(1)
    418      navigation_fn(lambda tp: tp.update(start, end))
    419
    420
    421 -def fit_week_fn(main_frame, current_period, navigation_fn):
    422      mean = BosparanianDateTime.from_time(current_period.mean_time())
    423      start = BosparanianDateTime.from_ymd(mean.year, mean.month, mean.day).to_time()
    424      weekday = BosparanianTimeType().get_day_of_week(start)
    425      start = start - BosparanianDelta.from_days(weekday)
    426      if not main_frame.week_starts_on_monday():
    427          start = start - BosparanianDelta.from_days(1)
    428      end = start + BosparanianDelta.from_days(7)
    429      navigation_fn(lambda tp: tp.update(start, end))
    430
    431
    432 -class StripCentury(Strip):
    433
    434 -    def label(self, time, major=False):
    435          if major:
    436              # TODO: This only works for English. Possible to localize?
    437              time = BosparanianDateTime.from_time(time)
    438              start_year = self._century_start_year(time.year)
    439              century = int((start_year + 100) // 100)
    440              if century <= 0:
    441                  century -= 1
    442              return str(century) + " century BF"
    443          return ""
    444
    445 -    def start(self, time):
    446          time = BosparanianDateTime.from_time(time)
    447          return BosparanianDateTime.from_ymd(self._century_start_year(time.year), 1, 1).to_time()
    448
    449 -    def increment(self, time):
    450          time = BosparanianDateTime.from_time(time)
    451          return time.replace(year=time.year + 100).to_time()
    452
    453 -    def _century_start_year(self, year):
    454          year = int(year // 100) * 100
    455          return year
    456
    457
    458 -class StripDecade(Strip):
    459
    460 -    def label(self, time, major=False):
    461          time = BosparanianDateTime.from_time(time)
    462          return format_decade(self._decade_start_year(time.year))
    463
    464 -    def start(self, time):
    465          bosparanian_time = BosparanianDateTime.from_time(time)
    466          new_bosparanian = BosparanianDateTime.from_ymd(self._decade_start_year(bosparanian_time.year), 1, 1)
    467          return new_bosparanian.to_time()
    468
    469 -    def increment(self, time):
    470          bosparanian_time = BosparanianDateTime.from_time(time)
    471          return bosparanian_time.replace(year=bosparanian_time.year + 10).to_time()
    472
    473 -    def _decade_start_year(self, year):
    474          # The first start year must be to the left of the first visible
    475          # year on the timeline in order to draw the first vertical decade
    476          # line correctly. Therefore -10 in the calculation below
    477          return int(year // 10) * 10 - 10
    478
    479
    480 -class StripYear(Strip):
    481
    482 -    def label(self, time, major=False):
    483          return format_year(BosparanianDateTime.from_time(time).year)
    484
    485 -    def start(self, time):
    486          bosparanian_time = BosparanianDateTime.from_time(time)
    487          new_bosparanian = BosparanianDateTime.from_ymd(bosparanian_time.year, 1, 1)
    488          return new_bosparanian.to_time()
    489
    490 -    def increment(self, time):
    491          bosparanian_time = BosparanianDateTime.from_time(time)
    492          return bosparanian_time.replace(year=bosparanian_time.year + 1).to_time()
    493
    494
    495 -class StripMonth(Strip):
    496
    497 -    def label(self, time, major=False):
    498          time = BosparanianDateTime.from_time(time)
    499          if major:
    500              return "%s %s" % (bosp_name_of_month(time.month),
    501                                format_year(time.year))
    502          if time.month == 13:
    503              return bosp_abbreviated_name_of_month(time.month)
    504          return bosp_name_of_month(time.month)
    505
    506 -    def start(self, time):
    507          bosparanian_time = BosparanianDateTime.from_time(time)
    508          new_bosparanian = BosparanianDateTime.from_ymd(bosparanian_time.year, bosparanian_time.month, 1)
    509          return new_bosparanian.to_time()
    510
    511 -    def increment(self, time):
    512          days_in_month = BosparanianDateTime.from_time(time).days_in_month()
    513          return time + BosparanianDelta.from_days(days_in_month)
    514
    515
    516 -class StripQuarter(Strip):
    517
    518 -    def get_quarter(self, time):
    519          m = BosparanianDateTime.from_time(time).month
    520          if m == 13:
    521              return 0
    522          return (m - 1) // 3 + 1
    523
    524 -    def label(self, time, major=False):
    525          q = self.get_quarter(time)
    526          if q == 0:
    527              return "NLD"
    528          return "Q%d" % q
    529
    530 -    def start(self, time):
    531          q = self.get_quarter(time)
    532          if q == 0:
    533              m = 13
    534          else:
    535              m = (q - 1) * 3 + 1
    536          return BosparanianDateTime.from_ymd(BosparanianDateTime.from_time(time).year, m, 1).to_time()
    537
    538 -    def increment(self, time):
    539          q = self.get_quarter(time)
    540          if q == 0:
    541              days_in_quarter = 5
    542          else:
    543              days_in_quarter = 30 * 3
    544          return time + BosparanianDelta.from_days(days_in_quarter)
    545
    546
    547 -class StripDay(Strip):
    548
    549 -    def label(self, time, major=False):
    550          time = BosparanianDateTime.from_time(time)
    551          if major:
    552              return "%s %s %s" % (time.day,
    553                                   bosp_abbreviated_name_of_month(time.month),
    554                                   format_year(time.year))
    555          return str(time.day)
    556
    557 -    def start(self, time):
    558          bosparanian_time = BosparanianDateTime.from_time(time)
    559          new_bosparanian = BosparanianDateTime.from_ymd(bosparanian_time.year, bosparanian_time.month, bosparanian_time.day)
    560          return new_bosparanian.to_time()
    561
    562 -    def increment(self, time):
    563          return time + BosparanianDelta.from_days(1)
    564
    565 -    def is_day(self):
    566          return True
    567
    568
    569 -class StripWeek(Strip):
    570
    571 -    def __init__(self):
    572          Strip.__init__(self)
    573
    574 -    def label(self, time, major=False):
    575          if major:
    576              first_weekday = self.start(time)
    577              next_first_weekday = self.increment(first_weekday)
    578              last_weekday = next_first_weekday - BosparanianDelta.from_days(1)
    579              range_string = self._time_range_string(first_weekday, last_weekday)
    580              return (_("Week") + " %s (%s)") % (BosparanianDateTime.from_time(time).week_number, range_string)
    581          return _("Week") + " %s" % BosparanianDateTime.from_time(time).week_number
    582
    583 -    def _time_range_string(self, start, end):
    584          start = BosparanianDateTime.from_time(start)
    585          end = BosparanianDateTime.from_time(end)
    586          if start.year == end.year:
    587              if start.month == end.month:
    588                  return "%s-%s %s %s" % (start.day, end.day,
    589                                          bosp_abbreviated_name_of_month(start.month),
    590                                          format_year(start.year))
    591              return "%s %s-%s %s %s" % (start.day,
    592                                         bosp_abbreviated_name_of_month(start.month),
    593                                         end.day,
    594                                         bosp_abbreviated_name_of_month(end.month),
    595                                         format_year(start.year))
    596          return "%s %s %s-%s %s %s" % (start.day,
    597                                        bosp_abbreviated_name_of_month(start.month),
    598                                        format_year(start.year),
    599                                        end.day,
    600                                        bosp_abbreviated_name_of_month(end.month),
    601                                        format_year(end.year))
    602
    603 -    def start(self, time):
    604          days_to_subtract = BosparanianTimeType().get_day_of_week(time)
    605          return BosparanianTime(time.julian_day - days_to_subtract, 0)
    606
    607 -    def increment(self, time):
    608          return time + BosparanianDelta.from_days(7)
    609
    610
    611 -class StripWeekday(Strip):
    612
    613 -    def label(self, time, major=False):
    614          day_of_week = BosparanianTimeType().get_day_of_week(time)
    615          if major:
    616              time = BosparanianDateTime.from_time(time)
    617              return "%s %s %s %s" % (bosp_abbreviated_name_of_weekday(day_of_week),
    618                                      time.day,
    619                                      bosp_abbreviated_name_of_month(time.month),
    620                                      format_year(time.year))
    621          return (bosp_abbreviated_name_of_weekday(day_of_week) +
    622                  " %s" % BosparanianDateTime.from_time(time).day)
    623
    624 -    def start(self, time):
    625          bosparanian_time = BosparanianDateTime.from_time(time)
    626          new_bosparanian = BosparanianDateTime.from_ymd(bosparanian_time.year, bosparanian_time.month, bosparanian_time.day)
    627          return new_bosparanian.to_time()
    628
    629 -    def increment(self, time):
    630          return time + BosparanianDelta.from_days(1)
    631
    632 -    def is_day(self):
    633          return True
    634
    635
    636 -class StripHour(Strip):
    637
    638 -    def label(self, time, major=False):
    639          time = BosparanianDateTime.from_time(time)
    640          if major:
    641              return "%s %s %s: %sh" % (time.day, bosp_abbreviated_name_of_month(time.month),
    642                                        format_year(time.year), time.hour)
    643          return str(time.hour)
    644
    645 -    def start(self, time):
    646          (hours, _, _) = time.get_time_of_day()
    647          return BosparanianTime(time.julian_day, hours * 60 * 60)
    648
    649 -    def increment(self, time):
    650          return time + BosparanianDelta.from_seconds(60 * 60)
    651
    652
    653 -class StripMinute(Strip):
    654
    655 -    def label(self, time, major=False):
    656          time = BosparanianDateTime.from_time(time)
    657          if major:
    658              return "%s %s %s: %s:%s" % (time.day, bosp_abbreviated_name_of_month(time.month),
    659                                          format_year(time.year), time.hour, time.minute)
    660          return str(time.minute)
    661
    662 -    def start(self, time):
    663          (hours, minutes, _) = time.get_time_of_day()
    664          return BosparanianTime(time.julian_day, minutes * 60 + hours * 60 * 60)
    665
    666 -    def increment(self, time):
    667          return time + BosparanianDelta.from_seconds(60)
    668
    669
    670 -def format_year(year):
    671      return "%dBF" % year
    672
    673
    674 -def format_decade(start_year):
    675      return str(start_year + 10) + "s"
    676
    677
    678 -def move_period_num_days(period, num):
    679      delta = BosparanianDelta.from_days(1) * num
    680      start_time = period.start_time + delta
    681      end_time = period.end_time + delta
    682      return TimePeriod(start_time, end_time)
    683
    684
    685 -def move_period_num_weeks(period, num):
    686      delta = BosparanianDelta.from_days(7) * num
    687      start_time = period.start_time + delta
    688      end_time = period.end_time + delta
    689      return TimePeriod(start_time, end_time)
    690
    691
    692 -def move_period_num_months(period, num):
    693      try:
    694          delta = num
    695          years = int(abs(delta)) // 13
    696          bosparanian_start = BosparanianDateTime.from_time(period.start_time)
    697          bosparanian_end = BosparanianDateTime.from_time(period.end_time)
    698          if num < 0:
    699              years = -years
    700          delta = delta - 13 * years
    701          if delta < 0:
    702              start_month = bosparanian_start.month + 13 + delta
    703              end_month = bosparanian_end.month + 13 + delta
    704              if start_month > 13:
    705                  start_month -= 13
    706                  end_month -= 13
    707              if start_month > bosparanian_start.month:
    708                  years -= 1
    709          else:
    710              start_month = bosparanian_start.month + delta
    711              end_month = bosparanian_start.month + delta
    712              if start_month > 13:
    713                  start_month -= 13
    714                  end_month -= 13
    715                  years += 1
    716          start_year = bosparanian_start.year + years
    717          end_year = bosparanian_start.year + years
    718          start_time = bosparanian_start.replace(year=start_year, month=start_month)
    719          end_time = bosparanian_end.replace(year=end_year, month=end_month)
    720          return TimePeriod(start_time.to_time(), end_time.to_time())
    721      except ValueError:
    722          return None
    723
    724
    725 -def move_period_num_years(period, num):
    726      try:
    727          delta = num
    728          start_year = BosparanianDateTime.from_time(period.start_time).year
    729          end_year = BosparanianDateTime.from_time(period.end_time).year
    730          start_time = BosparanianDateTime.from_time(period.start_time).replace(year=start_year + delta)
    731          end_time = BosparanianDateTime.from_time(period.end_time).replace(year=end_year + delta)
    732          return TimePeriod(start_time.to_time(), end_time.to_time())
    733      except ValueError:
    734          return None
    735
    736
    737 -def has_nonzero_time(time_period):
    738      return (time_period.start_time.seconds != 0 or
    739              time_period.end_time.seconds != 0)
    740