Gnumed.timelinelib.calendar.gregorian.gregorian

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

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

      1  # -*- coding: utf-8 -*-
      2  # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018  Rickard Lindberg, Roger Lindberg
      3  #
      4  # This file is part of Timeline.
      5  #
      6  # Timeline is free software: you can redistribute it and/or modify
      7  # it under the terms of the GNU General Public License as published by
      8  # the Free Software Foundation, either version 3 of the License, or
      9  # (at your option) any later version.
     10  #
     11  # Timeline is distributed in the hope that it will be useful,
     12  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  # GNU General Public License for more details.
     15  #
     16  # You should have received a copy of the GNU General Public License
     17  # along with Timeline.  If not, see <http://www.gnu.org/licenses/>.
     18
     19  """
     20  Contains the GregorianDateTime class and static functions related
     21  to the class.
     22
     23  :doc:`Tests are found here <unit_calendar_gregorian_gregorian>`.
     24  """
     25
     26  from timelinelib.calendar.gregorian.time import GregorianDelta
     27  from timelinelib.calendar.gregorian.time import GregorianTime
     28
     29
     30 -class GregorianDateTime(object):
     31      """ """
     32
     33 -    def __init__(self, year, month, day, hour, minute, second):
     34          """ """
     35          if not is_valid(year, month, day):
     36              raise ValueError("Invalid gregorian date %s-%s-%s" % (year, month, day))
     37          self.year = year
     38          self.month = month
     39          self.day = day
     40          self.hour = hour
     41          self.minute = minute
     42          self.second = second
     43
     44 -    def __eq__(self, other):
     45          """ """
     46          return (isinstance(other, self.__class__) and
     47                  self.to_tuple() == other.to_tuple())
     48
     49 -    def __ne__(self, other):
     50          """ """
     51          return not (self == other)
     52
     53      @classmethod
     54 -    def from_ymd(cls, year, month, day):
     55          """ """
     56          return cls(year, month, day, 0, 0, 0)
     57
     58      @classmethod
     59 -    def from_time(cls, time):
     60          """ """
     61          (year, month, day) = julian_day_to_gregorian_ymd(time.julian_day)
     62          (hour, minute, second) = time.get_time_of_day()
     63          return cls(year, month, day, hour, minute, second)
     64
     65      @property
     66 -    def week_number(self):
     67          """ """
     68
     69          def monday_week_1(year):
     70              from timelinelib.calendar.gregorian.timetype import GregorianTimeType
     71              jan_4 = GregorianDateTime.from_ymd(year, 1, 4).to_time()
     72              jan_4_day_of_week = GregorianTimeType().get_day_of_week(jan_4)
     73              return jan_4 - GregorianDelta.from_days(jan_4_day_of_week)
     74
     75          def days_between(end, start):
     76              return end.julian_day - start.julian_day
     77
     78          def days_since_monday_week_1(time):
     79              year = GregorianDateTime.from_time(time).year
     80              diff = days_between(end=time, start=monday_week_1(year + 1))
     81              if diff >= 0:
     82                  return diff
     83              diff = days_between(end=time, start=monday_week_1(year))
     84              if diff >= 0:
     85                  return diff
     86              diff = days_between(end=time, start=monday_week_1(year - 1))
     87              if diff >= 0:
     88                  return diff
     89              raise ValueError("should not end up here")
     90          return days_since_monday_week_1(self.to_time()) // 7 + 1
     91
     92 -    def is_bc(self):
     93          """ """
     94          return self.year <= 0
     95
     96 -    def replace(self, year=None, month=None):
     97          """ """
     98          if year is None:
     99              year = self.year
    100          if month is None:
    101              month = self.month
    102          return self.__class__(
    103              year,
    104              month,
    105              self.day,
    106              self.hour,
    107              self.minute,
    108              self.second
    109          )
    110
    111 -    def days_in_month(self):
    112          """ """
    113          return days_in_month(self.year, self.month)
    114
    115 -    def to_tuple(self):
    116          """ """
    117          return (self.year, self.month, self.day, self.hour, self.minute,
    118                  self.second)
    119
    120 -    def to_date_tuple(self):
    121          """ """
    122          return (self.year, self.month, self.day)
    123
    124 -    def to_time_tuple(self):
    125          """ """
    126          return (self.hour, self.minute, self.second)
    127
    128 -    def to_time(self):
    129          """ """
    130          days = gregorian_ymd_to_julian_day(self.year, self.month, self.day)
    131          seconds = self.hour * 60 * 60 + self.minute * 60 + self.second
    132          return GregorianTime(days, seconds)
    133
    134 -    def is_first_day_in_year(self):
    135          """ """
    136          return (self.month == 1 and
    137                  self.day == 1 and
    138                  self.hour == 0 and
    139                  self.minute == 0 and
    140                  self.second == 0)
    141
    142 -    def is_first_of_month(self):
    143          """ """
    144          return (self.day == 1 and
    145                  self.hour == 0 and
    146                  self.minute == 0 and
    147                  self.second == 0)
    148
    149 -    def __repr__(self):
    150          """ """
    151          return "GregorianDateTime<%d-%02d-%02d %02d:%02d:%02d>" % self.to_tuple()
    152
    153
    154 -def days_in_month(year, month):
    155      """ """
    156      if month in [4, 6, 9, 11]:
    157          return 30
    158      if month in [1, 3, 5, 7, 8, 10, 12]:
    159          return 31
    160      if is_leap_year(year):
    161          return 29
    162      return 28
    163
    164
    165 -def is_leap_year(year):
    166      """ """
    167      return year % 4 == 0 and (year % 400 == 0 or not year % 100 == 0)
    168
    169
    170 -def is_valid_time(hour, minute, second):
    171      """ """
    172      return (
    173          hour >= 0 and hour < 24 and
    174          minute >= 0 and minute < 60 and
    175          second >= 0 and second < 60
    176      )
    177
    178
    179 -def is_valid(year, month, day):
    180      """ """
    181      return (
    182          month >= 1 and month <= 12 and
    183          day >= 1 and day <= days_in_month(year, month)
    184      )
    185
    186
    187 -def julian_day_to_gregorian_ymd(julian_day):
    188      """
    189      This algorithm is described here:
    190
    191      * http://www.tondering.dk/claus/cal/julperiod.php#formula
    192
    193      Integer division works differently in C and in Python for negative numbers.
    194      C truncates towards 0 and Python truncates towards negative infinity:
    195      http://python-history.blogspot.se/2010/08/why-pythons-integer-division-floors.html
    196
    197      The above source don't state which to be used. If we can prove that
    198      division-expressions are always positive, we can be sure this algorithm
    199      works the same in C and in Python.
    200
    201      We must prove that:
    202
    203      1) m             >= 0
    204      2) ((5 * e) + 2) >= 0  =>  e >= 0
    205      3) (1461 * d)    >= 0  =>  d >= 0
    206      4) ((4 * c) + 3) >= 0  =>  c >= 0
    207      5) (b * 146097)  >= 0  =>  b >= 0
    208      6) ((4 * a) + 3) >= 0  =>  a >= 0
    209
    210      Let's work from the top:
    211
    212      julian_day >= 0                   =>
    213
    214      a >= 0 + 32044
    215         = 32044                        =>
    216
    217      This proves 6).
    218
    219      b >= ((4 * 32044) + 3) // 146097
    220         = 0
    221
    222      This proves 5).
    223
    224      Let's look at c:
    225
    226      c = a - ((b * 146097) // 4)
    227        = a - (((((4 * a) + 3) // 146097) * 146097) // 4)
    228
    229      For c to be >= 0, then
    230
    231      (((((4 * a) + 3) // 146097) * 146097) // 4) <= a
    232
    233      Let's look at this component: ((((4 * a) + 3) // 146097) * 146097)
    234
    235      This expression can never be larger than (4 * a) + 3. That gives this:
    236
    237      ((4 * a) + 3) // 4 <= a, which holds.
    238
    239      This proves 4).
    240
    241      Now, let's look at d:
    242
    243      d = ((4 * c) + 3) // 1461
    244
    245      If c is >= 0, then d is also >= 0.
    246
    247      This proves 3).
    248
    249      Let's look at e:
    250
    251      e = c - ((1461 * d) // 4)
    252        = c - ((1461 * (((4 * c) + 3) // 1461)) // 4)
    253
    254      The same resoning as above can be used to conclude that e >= 0.
    255
    256      This proves 2).
    257
    258      Now, let's look at m:
    259
    260      m = ((5 * e) + 2) // 153
    261
    262      If e >= 0, then m is also >= 0.
    263
    264      This proves 1).
    265      """
    266      if julian_day < GregorianTime.MIN_JULIAN_DAY:
    267          raise ValueError("julian_day_to_gregorian_ymd only works for julian days >= %d, but was %d" % (GregorianTime.MIN_JULIAN_DAY, julian_day))
    268      a = julian_day + 32044
    269      b = ((4 * a) + 3) // 146097
    270      c = a - ((b * 146097) // 4)
    271      d = ((4 * c) + 3) // 1461
    272      e = c - ((1461 * d) // 4)
    273      m = ((5 * e) + 2) // 153
    274      day = e - (((153 * m) + 2) // 5) + 1
    275      month = m + 3 - (12 * (m // 10))
    276      year = (b * 100) + d - 4800 + (m // 10)
    277      return (year, month, day)
    278
    279
    280 -def gregorian_ymd_to_julian_day(year, month, day):
    281      """
    282      This algorithm is described here:
    283
    284      * http://www.tondering.dk/claus/cal/julperiod.php#formula
    285      * http://en.wikipedia.org/wiki/Julian_day#Converting_Julian_or_Gregorian_calendar_date_to_Julian_Day_Number
    286
    287      Integer division works differently in C and in Python for negative numbers.
    288      C truncates towards 0 and Python truncates towards negative infinity:
    289      http://python-history.blogspot.se/2010/08/why-pythons-integer-division-floors.html
    290
    291      The above sources don't state which to be used. If we can prove that
    292      division-expressions are always positive, we can be sure this algorithm
    293      works the same in C and in Python.
    294
    295      We must prove that:
    296
    297      1) y >= 0
    298      2) ((153 * m) + 2) >= 0
    299
    300      Let's prove 1):
    301
    302      y = year + 4800 - a
    303        = year + 4800 - ((14 - month) // 12)
    304
    305      year >= -4713 (gives a julian day of 0)
    306
    307      so
    308
    309      year + 4800 >= -4713 + 4800 = 87
    310
    311      The expression ((14 - month) // 12) varies between 0 and 1 when month
    312      varies between 1 and 12. Therefore y >= 87 - 1 = 86, and 1) is proved.
    313
    314      Let's prove 2):
    315
    316      m = month + (12 * a) - 3
    317        = month + (12 * ((14 - month) // 12)) - 3
    318
    319      1 <= month <= 12
    320
    321      m(1)  = 1  + (12 * ((14 - 1)  // 12)) - 3 = 1  + (12 * 1) - 3 = 10
    322      m(2)  = 2  + (12 * ((14 - 2)  // 12)) - 3 = 2  + (12 * 1) - 3 = 11
    323      m(3)  = 3  + (12 * ((14 - 3)  // 12)) - 3 = 3  + (12 * 0) - 3 = 0
    324      m(4)  = 4  + (12 * ((14 - 4)  // 12)) - 3 = 4  + (12 * 0) - 3 = 1
    325      m(5)  = 5  + (12 * ((14 - 5)  // 12)) - 3 = 5  + (12 * 0) - 3 = 2
    326      m(6)  = 6  + (12 * ((14 - 6)  // 12)) - 3 = 6  + (12 * 0) - 3 = 3
    327      m(7)  = 7  + (12 * ((14 - 7)  // 12)) - 3 = 7  + (12 * 0) - 3 = 4
    328      m(8)  = 8  + (12 * ((14 - 8)  // 12)) - 3 = 8  + (12 * 0) - 3 = 5
    329      m(9)  = 9  + (12 * ((14 - 9)  // 12)) - 3 = 9  + (12 * 0) - 3 = 6
    330      m(10) = 10 + (12 * ((14 - 10) // 12)) - 3 = 10 + (12 * 0) - 3 = 7
    331      m(11) = 11 + (12 * ((14 - 11) // 12)) - 3 = 11 + (12 * 0) - 3 = 8
    332      m(12) = 12 + (12 * ((14 - 12) // 12)) - 3 = 12 + (12 * 0) - 3 = 9
    333
    334      So, m is always > 0. Which also makes the expression ((153 * m) + 2) > 0,
    335      and 2) is proved.
    336      """
    337      a = (14 - month) // 12
    338      y = year + 4800 - a
    339      m = month + (12 * a) - 3
    340      julian_day = (day
    341                    + (((153 * m) + 2) // 5)
    342                    + (y * 365)
    343                    + (y // 4)
    344                    - (y // 100)
    345                    + (y // 400)
    346                    - 32045)
    347      if julian_day < GregorianTime.MIN_JULIAN_DAY:
    348          raise ValueError("gregorian_ymd_to_julian_day only works for julian days >= %d, but was %d" % (GregorianTime.MIN_JULIAN_DAY, julian_day))
    349      return julian_day
    350
    351
    352 -def gregorian_ymd_to_julian_day_alt(year, month, day):
    353      """
    354      Table 15.14 Selected arithmetic calendars, with parameters for algorithms
    355          Calendar a         y   j   m  n  r   p    q  v   u    s  t  w  A  B       C
    356
    357          1 Egyptian      3968   47  0 13  1   365  0  0   1   30  0  0
    358          2 Ethiopian     4720  124  0 13  4  1461  0  3   1   30  0  0
    359          3 Coptic        4996  124  0 13  4  1461  0  3   1   30  0  0
    360          4 Republican b  6504  111  0 13  4  1461  0  3   1   30  0  0 396 578797 −51
    361          5 Julian        4716 1401  2 12  4  1461  0  3   5  153  2  2
    362          6 Gregorian     4716 1401  2 12  4  1461  0  3   5  153  2  2 184 274277 −38
    363          7 Civil Islamic 5519 7664  0 12 30 10631 14 15 100 2951 51 10
    364          8 Baha’i ´ c    6560 1412 19 20  4  1461  0  3   1   19  0  0 184 274273 −50
    365          9 Saka          4794 1348  1 12  4  1461  0  3   1   31  0  0 184 274073 −36
    366
    367      Algorithm 3. To convert a date D/M/Y in one of the calendars listed in Table 15.14 to a
    368                   Julian Day Number, J:
    369          1. h = M − m
    370          2. g = Y + y − (n − h)/n
    371          3. f = mod(h − 1+ n, n)
    372          4. e = (p ∗ g + q)/r + D − 1− j
    373          5. J = e + (s ∗ f + t)/u
    374          6. J = J − (3 ∗ ((g + A)/100))/4 − C
    375
    376      """
    377      y = 4716
    378      j = 1401
    379      m = 2
    380      n = 12
    381      r = 4
    382      p = 1461
    383      q = 0
    384      v = 3
    385      u = 5
    386      s = 153
    387      t = 2
    388      w = 2
    389      A = 184
    390      B = 274277
    391      C = -38
    392      h = month - m
    393      g = year + y - (n - h) // n
    394      f = (h -1 + n) % n
    395      e = (p * g + q) // r + day -1 - j
    396      julian_day = e + (s * f + t) // u
    397      julian_day = julian_day - (3 * ((g + A) // 100)) // 4 - C
    398      return julian_day
    399