1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
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
45 """ """
46 return (isinstance(other, self.__class__) and
47 self.to_tuple() == other.to_tuple())
48
50 """ """
51 return not (self == other)
52
53 @classmethod
55 """ """
56 return cls(year, month, day, 0, 0, 0)
57
58 @classmethod
64
65 @property
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
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
114
116 """ """
117 return (self.year, self.month, self.day, self.hour, self.minute,
118 self.second)
119
121 """ """
122 return (self.year, self.month, self.day)
123
125 """ """
126 return (self.hour, self.minute, self.second)
127
133
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
143 """ """
144 return (self.day == 1 and
145 self.hour == 0 and
146 self.minute == 0 and
147 self.second == 0)
148
150 """ """
151 return "GregorianDateTime<%d-%02d-%02d %02d:%02d:%02d>" % self.to_tuple()
152
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
166 """ """
167 return year % 4 == 0 and (year % 400 == 0 or not year % 100 == 0)
168
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
180 """ """
181 return (
182 month >= 1 and month <= 12 and
183 day >= 1 and day <= days_in_month(year, month)
184 )
185
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
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
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