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

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

  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  from timelinelib.calendar.gregorian.time import GregorianDelta 
 20  from timelinelib.calendar.gregorian.time import GregorianTime 
21 22 23 -class GregorianDateTime(object):
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
35 - def __eq__(self, other):
36 return (isinstance(other, self.__class__) and 37 self.to_tuple() == other.to_tuple())
38
39 - def __ne__(self, other):
40 return not (self == other)
41 42 @classmethod
43 - def from_ymd(cls, year, month, day):
44 return cls(year, month, day, 0, 0, 0)
45 46 @classmethod
47 - def from_time(cls, time):
48 (year, month, day) = julian_day_to_gregorian_ymd(time.julian_day) 49 (hour, minute, second) = time.get_time_of_day() 50 return cls(year, month, day, hour, minute, second)
51 52 @property
53 - def week_number(self):
54 def monday_week_1(year): 55 from timelinelib.calendar.gregorian.timetype import GregorianTimeType 56 jan_4 = GregorianDateTime.from_ymd(year, 1, 4).to_time() 57 jan_4_day_of_week = GregorianTimeType().get_day_of_week(jan_4) 58 return jan_4 - GregorianDelta.from_days(jan_4_day_of_week)
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
77 - def is_bc(self):
78 return self.year <= 0
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
94 - def days_in_month(self):
95 return days_in_month(self.year, self.month)
96
97 - def to_tuple(self):
98 return (self.year, self.month, self.day, self.hour, self.minute, 99 self.second)
100
101 - def to_date_tuple(self):
102 return (self.year, self.month, self.day)
103
104 - def to_time_tuple(self):
105 return (self.hour, self.minute, self.second)
106
107 - def to_time(self):
108 days = gregorian_ymd_to_julian_day(self.year, self.month, self.day) 109 seconds = self.hour * 60 * 60 + self.minute * 60 + self.second 110 return GregorianTime(days, seconds)
111
112 - def is_first_day_in_year(self):
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
119 - def is_first_of_month(self):
120 return (self.day == 1 and 121 self.hour == 0 and 122 self.minute == 0 and 123 self.second == 0)
124
125 - def __repr__(self):
126 return "GregorianDateTime<%d-%02d-%02d %02d:%02d:%02d>" % self.to_tuple()
127
128 129 -def days_in_month(year, month):
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
138 139 -def is_leap_year(year):
140 return year % 4 == 0 and (year % 400 == 0 or not year % 100 == 0)
141
142 143 -def is_valid_time(hour, minute, second):
144 return ( 145 hour >= 0 and hour < 24 and 146 minute >= 0 and minute < 60 and 147 second >= 0 and second < 60 148 )
149
150 151 -def is_valid(year, month, day):
152 return ( 153 month >= 1 and month <= 12 and 154 day >= 1 and day <= days_in_month(year, month) 155 )
156
157 158 -def julian_day_to_gregorian_ymd(julian_day):
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
250 251 -def gregorian_ymd_to_julian_day(year, month, day):
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