1 <?php
defined('SYSPATH') OR die('No direct access allowed.');
4 * A model used by reports to lookup when report times and timeperiods start
5 * and stop. This sounds real easy, until you start to look at nagios'
6 * less trivial timeperiod definitions.
8 * You should call the instance() method to get a timeperiod instance,
9 * which might be shared with other users. This class will call instance()
10 * itself, so exceptions to exceptions work.
12 class Old_Timeperiod_Model
extends Model
14 /** Please do not touch this outside of tests, it's a cache for performance purposes */
15 static public $precreated = array();
16 /** Definition of the regular (weekday) timeperiod definitions */
17 protected $period = false;
19 public $start_time = false;
21 public $end_time = false;
23 public $tp_exceptions = array(); /**< Timeperiod exceptions */
24 public $tp_excludes = array(); /**< Timeperiod excludes */
26 const DATERANGE_CALENDAR_DATE
= 0; /**< eg: 2001-01-01 - 2010-11-21 / 3 (specific complete calendar date) */
27 const DATERANGE_MONTH_DATE
= 1; /**< eg: july 4 - november 15 / 3 (specific month) */
28 const DATERANGE_MONTH_DAY
= 2; /**< eg: day 1 - 25 / 5 (generic month) */
29 const DATERANGE_MONTH_WEEK_DAY
= 3; /**< eg: thursday 1 april - tuesday 2 may / 2 (specific month) */
30 const DATERANGE_WEEK_DAY
= 4; /**< eg: thursday 3 - monday 4 (generic month) */
31 const DATERANGE_TYPES
= 5; /**< FIXME: incomprehensible magic */
34 * Return an instance from a Report_options object
35 * @param $options Report_options class, or possibly an array mock
37 public static function instance($options) {
38 $key = $options['rpttimeperiod'].$options['start_time'].$options['end_time'];
39 if (isset(self
::$precreated[$key]))
40 return self
::$precreated[$key];
41 $obj = new self($options);
42 self
::$precreated[$key] = $obj;
47 * Warning: you should not use this directly - see instance()
49 public function __construct($options) {
50 $this->start_time
= $options['start_time'];
51 $this->end_time
= $options['end_time'];
52 if (!$options['rpttimeperiod'])
54 $result = self
::get($options['rpttimeperiod'], true);
57 $this->set_timeperiod_data($result);
61 * Setup the data to base the timeperiod on
63 * You really shouldn't be using this, unless you're a test and want to mock a timeperiod
65 * @param $period A timeperiod db result
66 * @return true on success, false otherwise
68 public function set_timeperiod_data($period=NULL)
70 $valid_weekdays = reports
::$valid_weekdays;
73 $this->period
= false;
77 $this->period
= array();
80 unset($period['timeperiod_name']);
81 unset($period['alias']);
82 unset($period['instance_id']);
87 foreach ($includes as $k => $v) {
91 $errors +
= $this->set_timeperiod_variable($k, $v) === false;
94 if(!empty($period['excludes']))
96 foreach($period['excludes'] as $exclude)
98 $this->tp_excludes
[] = Old_Timeperiod_Model
::instance(array('start_time' => $this->start_time
, 'end_time' => $this->end_time
, 'rpttimeperiod' => $exclude));
108 * Returns the number of active seconds "inside"
109 * the timeperiod during the start -> stop interval
110 * @param $start: A timestamp in the unix epoch notation
111 * @param $stop: A timestamp in the unix epoch notation
112 * @return The number of seconds included in both $stop - $start
113 * and the timeperiod set for this report as an integer
114 * in the unix epoch notation
116 public function active_time($start, $stop) {
117 # if no timeperiod is set, the entire duration is active
118 if ($this->period
=== false)
119 return $stop - $start;
121 # a timeperiod without entries will cause us to never
122 # find start or stop. otoh, it never has any active time,
123 # so we simply return 0
127 $nstart = $this->tp_next($start, 'start');
128 # if there is no start event inside the timeperiod, or the
129 # first ever $nstart is beyond our $stop parameter,
130 # there are no active seconds between start and stop, so
132 if (!$nstart ||
$nstart >= $stop)
135 $nstop = $this->tp_next($nstart, 'stop');
136 # If the first ever $nstop encountered is beyond our
137 # $stop parameter, we can return early, as we won't
138 # need to loop at all
140 return $stop - $nstart;
142 $active = $nstop - $nstart;
143 while ($nstart != 0) {
144 if (($nstart = $this->tp_next($nstop, 'start')) > $stop)
146 if (($nstop = $this->tp_next($nstart, 'stop')) > $stop)
149 $active +
= $nstop - $nstart;
150 if ($nstart >= $stop ||
$nstop >= $stop)
154 # we ran out of time periods before we reached $stop, so let's
155 # show 'em what we've got, so far
160 * Finds next start or stop of timeperiod from a given timestamp. If
161 * given time is in an inactive timeperiod and we're looking for a
162 * stop, current time is returned.
163 * Vice versa, if we're looking for start inside an active period,
164 * the current timestamp is returned.
166 * @param $when Current timestamp to start from.
167 * @param $what Whether to search for start or stop. Valid values are 'start' and 'stop'.
168 * @return The timestamp
170 public function tp_next($when, $what = 'start')
172 if ($this->period
=== false)
175 # if there is a report timeperiod set that doesn't have
176 # any 'start' entry (ie, all days are empty, such as for
177 # the "none" timeperiod), we can't possibly find either
178 # start or stop, so we can break out early.
179 # No one sane will want to take a report from such a timeperiod,
180 # but in case the user misclicks, we should behave properly.
181 if (empty($this->period
) && empty($this->tp_exceptions
)){
185 if ($what === 'start') {
186 # try to find the next valid timestamp in this timeperiod,
187 # that is not valid in any of the exceptions.
188 # if we make it through a whole loop without $when changing, we
189 # must have found next tp start.
191 while ($main_when !== $when && $when <= $this->end_time
) {
192 $main_when = $when = $this->tp_flat_next($when, 'start');
193 foreach ($this->tp_excludes
as $exclude) {
194 $tmp_when = $exclude->tp_next($when, 'stop');
195 if ($tmp_when !== 0) # 0 => no more tp entries => ignore
199 if ($when > $this->end_time
)
203 else if ($what === 'stop') {
204 # when this timeperiod stops, or any of the excludes start, we
205 # have a stop, whatever happens first
207 $whens[] = $this->tp_flat_next($when, 'stop');
209 foreach ($this->tp_excludes
as $exclude) {
210 $whens[] = $exclude->tp_next($when, 'start');
212 $whens = array_filter($whens); // remove any 0
223 * Finds the next start or stop of timeperiod, ignoring excludes, from
224 * a given timestamp. Really just a helper for the above.
226 private function tp_flat_next($when, $what)
229 if ($what === 'stop')
232 $tm = localtime($when, true);
233 $year = $tm['tm_year'] +
1900; # stored as offsets since 1900
234 $tm_yday = $tm['tm_yday'];
235 $day = $tm['tm_wday'];
236 $day_seconds = ($tm['tm_hour'] * 3600) +
($tm['tm_min'] * 60) +
$tm['tm_sec'];
238 $midnight_to_when = $when - $day_seconds;
240 # see if we have an exception first
241 if (!empty($this->tp_exceptions
[$year][$tm_yday]))
242 $ents = $this->tp_exceptions
[$year][$tm_yday];
243 # if not, look for regular weekday
244 elseif (!empty($this->period
[$day]))
245 $ents = $this->period
[$day];
246 # we have no entries today, so if we're looking for something outside
247 # a timeperiod, everything is.
248 elseif ($what === 'stop')
251 if ($what === 'start') {
252 foreach ($ents as $ent) {
253 if ($ent['start'] <= $day_seconds && $ent['stop'] > $day_seconds)
256 if ($ent['start'] > $day_seconds)
257 return $midnight_to_when +
$ent['start'];
260 foreach ($ents as $ent) {
261 if ($ent['start'] > $day_seconds)
263 if ($ent['start'] <= $day_seconds && $ent['stop'] > $day_seconds)
264 return $midnight_to_when +
$ent['stop'];
270 for ($day = $orig_day +
1; $when +
($loops * 86400) < $this->end_time
; $day++
) {
276 $midnight_to_when +
= 86400;
278 if (!empty($this->tp_exceptions
[$year][$tm_yday +
$loops]))
279 $ents = $this->tp_exceptions
[$year][$tm_yday +
$loops];
280 elseif (!empty($this->period
[$day]))
281 $ents = $this->period
[$day];
283 # no exceptions, no timeperiod entry
287 foreach ($ents as $ent)
288 return $midnight_to_when +
$ent[$what];
295 * Returns whether the given timestamp is inside timeperiod
296 * @param $timestamp: A timestamp in the unix epoch notation
297 * @return TRUE if the timestamp is inside the timeperiod, FALSE otherwise
299 public function inside($timestamp) {
300 return ($this->tp_next($timestamp, 'start') === $timestamp);
304 * Resolve timeperiods, both the actual timeperiods and the exclusions
306 public function resolve_timeperiods()
308 if ($this->start_time
=== false ||
$this->end_time
=== false) {
309 throw new Exception("Timeperiods cannot be resolved unless report start and end time is set");
312 if ($this->end_time
< $this->start_time
) {
313 throw new Exception("Report time set to end before start");
316 $start_year = date('Y', $this->start_time
);
317 $end_year = date('Y', $this->end_time
);
319 if (!isset($this->tp_exceptions
['unresolved']))
322 for($day_time = $this->start_time
; $day_time <= $this->end_time
; $day_time +
= 86400)
324 $check_exception = true;
326 $day_shift = strtotime(date("Y-m-d", $day_time));
327 $day = date('z', $day_time);
328 $day_year = date('Y', $day_time);
329 $day_month = date('n', $day_time);
331 for($i=0,$n=count($this->tp_exceptions
['unresolved']) ; $i<$n ; $i++
)
333 $x =& $this->tp_exceptions
['unresolved'][$i];
335 if($x['syear'] > date('Y', $this->end_time
))
339 # find out if there is an exception during this day
342 case self
::DATERANGE_CALENDAR_DATE
:/* eg: 2008-12-25 */
343 # set fields: syear, smon, smday, eyear, emon, emday, skip_interval
345 $exp_start = mktime(0,0,0, $x['smon'], $x['smday'], $x['syear']);
347 # unspecified end date - two possibilities
348 if(self
::is_daterange_single_day($x))
349 $exp_end = $exp_start;
351 $exp_end = mktime(0,0,0, $x['emon'], $x['emday'], $x['eyear']);
354 case self
::DATERANGE_MONTH_DATE
:
355 /* eg: july 4 (specific month) */
356 # set fields: smon, emon, smday, emday
358 $exp_start = self
::calculate_time_from_day_of_month($start_year, $x['smon'], $x['smday']);
359 $exp_end = self
::calculate_time_from_day_of_month($end_year, $x['emon'], $x['emday']);
361 # XXX: can *both* be zero here?
362 if($exp_end < $exp_start)
364 $x['eyear'] = $day_year +
1;
365 $exp_end = self
::calculate_time_from_day_of_month($x['eyear'], $x['emon'], $x['emday']);
370 echo "php is broken. goodie....\n";
371 if($x['emday'] < 0) {
372 $check_exception = false;
375 $exp_end = self
::calculate_time_from_day_of_month($x['eyear'], $x['emon'], -1);
379 assert($exp_end != 0);
382 case self
::DATERANGE_MONTH_DAY
:
383 /* eg: day 21 (generic month) */
384 # set field: smday, emday
385 $exp_start = self
::calculate_time_from_day_of_month($day_year, $day_month, $x['smday']);
386 $exp_end = self
::calculate_time_from_day_of_month($day_year, $day_month, $x['emday']);
388 # get midnight at end of day
392 case self
::DATERANGE_MONTH_WEEK_DAY
:/* eg: 3rd thursday (specific month) */
393 # set field: smon, swday, swday_offset, emon, ewday, ewday_offset, skip_interval
394 $exp_start = self
::calculate_time_from_weekday_of_month($start_year, $x['smon'], $x['swday'], $x['swday_offset']);
395 $exp_end = self
::calculate_time_from_weekday_of_month($end_year, $x['emon'], $x['ewday'], $x['ewday_offset']);
398 case self
::DATERANGE_WEEK_DAY
:
399 # eg: 3rd thursday (generic month)
400 # set fields: swday, swday_offset, ewday, ewday_offset, skip_interval
401 $exp_start = self
::calculate_time_from_weekday_of_month($day_year, $day_month, $x['swday'], $x['swday_offset']);
402 $exp_end = self
::calculate_time_from_weekday_of_month($day_year, $day_month, $x['ewday'], $x['ewday_offset']);
406 # This day might be totally uninteresting, in which case
408 if($x['skip_interval'] > 1) {
409 $days_since_start = ($day_time - $exp_start) / 86400;
410 $check_exception = !($days_since_start %
$x['skip_interval']);
413 # day shift is "midnight, today", $exp_start and $exp_end are always whole days
414 if (!$check_exception ||
$exp_start < $day_shift ||
$exp_end > $day_shift)
417 if(!isset($this->tp_exceptions
[$day_year]))
418 $this->tp_exceptions
[$day_year] = array();
420 if(!isset($this->tp_exceptions
[$day_year][$day]))
421 $this->tp_exceptions
[$day_year][$day] = array();
423 # if so, merge timeranges with existing for this day
424 $this->tp_exceptions
[$day_year][$day] = self
::merge_timerange_sets($this->tp_exceptions
[$day_year][$day], $x['timeranges']);
427 unset($this->tp_exceptions
['unresolved']);
431 * Parses a timerange string
432 * FIXME: add more validation
434 * @return An array of timeranges
436 * $str="08:00-12:00,13:00-17:00" gives:
439 * array('start' => 28800, 'stop' => 43200),
440 * array('start' => 46800, 'stop' => 61200)
443 protected function tp_parse_day($str)
451 $ents = explode(',', $str);
452 foreach ($ents as $ent) {
453 $start_stop = explode('-', $ent);
454 $start_hour_minute = explode(':', $start_stop[0]);
455 $start_hour = $start_hour_minute[0];
456 $start_minute = $start_hour_minute[1];
457 $stop_hour_minute = explode(':', $start_stop[1]);
458 $stop_hour = $stop_hour_minute[0];
459 $stop_minute = $stop_hour_minute[1];
460 $stop_hour_minute = $start_hour_minute = $stop = $start = false;
461 $start_second = ($start_hour * 3600) +
($start_minute * 60);
462 $stop_second = ($stop_hour * 3600) +
($stop_minute * 60);
463 if($start_second >= $stop_second)
465 # @@@FIXME: no print statements in models!
466 print "Error: Skipping timerange $str, stop time is before start time<br>";
469 $ret[$i]['start'] = $start_second;
470 $ret[$i]['stop'] = $stop_second;
478 * Adds a timeperiod exception to the report.
479 * FIXME: should probably validate more
480 * @param $dateperiod_type Indicates the type of exception. Se timeperiod_class.php for valid values.
481 * @param $syear Start year
482 * @param $smon Start month
483 * @param $smday Start day of month
484 * @param $swday Start weekday
485 * @param $swday_offset Start weekday offset
486 * @param $eyear End year
487 * @param $emon End month
488 * @param $emday End day of month
489 * @param $ewday End weekday
490 * @param $ewday_offset End weekday offset
491 * @param $skip_interval Interval to skip, such as: "every 3 weeks" etc
492 * @param $timeranges Array of timeranges.
493 * Throws Exception if any parameter has bogus values.
495 protected function add_timeperiod_exception($dateperiod_type,
496 $syear, $smon, $smday, $swday, $swday_offset,
497 $eyear, $emon, $emday, $ewday, $ewday_offset,
498 $skip_interval, $timeranges)
500 $days_per_month = reports
::$days_per_month;
502 if (!isset($this->tp_exceptions
['unresolved']))
503 $this->tp_exceptions
['unresolved'] = array();
505 assert($dateperiod_type >= 0 && $dateperiod_type < self
::DATERANGE_TYPES
); # can only fail if programmer messed up
506 $timeranges = $this->tp_parse_day($timeranges);
508 $this->tp_exceptions
['unresolved'][] = array
510 'type' => $dateperiod_type,
515 'swday_offset' => $swday_offset,
520 'ewday_offset' => $ewday_offset,
521 'skip_interval' => $skip_interval,
522 'timeranges' => $timeranges,
527 * Parses given input as a nagios 3 timeperiod variable. If valid,
528 * it is added to the report.
529 * Code is derived from the nagios 3 sources (xdata/xodtemplate.c)
530 * FIXME: find better way of adding 24h to end date
532 * @param $name The timeperiod style variable we want to parse
533 * @param $value The value of the timeperiod variable
536 public function set_timeperiod_variable($name, $value)
538 $valid_weekdays = reports
::$valid_weekdays;
539 $valid_months = reports
::$valid_months;
541 $weekday_numbers = array_flip($valid_weekdays);
542 $month_numbers = array_flip($valid_months);
544 if(in_array($name, $valid_weekdays)) # add regular weekday include time
546 $this->period
[array_search($name, $valid_weekdays)] = $this->tp_parse_day($value);
550 $input = "$name $value";
552 # you could put this in one line but that will be too messy
553 $items = array_filter(sscanf($input,"%4d-%2d-%2d - %4d-%2d-%2d / %d %[0-9:, -]"));
554 if(count($items) == 8)
556 list($syear, $smon, $smday, $eyear, $emon, $emday, $skip_interval, $timeranges) = $items;
558 /* add timerange exception */
559 $this->add_timeperiod_exception(self
::DATERANGE_CALENDAR_DATE
,
560 $syear, $smon, $smday, 0, 0, $eyear, $emon, $emday, 0, 0, $skip_interval, $timeranges);
564 $items = array_filter(sscanf($input,"%4d-%2d-%2d / %d %[0-9:, -]"));
565 if(count($items) == 5)
567 list($syear,$smon, $smday, $skip_interval, $timeranges) = $items;
572 /* add timerange exception */
573 $this->add_timeperiod_exception(self
::DATERANGE_CALENDAR_DATE
,
574 $syear, $smon, $smday, 0, 0, $eyear, $emon, $emday, 0, 0, $skip_interval, $timeranges);
578 $items = array_filter(sscanf($input,"%4d-%2d-%2d - %4d-%2d-%2d %[0-9:, -]"));
579 if(count($items) == 7)
581 list($syear, $smon, $smday, $eyear, $emon, $emday, $timeranges) = $items;
583 /* add timerange exception */
584 $this->add_timeperiod_exception(self
::DATERANGE_CALENDAR_DATE
,
585 $syear, $smon, $smday, 0, 0, $eyear, $emon, $emday, 0, 0, 0, $timeranges);
589 $items=array_filter(sscanf($input,"%4d-%2d-%2d %[0-9:, -]"));
592 list($syear, $smon, $smday, $timeranges) = $items;
596 /* add timerange exception */
597 $this->add_timeperiod_exception(self
::DATERANGE_CALENDAR_DATE
,
598 $syear, $smon, $smday, 0, 0, $eyear, $emon, $emday, 0, 0, 0, $timeranges);
603 $items = array_filter(sscanf($input,"%[a-z] %d %[a-z] - %[a-z] %d %[a-z] / %d %[0-9:, -]"));
604 if(count($items) == 8)
606 list($str1, $swday_offset, $str2, $str3, $ewday_offset, $str4, $skip_interval, $timeranges) = $items;
607 /* wednesday 1 january - thursday 2 july / 3 */
609 if(in_array($str1, $valid_weekdays) &&
610 in_array($str2, $valid_months) &&
611 in_array($str3, $valid_weekdays) &&
612 in_array($str4, $valid_months))
614 $swday = $weekday_numbers[$str1];
615 $smon = $month_numbers[$str2];
616 $ewday = $weekday_numbers[$str3];
617 $emon = $month_numbers[$str4];
619 $this->add_timeperiod_exception(self
::DATERANGE_MONTH_WEEK_DAY
,
620 0, $smon, 0, $swday, $swday_offset, 0, $emon, 0, $ewday, $ewday_offset, $skip_interval, $timeranges);
626 $items = array_filter(sscanf($input,"%[a-z] %d - %[a-z] %d / %d %[0-9:, -]"));
627 if(count($items) == 6)
629 list($str1, $smday, $str2, $emday, $skip_interval, $timeranges) = $items;
630 /* february 1 - march 15 / 3 */
631 /* monday 2 - thursday 3 / 2 */
632 /* day 4 - day 6 / 2 */
633 if(in_array($str1, $valid_weekdays) && in_array($str2, $valid_weekdays))
635 /* monday 2 - thursday 3 / 2 */
636 $swday = $weekday_numbers[$str1];
637 $ewday = $weekday_numbers[$str2];
638 $swday_offset = $smday;
639 $ewday_offset = $emday;
641 /* add timeperiod exception */
642 $this->add_timeperiod_exception(self
::DATERANGE_WEEK_DAY
,
643 0, 0, 0, $swday, $swday_offset, 0, 0, 0, $ewday, $ewday_offset, $skip_interval, $timeranges);
646 elseif(in_array($str1, $valid_months) && in_array($str2, $valid_months))
648 $smon = $month_numbers[$str1];
649 $emon = $month_numbers[$str2];
650 /* february 1 - march 15 / 3 */
651 $this->add_timeperiod_exception(self
::DATERANGE_MONTH_DATE
,
652 0, $smon, $smday, 0, 0,
653 0, $emon, $emday, 0, 0, $skip_interval, $timeranges);
656 else if(!strcmp($str1,"day") && !strcmp($str2,"day"))
659 $this->add_timeperiod_exception(self
::DATERANGE_MONTH_DAY
,
660 0, 0, $smday, 0, 0, 0, 0, $emday, 0, 0, $skip_interval, $timeranges);
666 $items = array_filter(sscanf($input,"%[a-z] %d - %d / %d %[0-9:, -]"));
667 if(count($items) == 5)
669 list($str1, $smday, $emday, $skip_interval, $timeranges) = $items;
671 /* february 1 - 15 / 3 */
672 /* monday 2 - 3 / 2 */
674 if(in_array($str1, $valid_weekdays))
676 $swday = $weekday_numbers[$str1];
678 $swday_offset = $smday;
680 $ewday_offset = $emday;
681 $this->add_timeperiod_exception(self
::DATERANGE_WEEK_DAY
,
682 0, 0, 0, $swday, $swday_offset, 0, 0, 0, $ewday, $ewday_offset, $skip_interval, $timeranges);
685 else if(in_array($str1, $valid_months))
687 $smon = $month_numbers[$str1];
690 $this->add_timeperiod_exception(self
::DATERANGE_MONTH_DATE
,
691 0, $smon, $smday, 0, 0,
692 0, $emon, $emday, 0, 0, $skip_interval, $timeranges);
695 else if(!strcmp($str1, "day"))
698 $this->add_timeperiod_exception(self
::DATERANGE_MONTH_DAY
,
699 0, 0, $smday, 0, 0, 0, 0, $emday, 0, 0, $skip_interval, $timeranges);
705 $items = array_filter(sscanf($input,"%[a-z] %d %[a-z] - %[a-z] %d %[a-z] %[0-9:, -]"));
706 if(count($items) == 7)
708 list($str1, $swday_offset, $str2, $str3, $ewday_offset, $str4, $timeranges) = $items;
710 /* wednesday 1 january - thursday 2 july */
711 if(in_array($str1, $valid_weekdays) && in_array($str2, $valid_months) &&
712 in_array($str3, $valid_weekdays) && in_array($str4, $valid_months))
714 $swday = $weekday_numbers[$str1];
715 $smon = $month_numbers[$str2];
716 $ewday = $weekday_numbers[$str3];
717 $emon = $month_numbers[$str4];
718 $this->add_timeperiod_exception(self
::DATERANGE_MONTH_WEEK_DAY
,
719 0, $smon, 0, $swday, $swday_offset, 0, $emon, 0, $ewday, $ewday_offset, 0, $timeranges);
725 $items=array_filter(sscanf($input,"%[a-z] %d - %d %[0-9:, -]"));
726 if(count($items) == 4)
728 list($str1, $smday, $emday, $timeranges) = $items;
733 if(in_array($str1, $valid_weekdays))
736 $swday = $weekday_numbers[$str1];
737 $swday_offset = $smday;
738 $ewday = $weekday_numbers[$swday];
739 $ewday_offset = $emday;
740 $this->add_timeperiod_exception(self
::DATERANGE_WEEK_DAY
,
741 0, 0, 0, $swday, $swday_offset, 0, 0, 0, $ewday, $ewday_offset, 0, $timeranges);
744 else if(in_array($str1, $valid_months))
747 $smon = $month_numbers[$str1];
749 $this->add_timeperiod_exception(self
::DATERANGE_MONTH_DATE
,
750 0, $smon, $smday, 0, 0, 0, $emon, $emday, 0, 0, 0, $timeranges);
753 else if(!strcmp($str1,"day"))
756 $this->add_timeperiod_exception(self
::DATERANGE_MONTH_DAY
,
757 0, 0, $smday, 0, 0, 0, 0, $emday, 0, 0, 0, $timeranges);
763 $items = array_filter(sscanf($input,"%[a-z] %d - %[a-z] %d %[0-9:, -]"));
764 if(count($items) == 5)
766 list($str1, $smday, $str2, $emday, $timeranges) = $items;
767 /* february 1 - march 15 */
768 /* monday 2 - thursday 3 */
770 if(in_array($str1, $valid_weekdays) && in_array($str2, $valid_weekdays))
772 /* monday 2 - thursday 3 */
773 $swday = $weekday_numbers[$str1];
774 $ewday = $weekday_numbers[$str2];
775 $swday_offset = $smday;
776 $ewday_offset = $emday;
777 $this->add_timeperiod_exception(self
::DATERANGE_WEEK_DAY
,
778 0, 0, 0, $swday, $swday_offset, 0, 0, 0, $ewday, $ewday_offset, 0, $timeranges);
781 elseif(in_array($str1, $valid_months) && in_array($str2, $valid_months))
783 /* february 1 - march 15 */
784 $smon = $month_numbers[$str1];
785 $emon = $month_numbers[$str2];
786 $this->add_timeperiod_exception(self
::DATERANGE_MONTH_DATE
,
787 0, $smon, $smday, 0, 0, 0, $emon, $emday, 0, 0, 0, $timeranges);
790 else if(!strcmp($str1,"day") && !strcmp($str2,"day"))
793 $this->add_timeperiod_exception(self
::DATERANGE_MONTH_DAY
,
794 0, 0, $smday, 0, 0, 0, 0, $emday, 0, 0, 0, $timeranges);
800 $items = array_filter(sscanf($input,"%[a-z] %d%*[ \t]%[0-9:, -]"));
801 if(count($items) == 3)
803 list($str1, $smday, $timeranges) = $items;
807 if(in_array($str1, $valid_weekdays))
810 $swday = $weekday_numbers[$str1];
811 $swday_offset = $smday;
813 $ewday_offset = $swday_offset;
814 $this->add_timeperiod_exception(self
::DATERANGE_WEEK_DAY
,
815 0, 0, 0, $swday, $swday_offset, 0, 0, 0, $ewday, $ewday_offset, 0, $timeranges);
818 elseif(in_array($str1, $valid_months))
821 $smon = $month_numbers[$str1];
824 $this->add_timeperiod_exception(self
::DATERANGE_MONTH_DATE
,
825 0, $smon, $smday, 0, 0, 0, $emon, $emday, 0, 0, 0, $timeranges);
828 else if(!strcmp($str1,"day"))
832 $this->add_timeperiod_exception(self
::DATERANGE_MONTH_DAY
,
833 0, 0, $smday, 0, 0, 0, 0, $emday, 0, 0, 0, $timeranges);
839 $items = array_filter(sscanf($input,"%[a-z] %d %[a-z] %[0-9:, -]"));
840 if(count($items) == 4)
842 list($str1, $swday_offset, $str2, $timeranges) = $items;
844 /* thursday 3 february */
845 if(in_array($str1, $valid_weekdays) && in_array($str2, $valid_months))
847 $swday = $weekday_numbers[$str1];
848 $smon = $month_numbers[$str2];
851 $ewday_offset = $swday_offset;
852 $this->add_timeperiod_exception(self
::DATERANGE_MONTH_WEEK_DAY
,
853 0, $smon, 0, $swday, $swday_offset, 0, $emon, 0, $ewday, $ewday_offset, 0, $timeranges);
859 # syntactically incorrect variable
864 * Return true if both the start date and end date is the same day
866 * @param $dr A daterange
867 * @return true if condition holds
869 public function is_daterange_single_day(&$dr)
871 if($dr['syear'] != $dr['eyear'])
873 if($dr['smon'] != $dr['emon'])
875 if($dr['smday'] != $dr['emday'])
877 if($dr['swday'] != $dr['ewday'])
879 if($dr['swday_offset'] != $dr['ewday_offset'])
886 * Print the timerange $r
887 * @param $r A timerange
889 public function print_timerange(&$r)
891 print "$r[start]-$r[stop]";
895 * Converts a date into timestamp, with some extra features such as
896 * negative days of month to use days from the end of the month.
897 * As for time, 00:00:00 of the day is used.
900 * @param $month Month.
901 * @param $monthday - Day of month, can be negative.
902 * @return The resulting timestamp.
904 public function calculate_time_from_day_of_month($year, $month, $monthday)
908 /* positive day (3rd day) */
911 $midnight = mktime(0,0,0, $month, $monthday, $year);
913 /* if we rolled over to the next month, time is invalid */
914 /* assume the user's intention is to keep it in the current month */
915 if(date("n", $midnight) != $month)
917 } else {/* negative offset (last day, 3rd to last day) */
918 /* find last day in the month */
925 /* make the new time */
926 $midnight = mktime(0,0,0, $month, $day, $year);
928 } while(date("n", $midnight) != $month);
930 /* now that we know the last day, back up more */
932 /* -1 means last day of month, so add one to to make this correct - Mike Bird */
933 $d = date("d", $midnight) +
(($monthday < -30) ?
-30 : $monthday +
1);
934 $midnight = mktime(0,0,0, $month, $d, $year);
936 /* if we rolled over to the previous month, time is invalid */
937 /* assume the user's intention is to keep it in the current month */
938 if(date("n", $midnight) != $month)
946 * Nagios supports exceptions such as "third monday in november 2010" - this
947 * converts such statements to unix timestamps.
949 * @param $year The year
950 * @param $month The month number
951 * @param $weekday The weekday's numeric presentation (0=sunday, 6=saturday)
952 * @param $weekday_offset Which occurence of the weekday, can be negative
954 public function calculate_time_from_weekday_of_month($year, $month, $weekday, $weekday_offset)
957 $midnight = mktime(0,0,0, $month, 1, $year);
958 /* how many days must we advance to reach the first instance of the weekday this month? */
959 $days = $weekday - date("w", $midnight);
962 /* positive offset (3rd thursday) */
963 if($weekday_offset > 0)
965 /* how many weeks must we advance (no more than 5 possible) */
966 $weeks = ($weekday_offset > 5) ?
5 : $weekday_offset;
967 $days +
= (($weeks - 1) * 7);
969 /* make the new time */
970 $midnight = mktime(0,0,0, $month, $days +
1, $year);
971 /* if we rolled over to the next month, time is invalid */
972 /* assume the user's intention is to keep it in the current month */
973 if(date("n", $midnight) != $month)
975 } else { /* negative offset (last thursday, 3rd to last tuesday) */
976 /* find last instance of weekday in the month */
983 /* make the new time */
984 $midnight = mktime(0,0,0, $month, $days +
1, $year);
986 } while(date("n", $midnight) != $month);
988 /* now that we know the last instance of the weekday, back up more */
989 $weeks = ($weekday_offset < -5) ?
-5 : $weekday_offset;
990 $days = (($weeks +
1) * 7);
992 $midnight = mktime(0,0,0, $month, $days +
date("d", $midnight), $year);
994 /* if we rolled over to the previous month, time is invalid */
995 /* assume the user's intention is to keep it in the current month */
996 if(date("n", $midnight) != $month)
1003 * Determines if two timeranges overlap
1004 * Note: stop time equal to start time in other range is NOT considered an overlap
1006 * @param $range1 array('start'=> {timestamp}, 'stop' => {timestamp})
1007 * @param $range2 array('start'=> {timestamp}, 'stop' => {timestamp})
1008 * @param $inclusive Whether to count "straddling" periods as ovelapping,
1009 * (Eg: start1 == stop2 or start2 == stop1)
1012 public function timeranges_overlap(&$range1, &$range2, $inclusive=false)
1015 return ($range1['start'] <= $range2['stop'] && $range2['start'] <= $range1['stop']) ||
1016 ($range2['start'] <= $range1['stop'] && $range1['start'] <= $range2['stop']);
1018 return ($range1['start'] < $range2['stop'] && $range2['start'] < $range1['stop']) ||
1019 ($range2['start'] < $range1['stop'] && $range1['start'] < $range2['stop']);
1023 * Merges two timeranges into one.
1025 * Assumes timeranges actually overlap and timeranges are correct (stop time after start time)
1027 * @param $src_range array
1028 * @param $dst_range array
1031 public function merge_timeranges(&$src_range, &$dst_range)
1033 return array('start' => min($src_range['start'], $dst_range['start']),
1034 'stop' => max($src_range['stop'], $dst_range['stop']));
1038 * Add a new timerange to a set of timeranges.
1039 * If new range overlaps an existing range, the two are merged to one.
1041 * Assumes timeranges contain only valid values (eg: stop time after start time)
1042 * Assumes the timerange set does not contain overlapping periods itself
1044 * @param $range Array of range(s) to add
1045 * @param $timerange_set The timerange set to add to
1047 public function add_timerange_to_set($range, &$timerange_set)
1049 for($i=0 ; $i<count($timerange_set) ; $i++
)
1051 $testrange = $timerange_set[$i];
1052 if(self
::timeranges_overlap($range, $testrange, true)) {
1053 # if range overlaps with current item, merge them and continue
1054 $range = self
::merge_timeranges($range, $testrange);
1056 # remove the existing range, to later re-add it in the end
1057 unset($timerange_set[$i]);
1059 # to get the numerical indices back into sequence:
1060 $timerange_set = array_values($timerange_set);
1062 # Restart so we don't miss any element
1066 $timerange_set[] = $range;
1068 # recombobulate the indices
1069 $timerange_set = array_values($timerange_set);
1073 * Merge two sets of timeranges into one, with no overlapping ranges.
1074 * Assumption: The argument sets may contain overlapping timeranges,
1075 * which are wrong in principle, but we'll manage anyway.
1077 * @param $timerange_set1 (of structure array( array('start' => 1203120, 'stop' => 120399), aray('start' => 140104, 'stop') ....)
1078 * @param $timerange_set2 (of structure array( array('start' => 1203120, 'stop' => 120399), aray('start' => 140104, 'stop') ....)
1081 public function merge_timerange_sets(&$timerange_set1, &$timerange_set2)
1083 $resulting_timerange_set = array();
1085 # plan: add both ranges to third set, merging as we go along
1087 foreach($timerange_set1 as $range)
1089 self
::add_timerange_to_set($range, $resulting_timerange_set);
1092 foreach($timerange_set2 as $range)
1094 self
::add_timerange_to_set($range, $resulting_timerange_set);
1096 return $resulting_timerange_set;
1100 * Fetch info on a timeperiod
1102 * @param $period string: Timeperiod name
1103 * @return an array of the timeperiod's properties
1105 public static function get($period)
1107 $db = Database
::instance();
1108 $query = 'SELECT * FROM timeperiod ' .
1109 'WHERE timeperiod_name = ' .$db->escape($period);
1110 $res = $db->query($query);
1115 $res = $res->result(false);
1116 $res = $res->current();
1119 $query = "SELECT variable, value FROM custom_vars WHERE obj_type = 'timeperiod' AND obj_id = {$res['id']}";
1120 $exception_res = $db->query($query);
1121 foreach ($exception_res as $exception) {
1122 $res[$exception->variable
] = $exception->value
;
1125 $query = "SELECT tp.timeperiod_name FROM timeperiod tp".
1126 " JOIN timeperiod_exclude ON exclude = id ".
1127 " WHERE timeperiod = {$res['id']}";
1129 $exclude_res = $db->query($query);
1130 // it might seem appropriate to be all recursive about this,
1131 // but the recursiveness is already done on a model-level, so
1132 // we have no use for anything but name
1133 foreach ($exclude_res as $exclude) {
1134 $res['excludes'][] = $exclude->timeperiod_name
;
1140 private static $timeperiods_all = false;
1143 * Fetch all timperiods
1146 public static function get_all()
1148 if( self
::$timeperiods_all !== false )
1149 return self
::$timeperiods_all;
1151 $res = Livestatus
::instance()->getTimeperiods(array('columns'=>array('name')));
1152 foreach ($res as $row) {
1153 $result[$row['name']] = $row['name'];
1155 self
::$timeperiods_all = $result;