Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??

Hello dear monks, fair and foul

I have a monitoring alert gateway. It must invoke different behaviours in working hours and out of hours. Sometimes it may be asked to forward 1000/sec. Rather than run a test for in/out of hours on each event, I decided to have a flag for ooh, and a record of the epoch time when the next change will happen. This way, most of the time I just check the flag and its validity. Only a couple of times a day, do I need to work out if its in hours, weekend, bank holiday, etc.

This script builds a closure which knows the bank hols and working hours. When it is invoked with an epoch time, it will return a flag for out of hours, and the epoch time until which this is valid.

There is a minor inefficiency when Monday is a holiday. At the end of Friday it will return Mondays start time as the limit of validity for the OOH flag. This is not a big problem, as when it is called then to find the next validity point, it will see it is in a bank holiday and act accordingly

All suggestions/comments/improvements/missed corner cases welcome

Update

soonix++ for spotting a problem. There were timezone issues with the bank holiday code (see following thread). This version should have fixed them, the original version is left below. I have also made working days a parameter, I am aware we don't all work mon..fri

Update 2

I have also added correction for when the valid until time falls across a daylight savings time boundary.

#!/usr/bin/perl use strict; use warnings; use POSIX qw(mktime); use Data::Dumper; use 5.014; sub ooh { my $start = shift; my $end = shift; my %wkwk = map {$_ => 1} @_; my @start = reverse split /:/, $start; # deliciously my @end = reverse split /:/, $end; # naughty my $i; my %mns=map{$_=>$i++}qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct No +v Dec); # Bank holidays # We assume they all begin at 00:00:00 and encompas the entire day # POSIX::mktime(15, 28, 06, 27, 02, 206);' my @bh; my $today = join ":", reverse +(localtime time)[3 .. 5]; while (<DATA>) { # read bank holiday file next unless /\d\d?\s+\w\w\w\s+\d{4}/; chomp; my ($day, $month, $year) = split; my $bh_start = join ":", $year-1900, $mns{$month}, $day; next if $bh_start lt $today; # history push @bh, $bh_start; } @bh = sort @bh; return sub { my $epoch = shift; my ($s, $m, $h, $date, $mnth, $yr, $day) = +(localtime $epoch) +[0 .. 6]; # Check if we are out of hours, and when next flip is due my ($valid, $ooh); if ("$yr:$mnth:$date" eq $bh[0]) { # hooray! bank holiday shift @bh; # rip page from calendar $ooh = 1; print "Bank Holiday "; } if (not $ooh and $wkwk{$day}) { # not a bank hol, is a working + day my $time = sprintf "%02d:%02d:%02d", $h,$m,$s; if ($time lt $start) { # not out of bed yet print "Early doors "; $valid = POSIX::mktime(@start, $date, $mnth, $yr); $ooh = 1; } elsif ($time lt $end) { # came in, dreaming of home print "Working hours "; $valid = POSIX::mktime(@end, $date, $mnth, $yr); $ooh = 0; } else { print "G'night "; } } else { print "Weekend " unless $ooh; } unless ($valid) { # we did not establish our validity limit ye +t # we are at end of day, weekend or hols. Find next working + day my $add = 1; ++$add until $wkwk{($day + $add)%7}; print "next working day is " .($day + $add)%7 . " "; $valid = POSIX::mktime(@start, $date, $mnth, $yr); $valid += $add * (24*60*60); # Daylight savings adjustment my $dst = $start[2] - +(localtime $valid)[2]; $valid += $dst * 60 * 60; $ooh = 1 } return $ooh, $valid; } } # get an ooh tester, work week mon..fri my $ooh_check = ooh('08:15:00', '17:45:00', 1..5); # and run some tests for (sort (time, 1382310123, 1359806999, 1360306452, 1381941000, 1360016452, 1382823512)) { my ($ooh, $valid) = $ooh_check->($_); say join " - ", scalar localtime $_, $ooh, scalar localtime $valid +; } __DATA__ 02 Feb 2013 18 Oct 2013 21 Oct 2013 23 Oct 2013

This is the original, broken version

use strict; use warnings; use POSIX qw(strftime mktime); use Data::Dumper; sub ooh { my $start = shift; my $end = shift; my %wkwk = map {$_ => 1} (1..5); # Mon .. Fri my $i; my %mns = map{$_=>$i++}qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct +Nov Dec); # Bank holidays # We assume they all begin at 00:00:00 and encompas the entire day # POSIX::mktime(15, 28, 06, 27, 02, 206);' my @bh; while (<DATA>) { # read bank holiday file next unless /\d\d?\s+\w\w\w\s+\d{4}/; chomp; my ($day, $month, $year) = split; my $mnth = $mns{$month}; my $bh_start = POSIX::mktime(00, 00, 00, $day, $mnth, $year - +1900); next if $bh_start < time; # history push @bh, $bh_start; } @bh = sort @bh; return sub { my $epoch = shift; my ($s, $m, $h, $date, $mnth, $yr, $day) = +(localtime $epoch) +[0 .. 6]; print $/; print scalar localtime $epoch; # Check if we are out of hours, and when next flip is due my $valid; my $ooh; if ($epoch > $bh[0]) { # its a new bank holiday shift @bh; # rip this page from the calendar $ooh = 1; print " Bank Holiday"; } if (not $ooh and $wkwk{$day}) { # not a bank hol, is a working + day my $time = sprintf "%02d:%02d:%02d", $h,$m,$s; print " given: $time "; if ($time lt $start) { # not out of bed yet print "early doors "; ($h, $m, $s) = split /:/, $start; $valid = POSIX::mktime($s, $m, $h, $date, $mnth, $yr); return 1, $valid; } elsif ($time lt $end) { # came in, dreaming of home print "within working hours "; ($h, $m, $s) = split /:/, $end; $valid = POSIX::mktime($s, $m, $h, $date, $mnth, $yr); return 0, $valid; } else { print "g'night"; } } else { print " weekend" unless $ooh; } # find next working day my $add = 1; ++$add until $wkwk{($day + $add)%7}; print " Next working day is " .($day + $add)%7 . " "; # end of day, weekend or hols ($h, $m, $s) = split /:/, $start; $valid = POSIX::mktime($s, $m, $h, $date, $mnth, $yr); $valid+= $add * (24*60*60); return 1, $valid; } } # get an ooh tester my $ooh_check = ooh('08:15:00', '17:45:00'); # and run some tests print join " -> ", 'OOH', $ooh_check->(1382310123); print join " -> ", 'OOH', $ooh_check->(1359806999); print join " -> ", 'OOH', $ooh_check->(1360306452); print join " -> ", 'OOH', $ooh_check->(1381941000); print join " -> ", 'OOH', $ooh_check->(1360016452); print join " -> ", 'OOH', $ooh_check->(time); __DATA__ 01 Jan 2012 02 Feb 2013 03 Feb 2014 04 Aug 2013 21 Oct 2013

Cheers,
R.

Pereant, qui ante nos nostra dixerunt!

In reply to efficient determination of in/out of hours including Bank Holidays by Random_Walk

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (2)
As of 2024-04-25 05:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found