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