#!/usr/bin/perl ######################################################## # # # How to compare sets of date ranges for overlaps # use strict; use warnings; use lib qw(../lib); use Data::Range::Compare::Stream::Iterator::Array; use Data::Range::Compare::Stream::Iterator::Compare::Asc; my @link_a_down=( #YYYY-MM-DD-HH-mm-SS YYYY-MM-DD HH:mm:SS '2012-01-01 23:34:55 2012-01-02 23:39:16', '2012-01-03 09:34:25 2012-01-03 09:38:25', '2012-01-15 13:34:25 2012-01-16 09:38:24', '2012-01-18 14:34:25 2012-01-18 16:38:35', '2012-01-19 04:34:25 2012-01-20 06:31:15', '2012-01-28 04:34:25 2012-01-28 04:47:02', ); my @link_b_down=( #YYYY-MM-DD-HH-mm-SS YYYY-MM-DD HH:mm:SS '2012-01-01 23:34:55 2012-01-02 23:34:56', '2012-01-03 09:36:25 2012-01-03 09:37:01', '2012-01-15 13:35:40 2012-01-16 01:07:36', '2012-01-28 04:34:25 2012-01-28 04:47:02', ); my @link_c_down=( #YYYY-MM-DD-HH-mm-SS YYYY-MM-DD HH:mm:SS '2012-01-01 23:34:55 2012-01-02 23:34:56', '2012-01-03 09:34:25 2012-01-03 09:38:25', '2012-01-15 13:34:25 2012-01-16 09:38:24', '2012-01-18 14:34:25 2012-01-18 16:38:35', '2012-01-19 04:34:25 2012-01-20 06:31:15', '2012-01-28 04:34:25 2012-01-28 04:46:01', ); my $cmp=new Data::Range::Compare::Stream::Iterator::Compare::Asc(factory_instance=>'Data::Range::Compare::Stream::DateTime'); my $link_count=0; { foreach my $src (\(@link_a_down,@link_b_down,@link_c_down)) { ++$link_count; my $outages=Data::Range::Compare::Stream::Iterator::Array->new; foreach my $string (@$src) { $outages->add_range(parse_outage($string)); } $outages->set_sorted(1); my $id=$cmp->add_consolidator($outages); } } sub parse_outage { my ($line)=@_; my @data=($line=~ /(\d+)/g); # Y M D H m S Y M D H m S my @args; while(my @values=splice(@data,0,6)) { my %args; @args{qw(year month day hour minute second )}=@values; my $date=DateTime->new(%args); push @args,$date; } return Data::Range::Compare::Stream::DateTime->new(@args); } sub format_downtime { my ($time)=@_; # used to help format our outage strings my $day=60 * 60 * 24; my $hour=60 * 60; my $min=60; my $mod_day=$time % $day; my $days=($time - $mod_day)/$day; my $mod_hour=$mod_day % $hour; my $hours=($mod_day - $mod_hour)/$hour; my $mod_min=$mod_hour % $min; my $mins=($mod_hour - $mod_min)/$min; my $seconds=$mod_min; sprintf('days="%02d" hours="%02d" min="%02d" sec="%02d"',$days,$hours,$mins,$seconds); } # use to track our downtime my $total_downtime=0; my @link_downtime; while($cmp->has_next) { my $result=$cmp->get_next; next if $result->is_empty; my $links_down=$result->get_overlap_count; foreach my $id (@{$result->get_overlap_ids}) { $link_downtime[$id] +=$result->get_column_by_id($id)->duration_in_seconds; } print $result; if($links_down==$link_count) { print " Outage: ",format_downtime($result->get_common->duration_in_seconds),"\n"; $total_downtime +=$result->get_common->duration_in_seconds; } else { my $links_up=$link_count - $links_down; print " Redundant links online: $links_up/$link_count Location is online\n"; } } my @link_map=(qw( LINK_A LINK_B LINK_C )); print "Total downtime: ",format_downtime($total_downtime),"\n"; for(my $id=0; $id < scalar(@link_map);++$id) { print "Downtime for $link_map[$id] ",format_downtime($link_downtime[$id]),"\n"; } { package Data::Range::Compare::Stream::DateTime; use strict; use warnings; use DateTime; use lib qw(../lib); use base qw(Data::Range::Compare::Stream); use overload '""'=>\&to_string, fallback=>1; # # Define the class internals will use when creating a new object instance(s) use constant NEW_FROM_CLASS=>'Data::Range::Compare::Stream::DateTime'; use constant TIME_FORMAT=>'%Y-%m-%d %H:%M:%S'; sub cmp_values ($$) { my ($self,$value_a,$value_b)=@_; return DateTime->compare($value_a,$value_b); } sub add_one ($) { my ($self,$value)=@_; return $value->clone->add(seconds=>1); } sub sub_one ($) { my ($self,$value)=@_; return $value->clone->subtract(seconds=>1); } sub range_start_to_string { my ($self)=@_; return $self->range_start->strftime($self->TIME_FORMAT); } sub range_end_to_string { my ($self)=@_; return $self->range_end->strftime($self->TIME_FORMAT); } sub duration_in_seconds { my ($self)=@_; 1 + $self->range_end->epoch - $self->range_start->epoch; } sub to_string { my ($self)=@_; $self->range_start_to_string.' - '.$self->range_end_to_string; } 1; }