1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 from timelinelib.calendar.gregorian.time import GregorianDelta
20 from timelinelib.calendar.gregorian.time import GregorianTime
24
25 - def __init__(self, year, month, day, hour, minute, second):
26 if not is_valid(year, month, day):
27 raise ValueError("Invalid gregorian date %s-%s-%s" % (year, month, day))
28 self.year = year
29 self.month = month
30 self.day = day
31 self.hour = hour
32 self.minute = minute
33 self.second = second
34
36 return (isinstance(other, self.__class__) and
37 self.to_tuple() == other.to_tuple())
38
40 return not (self == other)
41
42 @classmethod
44 return cls(year, month, day, 0, 0, 0)
45
46 @classmethod
51
52 @property
59
60 def days_between(end, start):
61 return end.julian_day - start.julian_day
62
63 def days_since_monday_week_1(time):
64 year = GregorianDateTime.from_time(time).year
65 diff = days_between(end=time, start=monday_week_1(year + 1))
66 if diff >= 0:
67 return diff
68 diff = days_between(end=time, start=monday_week_1(year))
69 if diff >= 0:
70 return diff
71 diff = days_between(end=time, start=monday_week_1(year - 1))
72 if diff >= 0:
73 return diff
74 raise ValueError("should not end up here")
75 return days_since_monday_week_1(self.to_time()) / 7 + 1
76
79
80 - def replace(self, year=None, month=None):
81 if year is None:
82 year = self.year
83 if month is None:
84 month = self.month
85 return self.__class__(
86 year,
87 month,
88 self.day,
89 self.hour,
90 self.minute,
91 self.second
92 )
93
96
98 return (self.year, self.month, self.day, self.hour, self.minute,
99 self.second)
100
102 return (self.year, self.month, self.day)
103
105 return (self.hour, self.minute, self.second)
106
111
113 return (self.month == 1 and
114 self.day == 1 and
115 self.hour == 0 and
116 self.minute == 0 and
117 self.second == 0)
118
120 return (self.day == 1 and
121 self.hour == 0 and
122 self.minute == 0 and
123 self.second == 0)
124
126 return "GregorianDateTime<%d-%02d-%02d %02d:%02d:%02d>" % self.to_tuple()
127
130 if month in [4, 6, 9, 11]:
131 return 30
132 if month in [1, 3, 5, 7, 8, 10, 12]:
133 return 31
134 if is_leap_year(year):
135 return 29
136 return 28
137
140 return year % 4 == 0 and (year % 400 == 0 or not year % 100 == 0)
141
144 return (
145 hour >= 0 and hour < 24 and
146 minute >= 0 and minute < 60 and
147 second >= 0 and second < 60
148 )
149
152 return (
153 month >= 1 and month <= 12 and
154 day >= 1 and day <= days_in_month(year, month)
155 )
156
159 """
160 This algorithm is described here:
161
162 * http://www.tondering.dk/claus/cal/julperiod.php#formula
163
164 Integer division works differently in C and in Python for negative numbers.
165 C truncates towards 0 and Python truncates towards negative infinity:
166 http://python-history.blogspot.se/2010/08/why-pythons-integer-division-floors.html
167
168 The above source don't state which to be used. If we can prove that
169 division-expressions are always positive, we can be sure this algorithm
170 works the same in C and in Python.
171
172 We must prove that:
173
174 1) m >= 0
175 2) ((5 * e) + 2) >= 0 => e >= 0
176 3) (1461 * d) >= 0 => d >= 0
177 4) ((4 * c) + 3) >= 0 => c >= 0
178 5) (b * 146097) >= 0 => b >= 0
179 6) ((4 * a) + 3) >= 0 => a >= 0
180
181 Let's work from the top:
182
183 julian_day >= 0 =>
184
185 a >= 0 + 32044
186 = 32044 =>
187
188 This proves 6).
189
190 b >= ((4 * 32044) + 3) // 146097
191 = 0
192
193 This proves 5).
194
195 Let's look at c:
196
197 c = a - ((b * 146097) // 4)
198 = a - (((((4 * a) + 3) // 146097) * 146097) // 4)
199
200 For c to be >= 0, then
201
202 (((((4 * a) + 3) // 146097) * 146097) // 4) <= a
203
204 Let's look at this component: ((((4 * a) + 3) // 146097) * 146097)
205
206 This expression can never be larger than (4 * a) + 3. That gives this:
207
208 ((4 * a) + 3) // 4 <= a, which holds.
209
210 This proves 4).
211
212 Now, let's look at d:
213
214 d = ((4 * c) + 3) // 1461
215
216 If c is >= 0, then d is also >= 0.
217
218 This proves 3).
219
220 Let's look at e:
221
222 e = c - ((1461 * d) // 4)
223 = c - ((1461 * (((4 * c) + 3) // 1461)) // 4)
224
225 The same resoning as above can be used to conclude that e >= 0.
226
227 This proves 2).
228
229 Now, let's look at m:
230
231 m = ((5 * e) + 2) // 153
232
233 If e >= 0, then m is also >= 0.
234
235 This proves 1).
236 """
237 if julian_day < GregorianTime.MIN_JULIAN_DAY:
238 raise ValueError("julian_day_to_gregorian_ymd only works for julian days >= %d, but was %d" % (GregorianTime.MIN_JULIAN_DAY, julian_day))
239 a = julian_day + 32044
240 b = ((4 * a) + 3) // 146097
241 c = a - ((b * 146097) // 4)
242 d = ((4 * c) + 3) // 1461
243 e = c - ((1461 * d) // 4)
244 m = ((5 * e) + 2) // 153
245 day = e - (((153 * m) + 2) // 5) + 1
246 month = m + 3 - (12 * (m // 10))
247 year = (b * 100) + d - 4800 + (m // 10)
248 return (year, month, day)
249
252 """
253 This algorithm is described here:
254
255 * http://www.tondering.dk/claus/cal/julperiod.php#formula
256 * http://en.wikipedia.org/wiki/Julian_day#Converting_Julian_or_Gregorian_calendar_date_to_Julian_Day_Number
257
258 Integer division works differently in C and in Python for negative numbers.
259 C truncates towards 0 and Python truncates towards negative infinity:
260 http://python-history.blogspot.se/2010/08/why-pythons-integer-division-floors.html
261
262 The above sources don't state which to be used. If we can prove that
263 division-expressions are always positive, we can be sure this algorithm
264 works the same in C and in Python.
265
266 We must prove that:
267
268 1) y >= 0
269 2) ((153 * m) + 2) >= 0
270
271 Let's prove 1):
272
273 y = year + 4800 - a
274 = year + 4800 - ((14 - month) // 12)
275
276 year >= -4713 (gives a julian day of 0)
277
278 so
279
280 year + 4800 >= -4713 + 4800 = 87
281
282 The expression ((14 - month) // 12) varies between 0 and 1 when month
283 varies between 1 and 12. Therefore y >= 87 - 1 = 86, and 1) is proved.
284
285 Let's prove 2):
286
287 m = month + (12 * a) - 3
288 = month + (12 * ((14 - month) // 12)) - 3
289
290 1 <= month <= 12
291
292 m(1) = 1 + (12 * ((14 - 1) // 12)) - 3 = 1 + (12 * 1) - 3 = 10
293 m(2) = 2 + (12 * ((14 - 2) // 12)) - 3 = 2 + (12 * 1) - 3 = 11
294 m(3) = 3 + (12 * ((14 - 3) // 12)) - 3 = 3 + (12 * 0) - 3 = 0
295 m(4) = 4 + (12 * ((14 - 4) // 12)) - 3 = 4 + (12 * 0) - 3 = 1
296 m(5) = 5 + (12 * ((14 - 5) // 12)) - 3 = 5 + (12 * 0) - 3 = 2
297 m(6) = 6 + (12 * ((14 - 6) // 12)) - 3 = 6 + (12 * 0) - 3 = 3
298 m(7) = 7 + (12 * ((14 - 7) // 12)) - 3 = 7 + (12 * 0) - 3 = 4
299 m(8) = 8 + (12 * ((14 - 8) // 12)) - 3 = 8 + (12 * 0) - 3 = 5
300 m(9) = 9 + (12 * ((14 - 9) // 12)) - 3 = 9 + (12 * 0) - 3 = 6
301 m(10) = 10 + (12 * ((14 - 10) // 12)) - 3 = 10 + (12 * 0) - 3 = 7
302 m(11) = 11 + (12 * ((14 - 11) // 12)) - 3 = 11 + (12 * 0) - 3 = 8
303 m(12) = 12 + (12 * ((14 - 12) // 12)) - 3 = 12 + (12 * 0) - 3 = 9
304
305 So, m is always > 0. Which also makes the expression ((153 * m) + 2) > 0,
306 and 2) is proved.
307 """
308 a = (14 - month) // 12
309 y = year + 4800 - a
310 m = month + (12 * a) - 3
311 julian_day = (day
312 + (((153 * m) + 2) // 5)
313 + (y * 365)
314 + (y // 4)
315 - (y // 100)
316 + (y // 400)
317 - 32045)
318 if julian_day < GregorianTime.MIN_JULIAN_DAY:
319 raise ValueError("gregorian_ymd_to_julian_day only works for julian days >= %d, but was %d" % (GregorianTime.MIN_JULIAN_DAY, julian_day))
320 return julian_day
321