From 848512cec70480caf3b6650c2bafa7fa42325552 Mon Sep 17 00:00:00 2001 From: Steve Kemp Date: Mon, 24 Jun 2013 07:06:09 +0100 Subject: Added Custodian::Util::TimeSpan This class is used to determine whether an "hour" is within a given hour-span. e.g. To cope with: foo must run ping except between 04-06 otherwise 'alert'. This updates issue #4551. --- lib/custodian/util/timespan.rb | 93 +++++++++++++++++++++++++ t/test-custodian-util-timespan.rb | 143 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 236 insertions(+) create mode 100755 lib/custodian/util/timespan.rb create mode 100755 t/test-custodian-util-timespan.rb diff --git a/lib/custodian/util/timespan.rb b/lib/custodian/util/timespan.rb new file mode 100755 index 0000000..7d45e44 --- /dev/null +++ b/lib/custodian/util/timespan.rb @@ -0,0 +1,93 @@ +#!/usr/bin/ruby1.8 + +module Custodian + + module Util + + # + # A class for working with time-spans. + # + class TimeSpan + + # + # Given a starting hour such as 10pm and and ending hour such as 4am + # see if the current hour is inside that range. + # + def TimeSpan.inside?( p_start, p_end, cur_hour = nil) + + # + # If we don't have an hour specified then use the current one. + # + if ( cur_hour.nil? ) + cur_hour = Time.now.hour + end + + # + # Convert "XXPM" to appropriate 24-hour based integers + # + if ( ( p_start.kind_of? String ) && ( p_start =~ /([0-9]+)pm$/i ) ) + p_start = $1.dup.to_i + 12; + end + if ( ( p_end.kind_of? String ) && ( p_end =~ /([0-9]+)pm$/i ) ) + p_end = $1.dup.to_i + 12; + end + if ( ( cur_hour.kind_of? String ) && ( cur_hour =~ /([0-9]+)pm$/i ) ) + cur_hour = $1.dup.to_i + 12; + end + + # + # If we have AM suffixes then strip them + # + if ( p_start.kind_of? String ) + p_start = p_start.sub( /am$/, '' ) + p_start = p_start.to_i + end + if ( p_end.kind_of? String ) + p_end = p_end.sub( /am$/, '' ) + p_end = p_end.to_i + end + if ( cur_hour.kind_of? String ) + cur_hour = cur_hour.sub( /am$/, '' ) + cur_hour = cur_hour.to_i + end + + + # + # Ensure we're now left with integer values. + # + raise ArgumentError, "Integer required for start time" unless( p_start.kind_of? Integer ) + raise ArgumentError, "Integer required for end time" unless( p_end.kind_of? Integer ) + raise ArgumentError, "Integer required for current hour" unless( cur_hour.kind_of? Integer ) + + # + # Ensure the values have appropriate bounds. + # + raise ArgumentError, "Invalid start time" unless( ( p_start >= 0 ) && ( p_start <= 23 ) ) + raise ArgumentError, "Invalid end time" unless( ( p_end >= 0 ) && ( p_end <= 23 ) ) + raise ArgumentError, "Invalid current time" unless( ( cur_hour >= 0 ) && ( cur_hour <= 23 ) ) + + # + # Valid hours, within the span + # + valid = {} + + # + # Iterate over the hours. Store in a hash. + # + hour = p_start + while( hour != p_end ) + valid[hour] = 1 + hour += 1 + hour = 0 if ( hour >= 23 ) + end + valid[p_end]=1 + + # now do the test. + ( valid[cur_hour] == 1 ) + end + + end + end +end + +puts Custodian::Util::TimeSpan.inside?( "11pm", "2am", 14 ) diff --git a/t/test-custodian-util-timespan.rb b/t/test-custodian-util-timespan.rb new file mode 100755 index 0000000..200b8e1 --- /dev/null +++ b/t/test-custodian-util-timespan.rb @@ -0,0 +1,143 @@ +#!/usr/bin/ruby1.8 -I./lib/ -I../lib/ + + +require 'custodian/util/timespan' +require 'test/unit' + + + +# +# Unit test for our time-span code. +# +class TestTimeSpanUtil < Test::Unit::TestCase + + # + # Create the test suite environment: NOP. + # + def setup + end + + # + # Destroy the test suite environment: NOP. + # + def teardown + end + + # + # Ensure we received errors if the start/end hours are under/over 24 + # + def test_excessive_hours + + # + # Valid hours are 0-23, inclusive. Test outside that range. + # + for i in 24..100 + assert_raise ArgumentError do + result = Custodian::Util::TimeSpan.inside?( i, 2 ) + end + + assert_raise ArgumentError do + result = Custodian::Util::TimeSpan.inside?( 1, i ) + end + end + + # + # Now negative values. + # + for i in 1..50 + assert_raise ArgumentError do + result = Custodian::Util::TimeSpan.inside?( 1, ( -1 * i ) ) + end + assert_raise ArgumentError do + result = Custodian::Util::TimeSpan.inside?( ( -1 * i ), 1 ) + end + end + + end + + + # + # Test simple cases where the period is positive. + # + def test_simple_cases + # 8am-5pm + assert(Custodian::Util::TimeSpan.inside?( "8am", "5am", 12 )) + assert(Custodian::Util::TimeSpan.inside?( 8, 17, 12 )) + + end + + # + # Test cases which involve the wrap-around over midnight. + # + def test_midnight_cases + + # 9pm-2am + assert(Custodian::Util::TimeSpan.inside?( "9pm", "2am", 22 )) + assert(Custodian::Util::TimeSpan.inside?( "9pm", "2am", "10pm" )) + assert(Custodian::Util::TimeSpan.inside?( 21, 2, 22 )) + assert(Custodian::Util::TimeSpan.inside?( 21, 2, "10pm" )) + + # 10pm-3am + assert(Custodian::Util::TimeSpan.inside?( "10pm", "3am", 22 )) + assert(Custodian::Util::TimeSpan.inside?( 22, 3, 22 )) + assert(Custodian::Util::TimeSpan.inside?( 22, 3, 22 )) + assert(Custodian::Util::TimeSpan.inside?( 22, 3, "10pm" )) + + # 11pm-5am + assert(Custodian::Util::TimeSpan.inside?( "11pm", "5am", 23 )) + assert(Custodian::Util::TimeSpan.inside?( 23, 5, 23 )) + assert(Custodian::Util::TimeSpan.inside?( "11pm", "5am", "11pm" )) + + # midnight-3am + assert( Custodian::Util::TimeSpan.inside?( "0", "3am", 1 )) + assert( Custodian::Util::TimeSpan.inside?( "0", "3am", "1am" )) + end + + + # + # The time-spans listed are inclusive. + # + # Test the boundaries. + # + def test_inclusive + + open = "4pm" + close = "6pm" + + # The hours + the middle should be inside + assert( Custodian::Util::TimeSpan.inside?( open, close, 16 ) ) + assert( Custodian::Util::TimeSpan.inside?( open, close, "4pm" ) ) + + assert( Custodian::Util::TimeSpan.inside?( open, close, 17 ) ) + assert( Custodian::Util::TimeSpan.inside?( open, close, "5pm" ) ) + + assert( Custodian::Util::TimeSpan.inside?( open, close, 18 ) ) + assert( Custodian::Util::TimeSpan.inside?( open, close, "6pm" ) ) + + + # + # The preceeding + successive hours shouldn't be. + # + assert( ! Custodian::Util::TimeSpan.inside?( open, close, 15 ) ) + assert( ! Custodian::Util::TimeSpan.inside?( open, close, 19 ) ) + + # + # That is true for the string-versions too + # + assert( ! Custodian::Util::TimeSpan.inside?( open, close, "3pm" ) ) + assert( ! Custodian::Util::TimeSpan.inside?( open, close, "7pm" ) ) + + + # + # Random hours should be outside too. + # + assert( ! Custodian::Util::TimeSpan.inside?( open, close, 3 ) ) + assert( ! Custodian::Util::TimeSpan.inside?( open, close, "3am" ) ) + assert( ! Custodian::Util::TimeSpan.inside?( open, close, 7 ) ) + assert( ! Custodian::Util::TimeSpan.inside?( open, close, "7am" ) ) + assert( ! Custodian::Util::TimeSpan.inside?( open, close, 9 ) ) + assert( ! Custodian::Util::TimeSpan.inside?( open, close, "9am" ) ) + + end + +end -- cgit v1.2.1