2 #Copyright (C) 2009-2010 :
3 # Gabes Jean, naparuba@gmail.com
4 # Gerhard Lausser, Gerhard.Lausser@consol.de
5 # Gregory Starck, g.starck@gmail.com
6 # Hartmut Goebel, h.goebel@goebel-consult.de
8 #This file is part of Shinken.
10 #Shinken is free software: you can redistribute it and/or modify
11 #it under the terms of the GNU Affero General Public License as published by
12 #the Free Software Foundation, either version 3 of the License, or
13 #(at your option) any later version.
15 #Shinken is distributed in the hope that it will be useful,
16 #but WITHOUT ANY WARRANTY; without even the implied warranty of
17 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 #GNU Affero General Public License for more details.
20 #You should have received a copy of the GNU Affero General Public License
21 #along with Shinken. If not, see <http://www.gnu.org/licenses/>.
25 from shinken
.util
import get_sec_from_morning
, get_day
, get_start_of_day
, get_end_of_day
28 #Get the day number (like 27 in July tuesday 27 2010 for call:
29 #2010, july, tuesday, -1 (last tuesday of july 2010)
30 def find_day_by_weekday_offset(year
, month
, weekday
, offset
):
31 #et the id if teh weekday (1 for tuesday)
32 weekday_id
= Daterange
.get_weekday_id(weekday
)
33 if weekday_id
is None:
37 month_id
= Daterange
.get_month_id(month
)
42 cal
= calendar
.monthcalendar(year
, month_id
)
44 #If we ask for a -1 day, just reverse cal
52 for i
in xrange(0, offset
+ 1):
53 #in cal 0 mean "there are no day here :)"
54 if cal
[i
][weekday_id
] != 0:
56 if nb_found
== offset
:
57 return cal
[i
][weekday_id
]
63 def find_day_by_offset(year
, month
, offset
):
64 month_id
= Daterange
.get_month_id(month
)
67 (tmp
, days_in_month
) = calendar
.monthrange(year
, month_id
)
69 return min(offset
, days_in_month
)
71 return max(1, days_in_month
+ offset
+ 1)
75 #entry is like 00:00-24:00
76 def __init__(self
, entry
):
77 entries
= entry
.split('-')
80 sentries
= start
.split(':')
81 self
.hstart
= int(sentries
[0])
82 self
.mstart
= int(sentries
[1])
83 eentries
= end
.split(':')
84 self
.hend
= int(eentries
[0])
85 self
.mend
= int(eentries
[1])
88 return str(self
.__dict
__)
90 def get_sec_from_morning(self
):
91 return self
.hstart
*3600 + self
.mstart
*60
94 def get_first_sec_out_from_morning(self
):
95 #If start at 0:0, the min out is the end
96 if self
.hstart
== 0 and self
.mstart
== 0:
97 return self
.hend
*3600 + self
.mend
*60
101 def is_time_valid(self
, t
):
102 sec_from_morning
= get_sec_from_morning(t
)
103 return self
.hstart
*3600 + self
.mstart
* 60 <= sec_from_morning
<= self
.hend
*3600 + self
.mend
* 60
108 weekdays
= {'monday' : 0, 'tuesday' : 1, 'wednesday' : 2, 'thursday' : 3, \
109 'friday' : 4, 'saturday' : 5, 'sunday': 6 }
110 months
= {'january' : 1, 'february': 2, 'march' : 3, 'april' : 4, 'may' : 5, \
111 'june' : 6, 'july' : 7, 'august' : 8, 'september' : 9, \
112 'october' : 10, 'november' : 11, 'december' : 12}
113 def __init__(self
, syear
, smon
, smday
, swday
, swday_offset
,
114 eyear
, emon
, emday
, ewday
, ewday_offset
, skip_interval
, other
):
115 self
.syear
= int(syear
)
117 self
.smday
= int(smday
)
119 self
.swday_offset
= int(swday_offset
)
120 self
.eyear
= int(eyear
)
122 self
.emday
= int(emday
)
124 self
.ewday_offset
= int(ewday_offset
)
125 self
.skip_interval
= int(skip_interval
)
129 for timeinterval
in other
.split(','):
130 self
.timeranges
.append(Timerange(timeinterval
.strip()))
134 return ''#str(self.__dict__)
137 #By default, daterange are correct
138 def is_correct(self
):
142 def get_month_id(cls
, month
):
144 return Daterange
.months
[month
]
147 get_month_id
= classmethod(get_month_id
)
151 def get_month_by_id(cls
, id):
153 for key
in Daterange
.months
:
154 if id == Daterange
.months
[key
]:
157 get_month_by_id
= classmethod(get_month_by_id
)
160 def get_weekday_id(cls
, weekday
):
162 return Daterange
.weekdays
[weekday
]
165 get_weekday_id
= classmethod(get_weekday_id
)
168 def get_weekday_by_id(cls
, id):
170 for key
in Daterange
.weekdays
:
171 if id == Daterange
.weekdays
[key
]:
174 get_weekday_by_id
= classmethod(get_weekday_by_id
)
178 def get_start_and_end_time(self
, ref
=None):
179 print "Not implemented"
182 def is_time_valid(self
, t
):
183 if self
.is_time_day_valid(t
):
184 for tr
in self
.timeranges
:
185 if tr
.is_time_valid(t
):
190 def get_min_sec_from_morning(self
):
192 for tr
in self
.timeranges
:
193 mins
.append(tr
.get_sec_from_morning())
197 def get_min_sec_out_from_morning(self
):
199 for tr
in self
.timeranges
:
200 mins
.append(tr
.get_first_sec_out_from_morning())
204 def get_min_from_t(self
, t
):
205 if self
.is_time_valid(t
):
207 t_day_epoch
= get_day(t
)
208 tr_mins
= self
.get_min_sec_from_morning()
209 return t_day_epoch
+ tr_mins
212 def is_time_day_valid(self
, t
):
213 (start_time
, end_time
) = self
.get_start_and_end_time(t
)
214 # print "My class", self.__class__
215 # print "Search for t", time.asctime(time.localtime(start_time))
216 # print "Start time, endtime", time.asctime(time.localtime(start_time)), time.asctime(time.localtime(end_time))
217 if start_time
<= t
<= end_time
:
223 def is_time_day_invalid(self
, t
):
224 (start_time
, end_time
) = self
.get_start_and_end_time(t
)
225 if start_time
<= t
<= end_time
:
231 #def have_future_tiremange_valid(self, t):
233 # for tr in self.timeranges:
234 # tr_start = tr.hstart * 3600 + tr.mstart * 3600
235 # if tr_start >= sec_from_morning:
240 def get_next_future_timerange_valid(self
, t
):
241 sec_from_morning
= get_sec_from_morning(t
)
243 for tr
in self
.timeranges
:
244 tr_start
= tr
.hstart
* 3600 + tr
.mstart
* 60
245 if tr_start
>= sec_from_morning
:
246 starts
.append(tr_start
)
253 def get_next_future_timerange_invalid(self
, t
):
254 #print 'Call for get_next_future_timerange_invalid from ', time.asctime(time.localtime(t))
255 sec_from_morning
= get_sec_from_morning(t
)
256 #print 'sec from morning', sec_from_morning
258 for tr
in self
.timeranges
:
259 tr_start
= tr
.hstart
* 3600 + tr
.mstart
* 60
260 if tr_start
>= sec_from_morning
:
261 ends
.append(tr_start
)
262 tr_end
= tr
.hend
* 3600 + tr
.mend
* 60
263 if tr_end
>= sec_from_morning
:
266 #Remove the last second of the day for 00->24h"
275 def get_next_valid_day(self
, t
):
276 if self
.get_next_future_timerange_valid(t
) is None:
277 #this day is finish, we check for next period
278 (start_time
, end_time
) = self
.get_start_and_end_time(get_day(t
)+86400)
280 (start_time
, end_time
) = self
.get_start_and_end_time(t
)
282 #print self.__class__
283 #print "Get next valid day start/end for", time.asctime(time.localtime(t))
284 #print "Start", time.asctime(time.localtime(start_time))
285 #print "End", time.asctime(time.localtime(end_time))
288 return get_day(start_time
)
290 if self
.is_time_day_valid(t
):
296 def get_next_valid_time_from_t(self
, t
):
297 #print "DR Get next valid from:", time.asctime(time.localtime(t))
298 #print "DR Get next valid from:", t
299 if self
.is_time_valid(t
):
302 #print "DR Get next valid from:", time.asctime(time.localtime(t))
303 #First we search fot the day of t
304 t_day
= self
.get_next_valid_day(t
)
305 sec_from_morning
= self
.get_min_sec_from_morning()
307 #print "Search for t", time.asctime(time.localtime(t))
308 #print "DR: next day", time.asctime(time.localtime(t_day))
309 #print "DR: sec from morning", time.asctime(time.localtime(sec_from_morning))
311 #We search for the min of all tr.start > sec_from_morning
313 for tr
in self
.timeranges
:
314 tr_start
= tr
.hstart
* 3600 + tr
.mstart
* 3600
315 if tr_start
>= sec_from_morning
:
316 starts
.append(tr_start
)
318 #tr can't be valid, or it will be return at the begining
319 sec_from_morning
= self
.get_next_future_timerange_valid(t
)
320 #print "DR: sec from morning", time.asctime(time.localtime(sec_from_morning))
321 #print "Sec from morning", t_day
322 if sec_from_morning
is not None:
323 if t_day
is not None and sec_from_morning
is not None:
324 return t_day
+ sec_from_morning
326 #Then we search for the next day of t
327 #The sec will be the min of the day
329 t_day2
= self
.get_next_valid_day(t
)
330 sec_from_morning
= self
.get_next_future_timerange_valid(t_day2
)
331 if t_day2
is not None and sec_from_morning
is not None:
332 return t_day2
+ sec_from_morning
334 #I'm not find any valid time
338 def get_next_invalid_day(self
, t
):
339 #print 'DR: get_next_invalid_day for', time.asctime(time.localtime(t))
340 if self
.is_time_day_invalid(t
):
343 next_future_timerange_invalid
= self
.get_next_future_timerange_invalid(t
)
344 #print "next_future_timerange_invalid:", next_future_timerange_invalid
346 #If today there is no more unavalable timerange, search the next day
347 if next_future_timerange_invalid
is None:
348 #print 'DR: get_next_future_timerange_invalid is None'
349 #this day is finish, we check for next period
350 (start_time
, end_time
) = self
.get_start_and_end_time(get_day(t
)+86400)
352 #print 'DR: get_next_future_timerange_invalid is', time.asctime(time.localtime(next_future_timerange_invalid))
353 (start_time
, end_time
) = self
.get_start_and_end_time(t
)
354 #res = get_day(t) + next_future_timerange_invalid
355 #print "Early return"
358 #print 'DR:Start:', time.asctime(time.localtime(start_time))
359 #print 'DR:End:', time.asctime(time.localtime(end_time))
360 #The next invalid day can be t day if there a possible
361 #invalid time range (timerange is not 00->24
362 if next_future_timerange_invalid
is not None:
363 if start_time
<= t
<= end_time
:
364 #print "Early Return next invalid day:", time.asctime(time.localtime(get_day(t)))
367 return get_day(start_time
)
368 else:#Else, there is no possibility than in our start_time<->end_time we got
369 #any invalid time (full period out). So it's end_time+1 sec (tomorow of end_time)
370 #print "Full period out, got end_time", time.asctime(time.localtime(get_day(end_time +1)))
371 return get_day(end_time
+1)
376 def get_next_invalid_time_from_t(self
, t
):
377 #print 'DR:get_next_invalid_time_from_t', time.asctime(time.localtime(t))
378 if not self
.is_time_valid(t
):
379 #print "DR: cool, t is invalid", time.asctime(time.localtime(t))
382 # print "DR: Arg, t is valid", time.asctime(time.localtime(t))
384 #First we search fot the day of t
385 t_day
= self
.get_next_invalid_day(t
)
386 #print "Get next invalid day:", time.asctime(time.localtime(t_day))
387 #print "Is valid day?", self.is_time_valid(t_day)
389 #We search for the min of all tr.start > sec_from_morning
391 #for tr in self.timeranges:
392 # tr_start = tr.hstart * 3600 + tr.mstart * 3600
393 # if tr_start >= sec_from_morning:
394 # starts.append(tr_start)
396 #tr can't be valid, or it will be return at the begining
397 sec_from_morning
= self
.get_next_future_timerange_invalid(t
)
398 #print "TOTO sec_from_morning:", sec_from_morning
399 #Ok we've got a next invalid day and a invalid possibility in
400 #timerange, so the next invalid is this day+sec_from_morning
401 #print "T_day", t_day, "Sec from morning", sec_from_morning
402 if t_day
is not None and sec_from_morning
is not None:
403 return t_day
+ sec_from_morning
+ 1
405 #We've got a day but no sec_from_morning : the timerange is full (0->24h)
406 #so the next invalid is this day at the day_start
407 if t_day
is not None and sec_from_morning
is None:
410 #Then we search for the next day of t
411 #The sec will be the min of the day
413 t_day2
= self
.get_next_invalid_day(t
)
414 sec_from_morning
= self
.get_next_future_timerange_invalid(t_day2
)
415 if t_day2
is not None and sec_from_morning
is not None:
416 return t_day2
+ sec_from_morning
+ 1
418 if t_day2
is not None and sec_from_morning
is None:
421 #I'm not find any valid time
427 #ex: 2007-01-01 - 2008-02-01
428 class CalendarDaterange(Daterange
):
429 def get_start_and_end_time(self
, ref
=None):
430 start_time
= get_start_of_day(self
.syear
, int(self
.smon
), self
.smday
)
431 end_time
= get_end_of_day(self
.eyear
, int(self
.emon
), self
.emday
)
432 return (start_time
, end_time
)
436 #Like tuesday 00:00-24:00
437 class StandardDaterange(Daterange
):
438 def __init__(self
, day
, other
):
442 for timeinterval
in other
.split(','):
443 self
.timeranges
.append(Timerange(timeinterval
.strip()))
447 #It's correct only if the weekday (sunday, etc) is a valid one
448 def is_correct(self
):
449 b
= self
.day
in Daterange
.weekdays
451 print "Error : %s is not a valid day" % self
.day
455 def get_start_and_end_time(self
, ref
=None):
456 now
= time
.localtime(ref
)
457 self
.syear
= now
.tm_year
458 self
.month
= now
.tm_mon
459 #month_start_id = now.tm_mon
460 #month_start = Daterange.get_month_by_id(month_start_id)
461 self
.wday
= now
.tm_wday
462 day_id
= Daterange
.get_weekday_id(self
.day
)
463 today_morning
= get_start_of_day(now
.tm_year
, now
.tm_mon
, now
.tm_mday
)
464 tonight
= get_end_of_day(now
.tm_year
, now
.tm_mon
, now
.tm_mday
)
465 day_diff
= (day_id
- now
.tm_wday
) % 7
466 return (today_morning
+ day_diff
*86400, tonight
+ day_diff
*86400)
469 class MonthWeekDayDaterange(Daterange
):
470 #It's correct only if the weekday (sunday, etc) is a valid one
471 def is_correct(self
):
473 b
&= self
.swday
in Daterange
.weekdays
475 print "Error : %s is not a valid day" % self
.swday
477 b
&= self
.ewday
in Daterange
.weekdays
479 print "Error : %s is not a valid day" % self
.ewday
484 def get_start_and_end_time(self
, ref
=None):
485 now
= time
.localtime(ref
)
488 self
.syear
= now
.tm_year
489 month_id
= Daterange
.get_month_id(self
.smon
)
490 day_start
= find_day_by_weekday_offset(self
.syear
, self
.smon
, self
.swday
, self
.swday_offset
)
491 start_time
= get_start_of_day(self
.syear
, month_id
, day_start
)
494 self
.eyear
= now
.tm_year
495 month_end_id
= Daterange
.get_month_id(self
.emon
)
496 day_end
= find_day_by_weekday_offset(self
.eyear
, self
.emon
, self
.ewday
, self
.ewday_offset
)
497 end_time
= get_end_of_day(self
.eyear
, month_end_id
, day_end
)
499 now_epoch
= time
.mktime(now
)
500 if start_time
> end_time
: #the period is between years
501 if now_epoch
> end_time
:#check for next year
502 day_end
= find_day_by_weekday_offset(self
.eyear
+ 1, self
.emon
, self
.ewday
, self
.ewday_offset
)
503 end_time
= get_end_of_day(self
.eyear
+ 1, month_end_id
, day_end
)
504 else:#it s just that start was the last year
505 day_start
= find_day_by_weekday_offset(self
.syear
- 1, self
.smon
, self
.swday
, self
.swday_offset
)
506 start_time
= get_start_of_day(self
.syear
- 1, month_id
, day_start
)
508 if now_epoch
> end_time
:#just have to check for next year if necessery
509 day_start
= find_day_by_weekday_offset(self
.syear
+ 1, self
.smon
, self
.swday
, self
.swday_offset
)
510 start_time
= get_start_of_day(self
.syear
+ 1, month_id
, day_start
)
511 day_end
= find_day_by_weekday_offset(self
.eyear
+ 1, self
.emon
, self
.ewday
, self
.ewday_offset
)
512 end_time
= get_end_of_day(self
.eyear
+ 1, month_end_id
, day_end
)
514 return (start_time
, end_time
)
518 class MonthDateDaterange(Daterange
):
519 def get_start_and_end_time(self
, ref
=None):
520 now
= time
.localtime(ref
)
522 self
.syear
= now
.tm_year
523 month_start_id
= Daterange
.get_month_id(self
.smon
)
524 day_start
= find_day_by_offset(self
.syear
, self
.smon
, self
.smday
)
525 start_time
= get_start_of_day(self
.syear
, month_start_id
, day_start
)
528 self
.eyear
= now
.tm_year
529 month_end_id
= Daterange
.get_month_id(self
.emon
)
530 day_end
= find_day_by_offset(self
.eyear
, self
.emon
, self
.emday
)
531 end_time
= get_end_of_day(self
.eyear
, month_end_id
, day_end
)
533 now_epoch
= time
.mktime(now
)
534 if start_time
> end_time
: #the period is between years
535 if now_epoch
> end_time
:#check for next year
536 day_end
= find_day_by_offset(self
.eyear
+ 1, self
.emon
, self
.emday
)
537 end_time
= get_end_of_day(self
.eyear
+ 1, month_end_id
, day_end
)
538 else:#it s just that start was the last year
539 day_start
= find_day_by_offset(self
.syear
-1, self
.smon
, self
.emday
)
540 start_time
= get_start_of_day(self
.syear
-1, month_start_id
, day_start
)
542 if now_epoch
> end_time
:#just have to check for next year if necessery
543 day_start
= find_day_by_offset(self
.syear
+1, self
.smon
, self
.emday
)
544 start_time
= get_start_of_day(self
.syear
+1, month_start_id
, day_start
)
545 day_end
= find_day_by_offset(self
.eyear
+1, self
.emon
, self
.emday
)
546 end_time
= get_end_of_day(self
.eyear
+1, month_end_id
, day_end
)
548 return (start_time
, end_time
)
551 class WeekDayDaterange(Daterange
):
552 def get_start_and_end_time(self
, ref
=None):
553 now
= time
.localtime(ref
)
555 #If no year, it's our year
557 self
.syear
= now
.tm_year
558 month_start_id
= now
.tm_mon
559 month_start
= Daterange
.get_month_by_id(month_start_id
)
560 day_start
= find_day_by_weekday_offset(self
.syear
, month_start
, self
.swday
, self
.swday_offset
)
561 start_time
= get_start_of_day(self
.syear
, month_start_id
, day_start
)
565 self
.eyear
= now
.tm_year
566 month_end_id
= now
.tm_mon
567 month_end
= Daterange
.get_month_by_id(month_end_id
)
568 day_end
= find_day_by_weekday_offset(self
.eyear
, month_end
, self
.ewday
, self
.ewday_offset
)
569 end_time
= get_end_of_day(self
.eyear
, month_end_id
, day_end
)
571 #Maybe end_time is before start. So look for the
573 if start_time
> end_time
:
574 month_end_id
= month_end_id
+ 1
575 if month_end_id
> 12:
578 month_end
= Daterange
.get_month_by_id(month_end_id
)
579 day_end
= find_day_by_weekday_offset(self
.eyear
, month_end
, self
.ewday
, self
.ewday_offset
)
580 end_time
= get_end_of_day(self
.eyear
, month_end_id
, day_end
)
582 now_epoch
= time
.mktime(now
)
583 #But maybe we look not ethouth far. We should add a month
584 if end_time
< now_epoch
:
585 month_end_id
= month_end_id
+ 1
586 month_start_id
= month_start_id
+ 1
587 if month_end_id
> 12:
590 if month_start_id
> 12:
594 month_start
= Daterange
.get_month_by_id(month_start_id
)
595 day_start
= find_day_by_weekday_offset(self
.syear
, month_start
, self
.swday
, self
.swday_offset
)
596 start_time
= get_start_of_day(self
.syear
, month_start_id
, day_start
)
598 month_end
= Daterange
.get_month_by_id(month_end_id
)
599 day_end
= find_day_by_weekday_offset(self
.eyear
, month_end
, self
.ewday
, self
.ewday_offset
)
600 end_time
= get_end_of_day(self
.eyear
, month_end_id
, day_end
)
602 return (start_time
, end_time
)
605 class MonthDayDaterange(Daterange
):
606 def get_start_and_end_time(self
, ref
=None):
607 now
= time
.localtime(ref
)
609 self
.syear
= now
.tm_year
610 month_start_id
= now
.tm_mon
611 month_start
= Daterange
.get_month_by_id(month_start_id
)
612 day_start
= find_day_by_offset(self
.syear
, month_start
, self
.smday
)
613 start_time
= get_start_of_day(self
.syear
, month_start_id
, day_start
)
616 self
.eyear
= now
.tm_year
617 month_end_id
= now
.tm_mon
618 month_end
= Daterange
.get_month_by_id(month_end_id
)
619 day_end
= find_day_by_offset(self
.eyear
, month_end
, self
.emday
)
620 end_time
= get_end_of_day(self
.eyear
, month_end_id
, day_end
)
622 now_epoch
= time
.mktime(now
)
624 if start_time
> end_time
:
625 month_end_id
= month_end_id
+ 1
626 if month_end_id
> 12:
629 day_end
= find_day_by_offset(self
.eyear
, month_end
, self
.emday
)
630 end_time
= get_end_of_day(self
.eyear
, month_end_id
, day_end
)
632 if end_time
< now_epoch
:
633 month_end_id
= month_end_id
+ 1
634 month_start_id
= month_start_id
+ 1
635 if month_end_id
> 12:
638 if month_start_id
> 12:
643 month_start
= Daterange
.get_month_by_id(month_start_id
)
644 day_start
= find_day_by_offset(self
.syear
, month_start
, self
.smday
)
645 start_time
= get_start_of_day(self
.syear
, month_start_id
, day_start
)
648 month_end
= Daterange
.get_month_by_id(month_end_id
)
649 day_end
= find_day_by_offset(self
.eyear
, month_end
, self
.emday
)
650 end_time
= get_end_of_day(self
.eyear
, month_end_id
, day_end
)
652 return (start_time
, end_time
)