1 | #!/usr/bin/env python3 |
---|
2 | ''' |
---|
3 | This library handles date calculs and convertions in different calendars. |
---|
4 | |
---|
5 | Mostly conversion of IGCM_date.ksh to python. |
---|
6 | |
---|
7 | Dates formar |
---|
8 | - Human format : [yy]yy-mm-dd |
---|
9 | - Gregorian format : yymmdd |
---|
10 | - Julian format : yyddd |
---|
11 | |
---|
12 | Types of calendars are possible : |
---|
13 | |
---|
14 | - leap|gregorian|standard (other name leap) : |
---|
15 | The normal calendar. The time origin for the |
---|
16 | julian day in this case is 24 Nov -4713. |
---|
17 | - noleap|365_day : |
---|
18 | A 365 day year without leap years. |
---|
19 | - all_leap|366_day : |
---|
20 | A 366 day year with only leap years. |
---|
21 | - 360d|360_day : |
---|
22 | Year of 360 days with month of equal length. |
---|
23 | |
---|
24 | Warning, to install, configure, run, use any of included software or |
---|
25 | to read the associated documentation you'll need at least one (1) |
---|
26 | brain in a reasonably working order. Lack of this implement will |
---|
27 | void any warranties (either express or implied). Authors assumes |
---|
28 | no responsability for errors, omissions, data loss, or any other |
---|
29 | consequences caused directly or indirectly by the usage of his |
---|
30 | software by incorrectly or partially configured personal |
---|
31 | |
---|
32 | SVN information |
---|
33 | $Author$ |
---|
34 | $Date$ |
---|
35 | $Revision$ |
---|
36 | $Id$ |
---|
37 | $HeadURL$ |
---|
38 | ''' |
---|
39 | |
---|
40 | import numpy as np |
---|
41 | debug = False |
---|
42 | |
---|
43 | DefaultCalendarType = 'Gregorian' |
---|
44 | |
---|
45 | # Characteristics of the gregorian calender |
---|
46 | mth_length = np.array ( [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] ) |
---|
47 | mth_start = np.array ( [ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334] ) |
---|
48 | mth_end = mth_start + mth_length + 1 # A cause des bornes superieures de Python |
---|
49 | |
---|
50 | # Other caalendars |
---|
51 | mth_length365 = np.array ( [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] ) |
---|
52 | mth_length366 = np.array ( [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] ) |
---|
53 | mth_length360 = np.array ( [30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30] ) |
---|
54 | |
---|
55 | # List of possible names for calendar types |
---|
56 | Calendar_gregorian = [ 'leap', 'LEAP', 'Leap', 'gregorian', 'Gregorian', 'GREGORIAN' ] |
---|
57 | Calendar_360d = [ '360d', '360_day', '360d', '360_days', '360D', '360_DAY', '360D', '360_DAYS' ] |
---|
58 | Calendar_noleap = [ 'noleap', '365_day', '365_days', 'NOLEAP', '365_DAY', '365_DAYS', ] |
---|
59 | Calendar_allleap = [ 'all_leap', '366_day', 'allleap', '366_days', '336d', 'ALL_LEAP', '366_DAY', 'ALLLEAP', '366_DAYS', '336D', ] |
---|
60 | |
---|
61 | # List of possible names for day, month and year |
---|
62 | YE_name = [ 'YE', 'YEAR', 'Years', 'years', 'YEAR', 'Year', 'year', 'YE', 'ye', 'Y', 'y' ] |
---|
63 | MO_name = [ 'MO', 'MONTHS', 'Months', 'months', 'MONTH', 'Month', 'month', 'MO', 'mo', 'M', 'm' ] |
---|
64 | DA_name = [ 'DA', 'DAYS', 'Days', 'days', 'DAY', 'Day', 'day', 'DA', 'da', 'D', 'd' ] |
---|
65 | |
---|
66 | # Still to do |
---|
67 | # function IGCM_date_DaysInNextPeriod |
---|
68 | # function IGCM_date_DaysInPreviousPeriod |
---|
69 | |
---|
70 | def GetMonthsLengths ( year, CalendarType=DefaultCalendarType ) : |
---|
71 | ''' |
---|
72 | Returns the month lengths for a given year and calendar type |
---|
73 | ''' |
---|
74 | if CalendarType in Calendar_360d : |
---|
75 | zlengths = mth_length360 |
---|
76 | |
---|
77 | if CalendarType in Calendar_noleap : |
---|
78 | zlengths = mth_length365 |
---|
79 | |
---|
80 | if CalendarType in Calendar_noleap : |
---|
81 | zlengths = mth_length366 |
---|
82 | |
---|
83 | if CalendarType in Calendar_gregorian : |
---|
84 | if IsLeapYear ( year, CalendarType) : |
---|
85 | zlengths = mth_length366 |
---|
86 | else : |
---|
87 | zlengths = mth_length365 |
---|
88 | |
---|
89 | return zlengths |
---|
90 | |
---|
91 | def DaysInMonth ( yy, mm=None, CalendarType=DefaultCalendarType) : |
---|
92 | ''' |
---|
93 | Returns the number of days in a month |
---|
94 | |
---|
95 | Usage: DaysInMonth ( yyyy , mm, [CalendarType] ) |
---|
96 | or DaysInMonth ( yyyymmdd, [CalendarType] ) |
---|
97 | ''' |
---|
98 | if mm : |
---|
99 | year = int(yy) ; month = int(mm) |
---|
100 | else : |
---|
101 | year, month = GetYearMonth (yy) |
---|
102 | |
---|
103 | length = GetMonthsLengths ( year, CalendarType=CalendarType )[ np.mod(month-1, 12) ] |
---|
104 | return length |
---|
105 | |
---|
106 | def DaysSinceJC ( date, CalendarType=DefaultCalendarType ) : |
---|
107 | ''' |
---|
108 | Calculate the days difference between a date and 00010101 |
---|
109 | |
---|
110 | Computation is splitted in three case for the sake of speed |
---|
111 | ''' |
---|
112 | yy, mo, da = GetYearMonthDay (date) |
---|
113 | if yy < 500 : |
---|
114 | date0 = '0101-01-01' |
---|
115 | if CalendarType in Calendar_360d : |
---|
116 | aux = -360 |
---|
117 | if CalendarType in Calendar_noleap : |
---|
118 | aux = -365 |
---|
119 | if CalendarType in Calendar_allleap : |
---|
120 | aux = -366 |
---|
121 | if CalendarType in Calendar_gregorian : |
---|
122 | aux = -366 |
---|
123 | |
---|
124 | elif yy < 1500 : |
---|
125 | date0 = '1001-01-01' |
---|
126 | if CalendarType in Calendar_360d : |
---|
127 | aux = 359640 |
---|
128 | if CalendarType in Calendar_noleap : |
---|
129 | aux = 364635 |
---|
130 | if CalendarType in Calendar_allleap : |
---|
131 | aux = 365634 |
---|
132 | if CalendarType in Calendar_gregorian : |
---|
133 | aux = 364877 |
---|
134 | |
---|
135 | else : |
---|
136 | date0 = '1901-01-01' |
---|
137 | if CalendarType in Calendar_360d : |
---|
138 | aux = 683640 |
---|
139 | if CalendarType in Calendar_noleap : |
---|
140 | aux = 693135 |
---|
141 | if CalendarType in Calendar_allleap : |
---|
142 | aux = 695034 |
---|
143 | if CalendarType in Calendar_gregorian : |
---|
144 | aux = 693595 |
---|
145 | |
---|
146 | ndays = DaysBetweenDate ( date, date0 ) + aux |
---|
147 | |
---|
148 | return ndays |
---|
149 | |
---|
150 | def IsLeapYear ( year, CalendarType=DefaultCalendarType ) : |
---|
151 | ''' |
---|
152 | True if Year is a leap year |
---|
153 | ''' |
---|
154 | yy = int ( year ) |
---|
155 | zis_leap_year = None |
---|
156 | |
---|
157 | # What is the CalendarType : |
---|
158 | if CalendarType in Calendar_360d : |
---|
159 | zis_leap_year = False |
---|
160 | if CalendarType in Calendar_noleap : |
---|
161 | zis_leap_year = False |
---|
162 | |
---|
163 | if CalendarType in Calendar_allleap : |
---|
164 | zis_leap_year = True |
---|
165 | |
---|
166 | if CalendarType in Calendar_gregorian : |
---|
167 | # A year is a leap year if it is even divisible by 4 |
---|
168 | # but not evenly divisible by 100 |
---|
169 | # unless it is evenly divisible by 400 |
---|
170 | |
---|
171 | # if it is evenly divisible by 400 it must be a leap year |
---|
172 | if not zis_leap_year and np.mod ( yy, 400 ) == 0 : |
---|
173 | zis_leap_year = True |
---|
174 | |
---|
175 | # if it is evenly divisible by 100 it must not be a leap year |
---|
176 | if not zis_leap_year and np.mod ( yy, 100 ) == 0 : |
---|
177 | zis_leap_year = False |
---|
178 | |
---|
179 | # if it is evenly divisible by 4 it must be a leap year |
---|
180 | if not zis_leap_year and np.mod ( yy, 4 ) == 0 : |
---|
181 | zis_leap_year = True |
---|
182 | |
---|
183 | if not zis_leap_year : |
---|
184 | zis_leap_year = False |
---|
185 | |
---|
186 | return zis_leap_year |
---|
187 | |
---|
188 | def DateFormat ( date ) : |
---|
189 | ''' |
---|
190 | Get date format. Could be human or gregorian |
---|
191 | |
---|
192 | [yy]yymmdd is Gregorian |
---|
193 | [yy]yy-mm-dd is Human |
---|
194 | ''' |
---|
195 | if isinstance (date, str) : |
---|
196 | if '-' in date : |
---|
197 | zdate_format = 'Human' |
---|
198 | else : |
---|
199 | zdate_format = 'Gregorian' |
---|
200 | if isinstance (date, int) : zdate_format = 'Gregorian' |
---|
201 | return zdate_format |
---|
202 | |
---|
203 | def PrintDate ( ye, mo, da, pformat ) : |
---|
204 | ''' |
---|
205 | Return a date in the requested format |
---|
206 | ''' |
---|
207 | zPrintDate = None |
---|
208 | if pformat == 'Human' : zPrintDate = f'{ye:04d}-{mo:02d}-{da:02d}' |
---|
209 | if pformat == 'Gregorian' : zPrintDate = f'{ye:04d}{mo:02d}{da:02d}' |
---|
210 | return zPrintDate |
---|
211 | |
---|
212 | def ConvertFormatToGregorian ( date ) : |
---|
213 | ''' |
---|
214 | From a yyyy-mm-dd or yyymmdd date format returns a yyymmdd date format |
---|
215 | ''' |
---|
216 | return PrintDate ( *GetYearMonthDay ( date ), 'Gregorian' ) |
---|
217 | |
---|
218 | def ConvertFormatToHuman ( date ) : |
---|
219 | ''' |
---|
220 | From a yyyymmdd or yyymmdd date format returns a yyy-mm-dd date format |
---|
221 | ''' |
---|
222 | return PrintDate ( *GetYearMonthDay ( date ), 'Human' ) |
---|
223 | |
---|
224 | def GetYearMonthDay ( date ) : |
---|
225 | ''' |
---|
226 | Split Date in format [yy]yymmdd or [yy]yy-mm-dd to yy, mm, dd |
---|
227 | ''' |
---|
228 | if isinstance (date, str) : |
---|
229 | if '-' in date : |
---|
230 | zz = date.split ('-') |
---|
231 | if len(zz) == 3 : |
---|
232 | ye, mo, da = zz |
---|
233 | if len(zz) == 2 : |
---|
234 | ye, mo = zz |
---|
235 | da = 0 |
---|
236 | if len(zz) == 1 : |
---|
237 | ye = zz |
---|
238 | da = 0 ; mo = 0 |
---|
239 | else : |
---|
240 | date = int (date) |
---|
241 | |
---|
242 | if isinstance (date, int) : |
---|
243 | if date > 1000000 : |
---|
244 | da = np.mod ( date, 100) |
---|
245 | mo = np.mod ( date//100, 100) |
---|
246 | ye = date // 10000 |
---|
247 | elif date > 100000 : |
---|
248 | mo = np.mod ( date, 100) |
---|
249 | ye = date // 100 |
---|
250 | da = 0 |
---|
251 | else : |
---|
252 | ye = date |
---|
253 | da = 0 ; mo = 0 |
---|
254 | |
---|
255 | if ye : ye = int (ye) |
---|
256 | if mo : mo = int (mo) |
---|
257 | if da : da = int (da) |
---|
258 | return ye, mo, da |
---|
259 | |
---|
260 | def GetYearMonth ( date ) : |
---|
261 | ''' |
---|
262 | Split Date in format [yy]yymmdd or [yy]yy-mm-dd to yy, mm, dd |
---|
263 | ''' |
---|
264 | ye, mo, da = GetYearMonthDay (date) |
---|
265 | return ye, mo |
---|
266 | |
---|
267 | def DateAddYear ( date, year_inc='1Y' ) : |
---|
268 | ''' |
---|
269 | Add year(s) to date in format [yy]yymmdd or [yy]yy-mm-dd |
---|
270 | ''' |
---|
271 | zformat = DateFormat ( date ) |
---|
272 | ye, mo, da = GetYearMonthDay ( date ) |
---|
273 | |
---|
274 | if isinstance ( year_inc, str) : |
---|
275 | PeriodType, PeriodLength = AnaPeriod ( year_inc ) |
---|
276 | print ( f"DateAddYear {PeriodType=} {PeriodLength=}" ) |
---|
277 | if PeriodType == YE_name[0] : |
---|
278 | year_inc = PeriodLength |
---|
279 | else : |
---|
280 | raise AttributeError ( f'Parameter {year_inc=} is not a year period' ) |
---|
281 | |
---|
282 | ye_new = ye + year_inc |
---|
283 | return PrintDate ( ye_new, mo, da, zformat) |
---|
284 | |
---|
285 | def CorrectYearMonth ( ye, mo) : |
---|
286 | ''' |
---|
287 | Correct month values outside [1,12] |
---|
288 | ''' |
---|
289 | mo_new = mo |
---|
290 | ye_new = ye |
---|
291 | |
---|
292 | while mo_new > 12 : |
---|
293 | mo_new = mo_new - 12 |
---|
294 | ye_new = ye_new + 1 |
---|
295 | |
---|
296 | while mo_new < 1 : |
---|
297 | mo_new = mo_new + 12 |
---|
298 | ye_new = ye_new - 1 |
---|
299 | |
---|
300 | return ye_new, mo_new |
---|
301 | |
---|
302 | def CorrectYearMonthDay (ye, mo, da, CalendarType=DefaultCalendarType) : |
---|
303 | ''' |
---|
304 | Correct month values outside [1,12] and day outside month length |
---|
305 | ''' |
---|
306 | ye_new, mo_new = CorrectYearMonth ( ye, mo) |
---|
307 | da_new = da |
---|
308 | |
---|
309 | num_day = DaysInMonth (ye, mo, CalendarType) |
---|
310 | |
---|
311 | while da_new > num_day : |
---|
312 | da_new = da_new - num_day |
---|
313 | mo_new = mo_new + 1 |
---|
314 | ye_new, mo_new = CorrectYearMonth ( ye_new, mo_new) |
---|
315 | num_day = DaysInMonth (ye_new, mo_new, CalendarType) |
---|
316 | while da_new < 1 : |
---|
317 | mo_new = mo_new - 1 |
---|
318 | num_day = DaysInMonth (ye_new, mo_new, CalendarType) |
---|
319 | da_new = da_new + num_day |
---|
320 | ye_new, mo_new = CorrectYearMonth ( ye_new, mo_new) |
---|
321 | num_day = DaysInMonth (ye, mo, CalendarType) |
---|
322 | |
---|
323 | return ye_new, mo_new, da_new |
---|
324 | |
---|
325 | def DateAddMonth ( date, month_inc=1, CalendarType=DefaultCalendarType, verbose=False ) : |
---|
326 | ''' |
---|
327 | Add on year(s) to date in format [yy]yymmdd or [yy]yy-mm-dd |
---|
328 | ''' |
---|
329 | zformat = DateFormat ( date ) |
---|
330 | ye, mo, da = GetYearMonthDay ( date ) |
---|
331 | |
---|
332 | if isinstance ( month_inc, str) : |
---|
333 | PeriodType, PeriodLength = AnaPeriod ( month_inc ) |
---|
334 | if PeriodType == MO_name[0] : |
---|
335 | month_inc = PeriodLength |
---|
336 | else : |
---|
337 | raise AttributeError ( f'Parameter {month} is not a month period' ) |
---|
338 | |
---|
339 | if month_inc < 0 : |
---|
340 | ye_inc = -( -month_inc // 12) |
---|
341 | else : |
---|
342 | ye_inc = month_inc // 12 |
---|
343 | |
---|
344 | ye_new = ye + ye_inc |
---|
345 | mo_new = mo + month_inc # - ye_inc*12 |
---|
346 | ye_new, mo_new = CorrectYearMonth (ye_new, mo_new) |
---|
347 | lday1 = DaysInMonth ( ye , mo , CalendarType=CalendarType ) |
---|
348 | lday2 = DaysInMonth ( ye_new, mo_new, CalendarType=CalendarType ) |
---|
349 | if da == lday1 : da_new = lday2 |
---|
350 | da_new = np.minimum ( da_new, lday2) |
---|
351 | |
---|
352 | if verbose : print ( f'{ye=} {mo=} {da=} {ye_new=} {mo_new=} {lday1=} {lday2=} {da_new=}' ) |
---|
353 | |
---|
354 | return PrintDate ( ye_new, mo_new, da_new, zformat) |
---|
355 | |
---|
356 | def DateAddPeriod ( date, period='1YE', CalendarType=DefaultCalendarType ) : |
---|
357 | ''' |
---|
358 | Add a period to date in format [yy]yymmdd or [yy]yy-mm-dd |
---|
359 | ''' |
---|
360 | zformat = DateFormat ( date ) |
---|
361 | |
---|
362 | |
---|
363 | PeriodType, PeriodLength = AnaPeriod ( period ) |
---|
364 | |
---|
365 | if PeriodType == YE_name[0] : |
---|
366 | new_date = DateAddYear ( date, year_inc=period ) |
---|
367 | if PeriodType == MO_name[0] : |
---|
368 | new_date = DateAddMonth ( date, month_inc=period, CalendarType=DefaultCalendarType ) |
---|
369 | if PeriodType == DA_name[0] : |
---|
370 | new_date = AddDaysToDate ( date, ndays=period, CalendarType=DefaultCalendarType ) |
---|
371 | if PeriodType == 'Unknow' : |
---|
372 | raise AttributeError ( f"DateAddPeriod : period syntax {period=} not understood" ) |
---|
373 | |
---|
374 | ye, mo, da = GetYearMonthDay ( new_date ) |
---|
375 | return PrintDate ( ye, mo, da, zformat ) |
---|
376 | |
---|
377 | def SubOneDayToDate ( date, CalendarType=DefaultCalendarType) : |
---|
378 | ''' |
---|
379 | Substracts one day to date in format [yy]yymmdd or [yy]yy-mm-dd |
---|
380 | ''' |
---|
381 | zformat = DateFormat ( date ) |
---|
382 | ye, mo, da = GetYearMonthDay ( date ) |
---|
383 | zlength = GetMonthsLengths ( ye, CalendarType ) |
---|
384 | |
---|
385 | ye = int(ye) ; mo = int(mo) ; da=int(da) |
---|
386 | if da == 1 : |
---|
387 | if mo == 1 : |
---|
388 | da_new, mo_new, ye_new = zlength[-1 ], 12 , ye - 1 |
---|
389 | else : |
---|
390 | da_new, mo_new, ye_new = zlength[mo-2], mo - 1, ye |
---|
391 | else : |
---|
392 | da_new, mo_new, ye_new = da - 1, mo, ye |
---|
393 | |
---|
394 | return PrintDate ( ye_new, mo_new, da_new, zformat) |
---|
395 | |
---|
396 | def AddOneDayToDate ( date, CalendarType=DefaultCalendarType ) : |
---|
397 | ''' |
---|
398 | Add one day to date in format [yy]yymmdd or [yy]yy-mm-dd |
---|
399 | ''' |
---|
400 | if debug : print ( f'AddOneDayToDate : {date=}' ) |
---|
401 | zformat = DateFormat ( date ) |
---|
402 | ye, mo, da = GetYearMonthDay ( date ) |
---|
403 | zlength = GetMonthsLengths ( ye, CalendarType ) |
---|
404 | |
---|
405 | ye_new = ye |
---|
406 | mo_new = mo |
---|
407 | da_new = da+1 |
---|
408 | if da_new > zlength [mo_new-1] : |
---|
409 | da_new = 1 |
---|
410 | mo_new = mo_new + 1 |
---|
411 | if mo_new == 13 : |
---|
412 | mo_new = 1 |
---|
413 | ye_new += 1 |
---|
414 | |
---|
415 | return PrintDate ( ye_new, mo_new, da_new, zformat ) |
---|
416 | |
---|
417 | def AddDaysToDate ( date, ndays='1D', CalendarType=DefaultCalendarType ) : |
---|
418 | ''' |
---|
419 | Add days to date in format [yy]yymmdd or [yy]yy-mm-dd |
---|
420 | Number of days migth be negative |
---|
421 | ''' |
---|
422 | zformat = DateFormat ( date ) |
---|
423 | |
---|
424 | # Break it into pieces |
---|
425 | yy, mm, dd = GetYearMonthDay ( date ) |
---|
426 | |
---|
427 | if isinstance (ndays, str) : |
---|
428 | PeriodType, PeriodLength = AnaPeriod (ndays ) |
---|
429 | if PeriodType == DA_name[0] : |
---|
430 | ndays=PeriodLength |
---|
431 | else : |
---|
432 | raise AttributeError ( f'{ndays=} is not a day period' ) |
---|
433 | |
---|
434 | zdate0 = date |
---|
435 | |
---|
436 | if ndays > 0 : |
---|
437 | for nn in np.arange (ndays) : |
---|
438 | zdate0 = AddOneDayToDate ( zdate0, CalendarType ) |
---|
439 | |
---|
440 | if ndays < 0 : |
---|
441 | for nn in np.arange (-ndays) : |
---|
442 | zdate0 = SubOneDayToDate ( zdate0, CalendarType ) |
---|
443 | |
---|
444 | yy, mm, dd = GetYearMonthDay ( zdate0 ) |
---|
445 | |
---|
446 | return PrintDate ( yy, mm, dd, zformat ) |
---|
447 | |
---|
448 | def AddPeriodToDate ( date, period, CalendarType=DefaultCalendarType ) : |
---|
449 | ''' |
---|
450 | Add a period to a date. |
---|
451 | period is specified as '1D', '5YE', '3DA', etc ... |
---|
452 | ''' |
---|
453 | ndays = DaysInCurrentPeriod ( date, period, CalendarType=CalendarType) |
---|
454 | new_date = AddDaysToDate ( date, ndays=1, CalendarType=CalendarType ) |
---|
455 | |
---|
456 | return new_date |
---|
457 | |
---|
458 | def DaysInYear (year, CalendarType=DefaultCalendarType ) : |
---|
459 | ''' |
---|
460 | Return the number of days in a year |
---|
461 | ''' |
---|
462 | if CalendarType in Calendar_360d : |
---|
463 | ndays = 360 |
---|
464 | |
---|
465 | if CalendarType in Calendar_noleap : |
---|
466 | ndays = 365 |
---|
467 | |
---|
468 | if CalendarType in Calendar_allleap : |
---|
469 | ndays = 366 |
---|
470 | |
---|
471 | if CalendarType in Calendar_gregorian : |
---|
472 | if IsLeapYear ( year, CalendarType ) : |
---|
473 | ndays = 366 |
---|
474 | else : |
---|
475 | ndays = 365 |
---|
476 | |
---|
477 | return ndays |
---|
478 | |
---|
479 | def DaysBetweenDate ( pdate1, pdate2, CalendarType=DefaultCalendarType ) : |
---|
480 | ''' |
---|
481 | Calculates the days difference between two dates |
---|
482 | |
---|
483 | This process subtracts pdate2 from pdate1. If pdate2 is larger |
---|
484 | than pdate1 then reverse the arguments. The calculations are done |
---|
485 | and then the sign is reversed. |
---|
486 | ''' |
---|
487 | if pdate1 < pdate2 : |
---|
488 | date1=pdate2 ; date2=pdate1 |
---|
489 | if pdate1 > pdate2 : |
---|
490 | date1=pdate1 ; date2=pdate2 |
---|
491 | |
---|
492 | if pdate1 == pdate2 : |
---|
493 | res = 0 |
---|
494 | else : |
---|
495 | res = 0 |
---|
496 | zdate1 = date2 |
---|
497 | |
---|
498 | while zdate1 < date1 : |
---|
499 | zdate1 = AddOneDayToDate ( zdate1, CalendarType) |
---|
500 | res += 1 |
---|
501 | |
---|
502 | # if argument 2 was larger than argument 1 then |
---|
503 | # the arguments were reversed before calculating |
---|
504 | # adjust by reversing the sign |
---|
505 | if pdate1 < pdate2 : |
---|
506 | res = -res |
---|
507 | |
---|
508 | # and output the results |
---|
509 | return res |
---|
510 | |
---|
511 | def ConvertGregorianDateToJulian (date, CalendarType=DefaultCalendarType) : |
---|
512 | ''' |
---|
513 | Convert yyyymmdd to yyyyddd |
---|
514 | ''' |
---|
515 | ye, mo, da = GetYearMonthDay (date) |
---|
516 | ndays = DaysBetweenDate ( PrintDate (ye,mo,da, 'Human'), PrintDate (ye,1,1, 'Human'), CalendarType=CalendarType ) |
---|
517 | |
---|
518 | return int ( f'{ye}{ndays+1:03d}' ) |
---|
519 | |
---|
520 | def ConvertJulianDateToGregorian (date, CalendarType=DefaultCalendarType) : |
---|
521 | ''' |
---|
522 | Convert yyyyddd to yyyymmdd |
---|
523 | ''' |
---|
524 | |
---|
525 | # Break apart the year and the days |
---|
526 | zdate = int (date) |
---|
527 | yy = zdate // 1000 |
---|
528 | dd = np.mod (zdate, 1000 ) |
---|
529 | |
---|
530 | # subtract the number of days in each month starting from 1 |
---|
531 | # from the days in the date. When the day goes below 1, you |
---|
532 | # have the current month. Add back the number of days in the |
---|
533 | # month to get the correct day of the month |
---|
534 | mm=1 |
---|
535 | while dd > 0 : |
---|
536 | #print ( f'ConvertJulianDateToGregorian {yy=} {mm=} {dd=}' ) |
---|
537 | md = DaysInMonth ( yy, mm, CalendarType=CalendarType) |
---|
538 | dd = dd - md |
---|
539 | mm = mm + 1 |
---|
540 | |
---|
541 | # The loop steps one past the correct month, so back up the month |
---|
542 | dd = dd + md |
---|
543 | mm = mm - 1 |
---|
544 | |
---|
545 | # Assemble the results into a gregorian date |
---|
546 | return PrintDate ( yy, mm, dd, 'Gregorian') |
---|
547 | |
---|
548 | def DaysInCurrentPeriod ( startdate, period, CalendarType=DefaultCalendarType ) : |
---|
549 | ''' |
---|
550 | Give the numbers of days during the period from startdate date |
---|
551 | ''' |
---|
552 | year, month, day = GetYearMonthDay ( startdate ) |
---|
553 | PeriodType, PeriodLength = AnaPeriod ( period ) |
---|
554 | |
---|
555 | if PeriodType == YE_name[0] : |
---|
556 | PeriodLengthInYears = PeriodLength |
---|
557 | |
---|
558 | dateend = DateAddYear ( startdate, PeriodLengthInYears ) |
---|
559 | Length = DaysBetweenDate ( dateend, startdate ) |
---|
560 | |
---|
561 | elif PeriodType == MO_name[0] : |
---|
562 | PeriodLengthInMonths = PeriodLength |
---|
563 | |
---|
564 | year0 = year |
---|
565 | treatedYear = 0 |
---|
566 | Length = 0 |
---|
567 | for i in np.arange ( PeriodLengthInMonths ) : |
---|
568 | Length = Length + DaysInMonth ( year, month + i-12*treatedYear, CalendarType=CalendarType) |
---|
569 | if month + i >= 12 * (treatedYear + 1) : |
---|
570 | year = year0 + 1 |
---|
571 | treatedYear = treatedYear + 1 |
---|
572 | |
---|
573 | elif PeriodType == DA_name[0] : |
---|
574 | PeriodLengthInDays = PeriodLength |
---|
575 | Length = PeriodLengthInDays |
---|
576 | |
---|
577 | else : |
---|
578 | Length = None |
---|
579 | |
---|
580 | return Length |
---|
581 | |
---|
582 | def AnaPeriod ( period ) : |
---|
583 | ''' |
---|
584 | Decodes a period definition like '1Y', ''1MO', 'DA', etc ... |
---|
585 | Return period types (string) and period length (integer) |
---|
586 | ''' |
---|
587 | periodName = rmDigits (period) |
---|
588 | periodLength = getDigits (period) |
---|
589 | |
---|
590 | if '-' in periodName : |
---|
591 | Neg = True |
---|
592 | else : |
---|
593 | Neg = False |
---|
594 | |
---|
595 | periodName = periodName.replace ( '-', '') |
---|
596 | |
---|
597 | if periodName in YE_name : |
---|
598 | PeriodType = YE_name[0] |
---|
599 | if periodLength == '' : |
---|
600 | PeriodLength = 1 |
---|
601 | else : |
---|
602 | PeriodLength = int ( periodLength ) |
---|
603 | |
---|
604 | elif periodName in MO_name : |
---|
605 | PeriodType = MO_name[0] |
---|
606 | if periodLength == '' : |
---|
607 | PeriodLength = 1 |
---|
608 | else : |
---|
609 | PeriodLength = int ( periodLength ) |
---|
610 | |
---|
611 | elif periodName in DA_name : |
---|
612 | PeriodType = DA_name[0] |
---|
613 | if periodLength == '' : |
---|
614 | PeriodLength = 1 |
---|
615 | else : |
---|
616 | PeriodLength = int ( periodLength ) |
---|
617 | |
---|
618 | else : |
---|
619 | PeriodType = 'Unknown' |
---|
620 | PeriodLength = 0 |
---|
621 | |
---|
622 | if Neg : PeriodLength = -PeriodLength |
---|
623 | |
---|
624 | return PeriodType, PeriodLength |
---|
625 | |
---|
626 | def getDigits ( s ) : |
---|
627 | '''Extract digits in a string''' |
---|
628 | return ''.join (i for i in s if i.isdigit()) |
---|
629 | |
---|
630 | def rmDigits ( s ) : |
---|
631 | '''Removes digits from aa string''' |
---|
632 | return ''.join (i for i in s if not i.isdigit()) |
---|