File Coverage

blib/lib/Time/Period.pm
Criterion Covered Total %
statement 265 265 100.0
branch 273 276 98.9
condition 169 192 88.0
subroutine 11 11 100.0
pod 0 11 0.0
total 718 755 95.1


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Time::Period - A Perl module to deal with time periods.
4              
5             =head1 SYNOPSIS
6              
7             C
8              
9             C<$result = inPeriod($time, $period);>
10              
11             =head1 DESCRIPTION
12              
13             The B function determines if a given time falls within a given
14             period. B returns B<1> if the time does fall within the given
15             period, B<0> if not, and B<-1> if B detects a malformed time or
16             period.
17              
18             The time is specified as per the C function, which is assumed to
19             be the number of non-leap seconds since January 1, 1970.
20              
21             The period is specified as a string which adheres to the format
22              
23             sub-period[, sub-period...]
24              
25             or the string "none" or whitespace. The string "none" is not case
26             sensitive.
27              
28             If the period is blank, then any time period is assumed because the time
29             period has not been restricted. In that case, B returns 1. If
30             the period is "none", then no time period applies and B returns
31             0.
32              
33             A sub-period is of the form
34              
35             scale {range [range ...]} [scale {range [range ...]}]
36              
37             Scale must be one of nine different scales (or their equivalent codes):
38              
39             Scale | Scale | Valid Range Values
40             | Code |
41             *******|*******|************************************************
42             year | yr | n where n is an integer 0<=n<=99 or n>=1970
43             month | mo | 1-12 or jan, feb, mar, apr, may, jun, jul,
44             | | aug, sep, oct, nov, dec
45             week | wk | 1-6
46             yday | yd | 1-366
47             mday | md | 1-31
48             wday | wd | 1-7 or su, mo, tu, we, th, fr, sa
49             hour | hr | 0-23 or 12am 1am-11am 12noon 12pm 1pm-11pm
50             minute | min | 0-59
51             second | sec | 0-59
52              
53             The same scale type may be specified multiple times. Additional scales
54             simply extend the range defined by previous scales of the same type.
55              
56             The range for a given scale must be a valid value in the form of
57              
58             v
59              
60             or
61              
62             v-v
63              
64             For the range specification v-v, if the first value is larger than the second
65             value (e.g. "min {20-10}"), the range wraps around unless the scale
66             specification is year.
67              
68             Year does not wrap because the year is never really reset, it just
69             increments. Ignoring that fact has lead to the dreaded year 2000
70             nightmare. When the year rolls over from 99 to 00, it has really rolled
71             over a century, not gone back a century. B supports the
72             dangerous two digit year notation because it is so rampant. However,
73             B converts the two digit notation to four digits by prepending
74             the first two digits from the current year. In the case of 99-1972, the
75             99 is translated to whatever current century it is (probably 20th), and
76             then range 99-1972 is treated as 1972-1999. If it were the 21st century,
77             then the range would be 1972-2099.
78              
79             Anyway, if v-v is 9-2 and the scale is month, September, October,
80             November, December, January, and February are the months that the range
81             specifies. If v-v is 2-9, then the valid months are February, March,
82             April, May, Jun, July, August, and September. 9-2 is the same as Sep-Feb.
83              
84             v isn't a point in time. In the context of the hour scale, 9 specifies
85             the time period from 9:00:00 am to 9:59:59 am. This is what most people
86             would call 9-10. In other words, v is discrete in its time scale.
87             9 changes to 10 when 9:59:59 changes to 10:00:00, but it is 9 from
88             9:00:00 to 9:59:59. Just before 9:00:00, v was 8.
89              
90             Note that whitespace can be anywhere and case is not important. Note
91             also that scales must be specified either in long form (year, month,
92             week, etc.) or in code form (yr, mo, wk, etc.). Scale forms may be
93             mixed in a period statement.
94              
95             Furthermore, when using letters to specify ranges, only the first two
96             for week days or the first three for months are significant. January
97             is a valid specification for jan, and Sunday is a valid specification
98             for su. Sun is also valid for su.
99              
100             =head2 PERIOD EXAMPLES
101              
102             To specify a time period from Monday through Friday, 9am to 5pm, use a
103             period such as
104              
105             wd {Mon-Fri} hr {9am-4pm}
106              
107             When specifing a range by using -, it is best to think of - as meaning
108             through. It is 9am through 4pm, which is just before 5pm.
109              
110             To specify a time period from Monday through Friday, 9am to 5pm on
111             Monday, Wednesday, and Friday, and 9am to 3pm on Tuesday and Thursday,
112             use a period such as
113              
114             wd {Mon Wed Fri} hr {9am-4pm}, wd{Tue Thu} hr {9am-2pm}
115              
116             To specify a time period that extends Mon-Fri 9am-5pm, but alternates
117             weeks in a month, use a period such as
118              
119             wk {1 3 5} wd {Mon Wed Fri} hr {9am-4pm}
120              
121             Or how about a period that specifies winter?
122              
123             mo {Nov-Feb}
124              
125             This is equivalent to the previous example:
126              
127             mo {Jan-Feb Nov-Dec}
128              
129             As is
130              
131             mo {jan feb nov dec}
132              
133             And this is too:
134              
135             mo {Jan Feb}, mo {Nov Dec}
136              
137             Wait! So is this:
138              
139             mo {Jan Feb} mo {Nov Dec}
140              
141             To specify a period that describes every other half-hour, use something
142             like
143              
144             minute { 0-29 }
145              
146             To specify the morning, use
147              
148             hour { 12am-11am }
149              
150             Remember, 11am is not 11:00:00am, but rather 11:00:00am - 11:59:59am.
151              
152             Hmmmm, 5 second blocks could be a fun period...
153              
154             sec {0-4 10-14 20-24 30-34 40-44 50-54}
155              
156             To specify every first half-hour on alternating week days, and the second
157             half-hour the rest of the week, use the period
158              
159             wd {1 3 5 7} min {0-29}, wd {2 4 6} min {30-59}
160              
161             =head1 VERSION
162              
163             1.24
164              
165             =head1 HISTORY
166              
167             Version 1.24
168             ------------
169             - Minor doc update.
170              
171             Version 1.23
172             ------------
173             - Bug fixes:
174             - Validate min and max for right side of hour ranges (e.g.
175             hr { 20-25 } now correctly returns -1)
176             - Range for yd is now 1 to 366
177             - Years are no longer considered to be 365 days long for
178             calculating a 4-digit year.
179              
180             Version 1.22
181             ------------
182             - Fixed tests
183              
184             Version 1.21
185             ------------
186             - Bug fix: Stopped using $' and $`.
187              
188             Version 1.20
189             ------------
190             - Added the ability to specify no time period.
191              
192             Version 1.13
193             ------------
194             - Cleaned up the error checking code.
195              
196             Version 1.12
197             ------------
198             - Updated email and web space information.
199              
200             Version 1.11
201             ------------
202             - Minor bug fix in 1.10.
203              
204             Version 1.10
205             ------------
206             - Released.
207              
208             =head1 AUTHOR
209              
210             Patrick Ryan wrote it.
211              
212             Paul Boyd fixed a few bugs.
213              
214             =head1 COPYRIGHT
215              
216             Copyright (c) 1997 Patrick Ryan. All rights reserved. This Perl module
217             uses the conditions given by Perl. This module may only be distributed
218             and or modified under the conditions given by Perl.
219              
220             =cut
221              
222             package Time::Period;
223              
224             require 5.001;
225             require Exporter;
226             @ISA = qw(Exporter);
227             @EXPORT = qw(inPeriod);
228              
229             $VERSION = "1.24";
230              
231             sub inPeriod {
232              
233 209     209 0 3522 my($time, $period) = @_[0,1];
234 209         264 my(%scaleCode, %scaleCodeV, $result, $i, $lb, $rb, @subPeriods, $subPeriod,
235             @scales, %scaleResults, $rangeData, @ranges, $range, $v1, $v2);
236 209         399 local($scale, $yr, $mo, $wk, $yd, $md, $wd, $hr, $min, $sec);
237              
238             # $scale, $yr, $mo, $wk, $yd, $md, $wd, $hr, $min, and $sec are declared
239             # with local() because they are referenced symbolically.
240              
241              
242             # Test $period and $time for validity. Return -1 if $time contains
243             # non-numbers or is null. Return 1 if $time is numeric but $period is all
244             # whitespace. No period means all times are within the period because
245             # period is not restricted. Return 0 if $period is "none". Also make
246             # $period all lowercase.
247              
248 209         1073 $time =~ s/^\s*(.*)/$1/;
249 209         912 $time =~ s/\s*$//;
250 209 100 100     1204 return -1 if ( ($time =~ /\D/) || ($time eq "") );
251              
252 207 100       407 return 1 if (!defined($period));
253 206         688 $period =~ s/^\s*(.*)/$1/;
254 206         869 $period =~ s/\s*$//;
255 206         355 $period = lc($period);
256 206 100       436 return 1 if ($period eq "");
257              
258 205 100       348 return 0 if ($period eq "none");
259              
260             # Thise two associative arrays are used to map and validate scales.
261              
262 204         1218 %scaleCode = ('year' => 'yr', 'month' => 'mo', 'week' => 'wk', 'mday' => 'md',
263             'wday' => 'wd', 'yday' => 'yd', 'hour' => 'hr',
264             'minute' => 'min', 'second' => 'sec');
265 204         862 %scaleCodeV = ('yr' => 1, 'mo' => 1, 'wk' => 1, 'md' => 1, 'wd' => 1,
266             'yd' => 1, 'hr' => 1, 'min' => 1, 'sec' => 1);
267              
268              
269             # The names of these variables must correlate with the scale codes.
270              
271 204         373 ($yr, $mo, $wk, $yd, $md, $wd, $hr, $min, $sec) = getTimeVars($time);
272              
273              
274             # The first step is to break $period up into all its sub periods.
275              
276 204         719 @subPeriods = split(/\s*,\s*/, $period);
277              
278             # Evaluate each sub-period to see if $time falls within it. If it does
279             # then return 1, if $time does not fall within any of the sub-periods,
280             # return 0.
281              
282 204         332 foreach $subPeriod (@subPeriods) {
283              
284             # Do a validity check for braces. Make sure the number of {s equals the
285             # number of }s. If there aren't any, return -1 as well.
286              
287 204         323 $lb = $subPeriod =~ tr/{//;
288 204         316 $rb = $subPeriod =~ tr/}//;
289 204 100 100     897 return -1 if ( ($lb != $rb) || ($lb == 0) );
290              
291              
292 201         768 @scales = split(/}\s*/, $subPeriod);
293              
294             # Make sure that the number of {s are equal to the number of scales
295             # found. If it is not, return -1.
296              
297 201 50       466 return -1 if ($lb != @scales);
298              
299              
300             # Evaluate each scale, one by one, in the sub-period. Once this
301             # completes, there will be a hash called %scaleResults which will contain
302             # boolean values. The key to this hash will be the code version of
303             # each scale in @scales, if it was a valid scale. If an invalid string
304             # is found, -1 will be returned. The boolean value will indicate
305             # whether $time falls within the particular scale in question.
306              
307 201         347 foreach $scale (@scales) {
308 203 100       2745 return -1 if ($scale !~ /^([a-z]*)\s*{\s*(.*)/);
309 202         435 $scale = $1;
310 202         335 $rangeData = $2;
311              
312             # Check to see if $scale is a valid scale. If it is, make sure
313             # it is in code form.
314              
315             # Is it possibly the long form?
316 202 100       553 if (length($scale) > 3) {
    100          
317             # If it doesn't map to a code...
318 8 100       38 return -1 if (!defined($scaleCode{$scale}));
319 7         16 $scale = $scaleCode{$scale};
320             # Okay, it's not longer than 3 characters, is it 2 or 3 characters long?
321             } elsif (length($scale) > 1) {
322             # Is it an invalid code?
323 193 100       450 return -1 if (!defined($scaleCodeV{$scale}));
324             # It must be zero or one character long, which is an invalid scale.
325             } else {
326 1         9 return -1;
327             }
328              
329             # $scale is a valid scale and it is now in code form.
330              
331             # Erase any whitespace between any "v - v"s so that they become "v-v".
332 199         1777 $rangeData =~ s/(\w+)\s*-\s*(\w+)/$1-$2/g;
333              
334 199         452 @ranges = split(/\s+/, $rangeData);
335              
336 199 50       615 $scaleResults{$scale} = 0 if (!defined($scaleResults{$scale}));
337              
338             # Alright, $range is one of the ranges (could be the only one) for
339             # $scale. If $range is valid within the context of $scale and $time,
340             # set $scaleResults{$scale} to 1.
341              
342 199         275 foreach $range (@ranges) {
343              
344 198 100       576 if ($range =~ /(.*)-(.*)/) {
345 127         242 $v1 = $1;
346 127         174 $v2 = $2;
347 127 100 66     816 return -1 if ($v1 !~ /\w/ || $v2 !~ /\w/);
348             } else {
349 71 100       236 return -1 if ($range !~ /\w/);
350             }
351              
352             # This line calls the function named by $scale and feeds it the
353             # variable $range and the variable named by $scale.
354              
355 195         622 $result = &$scale($range, $$scale);
356              
357 195 100       1090 return -1 if ($result == -1);
358 117 100       456 $scaleResults{$scale} = 1 if ($result == 1);
359             }
360             }
361              
362             # Now, there is a boolean value associated with each scale. If every
363             # scale is 1, then $time falls within this sub-period, which means $time
364             # falls within the period, so return 1. If that condition isn't met,
365             # then the loop will test other sub-periods, if they exist, and return 0
366             # if none of them cover $time.
367              
368 116         149 $i = 1;
369 116         267 foreach $scale (keys %scaleResults) {
370 118 100       336 $i = 0 if ($scaleResults{$scale} == 0);
371             }
372              
373             # This is a sub-period where the time falls into all of the scales
374             # specified.
375 116 100       891 return 1 if ($i == 1);
376              
377             # Reset scale for a new sub-period.
378 38         93 %scaleResults = ();
379             }
380              
381             # $time didn't fall into any of the sub-periods. :(
382              
383 38         390 return 0;
384             }
385              
386             sub getTimeVars {
387             # This function takes $time (seconds past 0000 Jan 1, 1970) and returns
388             # it in component form. Specifically, this function returns
389             # ($year, $month, $week, $yday, $mday, $wday, $hour, $minute, $second).
390              
391 204     204 0 358 my($time) = $_[0];
392 204         202 my($sec, $min, $hr, $md, $mo, $yr, $wd, $yd, @pwd, @wd, $wk, $i);
393              
394              
395             # Now, break $time into $yr, $mo, $wk, $md, $wd, $yd, $hr, $min, and $sec.
396              
397 204         5101 ($sec, $min, $hr, $md, $mo, $yr, $wd, $yd) = localtime($time);
398              
399             # The assumption for the ranges from localtime are
400             # Year ($yr) = 0-99
401             # Month ($mo) = 0-11
402             # Year Day ($yd) = 0-365
403             # Month Day ($md) = 1-31
404             # Week Day ($wd) = 0-6
405             # Hour ($hr) = 0-23
406             # Minute ($min) = 0-59
407             # Second ($sec) = 0-59
408              
409             # Calculate the full year (yyyy).
410 204         363 $yr += 1900;
411              
412             # Figure out which week $time is in ($wk) so that $wk goes from 0-5.
413              
414             # Set up an array where a week day maps to the previous week day.
415 204         423 @pwd = (6, 0, 1, 2, 3, 4, 5);
416              
417             # Define an array @wd from 1 to $md that maps $md to its corresponding
418             # day of the week.
419              
420 204         293 $wd[$md] = $wd;
421 204         518 for ($i = $md - 1; $i >= 0; $i--) {
422 1131         2350 $wd[$i] = $pwd[$wd[$i+1]];
423             }
424              
425              
426             # Calculate which week it is.
427              
428 204         258 $wk = 0;
429              
430 204         418 for ($i = 1; $i <= $md; $i++) {
431             # Itterate $i from 1 to $md. If $i happens to land on a Sunday,
432             # increment $wk unless $i is also 1, which means its still week 0.
433 1131 100 66     3224 if ( (0 == $wd[$i]) && (1 != $md) ) {
434 130         255 $wk++;
435             }
436             }
437              
438 204         933 return ($yr, $mo, $wk, $yd, $md, $wd, $hr, $min, $sec);
439             }
440              
441             sub yr {
442             # A function to determine if a given range is within a given year.
443             # Returns 1 if it is, 0 if not, and -1 if the supplied range is invalid.
444              
445 17     17 0 32 my($range, $yr) = @_[0,1];
446 17         20 my($v1, $v2);
447              
448 17 100       48 if ($range =~ /(.*)-(.*)/) {
449 9         15 $v1 = $1;
450 9         12 $v2 = $2;
451 9 100 100     52 return -1 if ( ($v1 =~ /\D/) || ($v2 =~ /\D/) );
452 7 50 33     32 return -1 if ( ($v1 < 0) || ($v2 < 0) );
453 7 100 100     35 return -1 if ( ($v1 > 99) && ($v1 < 1970) );
454 6 100 100     25 return -1 if ( ($v2 > 99) && ($v2 < 1970) );
455 5 100       14 $v1 = (100 * substr($yr, 0, 2) + $v1) if ($v1 <= 99);
456 5 100       11 $v2 = (100 * substr($yr, 0, 2) + $v2) if ($v2 <= 99);
457 5 100       11 if ($v1 > $v2) {
458 1         3 $i = $v2;
459 1         3 $v2 = $v1;
460 1         2 $v1 = $i;
461             }
462              
463 5 100 100     42 return 1 if ( ($v1 <= $yr) && ($yr <= $v2) );
464             } else {
465 8 100 66     74 return -1 if ( ($range =~ /\D/) || ($range < 0) ||
      100        
      66        
466             ( ($range > 99) && ($range < 1970) ) );
467 6 100       15 $range = (100 * substr($yr, 0, 2) + $range) if ($range <= 99);
468              
469 6 100       33 return 1 if ($range == $yr);
470             }
471              
472 4         9 return 0;
473             }
474              
475             sub mo {
476             # A function to determine if a given range is within a given month.
477             # Returns 1 if it is, 0 if not, and -1 if the supplied range is invalid.
478              
479 27     27 0 36 my($range, $mo) = @_[0,1];
480 27         27 my(%mo, %moV, $v1, $v2);
481              
482             # These associative arrays are used to validate months and to map the
483             # letter designations to their numeric equivalents.
484              
485 27         147 %mo = ('jan' => 0, 'feb' => 1, 'mar' => 2, 'apr' => 3, 'may' => 4,
486             'jun' => 5, 'jul' => 6, 'aug' => 7, 'sep' => 8, 'oct' => 9,
487             'nov' => 10, 'dec' => 11);
488 27         118 %moV = ('jan' => 1, 'feb' => 1, 'mar' => 1, 'apr' => 1, 'may' => 1,
489             'jun' => 1, 'jul' => 1, 'aug' => 1, 'sep' => 1, 'oct' => 1,
490             'nov' => 1, 'dec' => 1);
491              
492 27 100       120 if ($range =~ /(.*)-(.*)/) {
493 19         29 $v1 = $1;
494 19         22 $v2 = $2;
495 19 100       48 if ($v1 =~ /[a-z]/) {
    100          
496 12         17 $v1 = substr($v1, 0, 3);
497 12 100       29 return -1 if (!defined($moV{$v1}));
498 11         12 $v1 = $mo{$v1};
499             } elsif ($v1 =~ /\D/) {
500 1         5 return -1;
501             } else {
502 6         8 $v1--;
503 6 100 100     35 return -1 if ( ($v1 < 0) || ($v1 > 11) );
504             }
505 15 100       34 if ($v2 =~ /[a-z]/) {
    100          
506 11         15 $v2 = substr($v2, 0, 3);
507 11 100       24 return -1 if (!defined($moV{$v2}));
508 10         9 $v2 = $mo{$v2};
509             } elsif ($v2 =~ /\D/) {
510 1         5 return -1;
511             } else {
512 3         4 $v2--;
513 3 100 100     18 return -1 if ( ($v2 < 0) || ($v2 > 11) );
514             }
515 11 100       18 if ($v1 > $v2) {
516 6 100 66     41 return 1 if ( ($v1 <= $mo) || ($v2 >= $mo) );
517             } else {
518 5 100 66     26 return 1 if ( ($v1 <= $mo) && ($mo <= $v2) );
519             }
520             } else {
521 8 100       39 if ($range =~ /[a-z]/) {
    100          
522 4         7 $range = substr($range, 0, 3);
523 4 100       17 return -1 if (!defined($moV{$range}));
524 3         4 $range = $mo{$range};
525             } elsif ($range =~ /\D/) {
526 1         5 return -1;
527             } else {
528 3         6 $range--;
529 3 100 100     21 return -1 if ( ($range < 0) || ($range > 11) );
530             }
531 4 100       19 return 1 if ($range == $mo);
532             }
533              
534 4         15 return 0;
535             }
536              
537             sub wk {
538             # A function to determine if a given range is within a given week.
539             # Returns 1 if it is, 0 if not, and -1 if the supplied range is invalid.
540              
541 19     19 0 35 my($range, $wk) = @_[0,1];
542 19         21 my($v1, $v2);
543              
544 19 100       57 if ($range =~ /(.*)-(.*)/) {
545 12         23 $v1 = $1;
546 12         47 $v2 = $2;
547 12 100 100     72 return -1 if ( ($v1 =~ /\D/) || ($v2 =~ /\D/) );
548 10         15 $v1--;
549 10         10 $v2--;
550 10 100 100     49 return -1 if ( ($v1 < 0) || ($v1 > 5) );
551 8 100 100     36 return -1 if ( ($v2 < 0) || ($v2 > 5) );
552 6 100       13 if ($v1 > $v2) {
553 3 100 100     18 return 1 if ( ($v1 <= $wk) || ($v2 >= $wk) );
554             } else {
555 3 100 100     14 return 1 if ( ($v1 <= $wk) && ($wk <= $v2) );
556             }
557             } else {
558 7 100       20 return -1 if ($range =~ /\D/);
559 6         12 $range--;
560 6 100 100     116 return -1 if ( ($range < 0) || ($range > 5) );
561 4 100       15 return 1 if ($range == $wk);
562             }
563              
564 4         9 return 0;
565             }
566              
567             sub yd {
568             # A function to determine if a given range is within a given day of the
569             # year. Returns 1 if it is, 0 if not, and -1 if the supplied range is
570             # invalid.
571              
572 19     19 0 34 my($range, $yd) = @_[0,1];
573 19         78 my($v1, $v2);
574              
575 19 100       55 if ($range =~ /(.*)-(.*)/) {
576 12         19 $v1 = $1;
577 12         14 $v2 = $2;
578 12 100 100     58 return -1 if ( ($v1 =~ /\D/) || ($v2 =~ /\D/) );
579 10         16 $v1--;
580 10         12 $v2--;
581 10 100 100     43 return -1 if ( ($v1 < 0) || ($v1 > 365) );
582 8 100 100     31 return -1 if ( ($v2 < 0) || ($v2 > 365) );
583 6 100       11 if ($v1 > $v2) {
584 3 100 100     16 return 1 if ( ($v1 <= $yd) || ($v2 >= $yd) );
585             } else {
586 3 100 66     17 return 1 if ( ($v1 <= $yd) && ($yd <= $v2) );
587             }
588             } else {
589 7         14 $range--;
590 7 100 66     64 return -1 if (($range =~ /\D/) || ($range < 0) || ($range > 365));
      100        
591 4 100       26 return 1 if ($range == $yd);
592             }
593              
594 4         7 return 0;
595             }
596              
597             sub md {
598             # A function to determine if a given range is within a given day of the
599             # month. Returns 1 if it is, 0 if not, and -1 if the supplied range is
600             # invalid.
601              
602 18     18 0 32 my($range, $md) = @_[0,1];
603 18         13 my($v1, $v2);
604              
605 18 100       44 if ($range =~ /(.*)-(.*)/) {
606 12         17 $v1 = $1;
607 12         13 $v2 = $2;
608 12 100 100     54 return -1 if ( ($v1 =~ /\D/) || ($v2 =~ /\D/) );
609 10 100 100     40 return -1 if ( ($v1 < 1) || ($v1 > 31) );
610 8 100 100     31 return -1 if ( ($v2 < 1) || ($v2 > 31) );
611 6 100       8 if ($v1 > $v2) {
612 3 100 100     15 return 1 if ( ($v1 <= $md) || ($v2 >= $md) );
613             } else {
614 3 100 66     14 return 1 if ( ($v1 <= $md) && ($md <= $v2) );
615             }
616             } else {
617 6 100 100     44 return -1 if (($range =~ /\D/) || ($range < 1) || ($range > 31));
      100        
618 3 100       9 return 1 if ($range == $md);
619             }
620              
621 4         7 return 0;
622             }
623              
624             sub wd {
625             # A function to determine if a given range is within a given day of the
626             # week. Returns 1 if it is, 0 if not, and -1 if the supplied range is
627             # invalid.
628              
629 32     32 0 58 my($range, $wd) = @_[0,1];
630 32         34 my(%wd, %wdV, $v1, $v2);
631              
632             # These associative arrays are used to validate week days and to map the
633             # letter designations to their numeric equivalents.
634              
635 32         147 %wd = ('su' => 0, 'mo' => 1, 'tu' => 2, 'we' => 3, 'th' => 4, 'fr' => 5,
636             'sa' => 6);
637 32         100 %wdV = ('su' => 1, 'mo' => 1, 'tu' => 1, 'we' => 1, 'th' => 1, 'fr' => 1,
638             'sa' => 1);
639              
640 32 100       89 if ($range =~ /(.*)-(.*)/) {
641 19         36 $v1 = $1;
642 19         25 $v2 = $2;
643 19 100       61 if ($v1 =~ /[a-z]/) {
    100          
644 11         22 $v1 = substr($v1, 0, 2);
645 11 100       30 return -1 if (!defined($wdV{$v1}));
646 10         18 $v1 = $wd{$v1};
647             } elsif ($v1 =~ /\D/) {
648 1         5 return -1;
649             } else {
650 7         13 $v1--;
651 7 100 100     35 return -1 if ( ($v1 < 0) || ($v1 > 6) );
652             }
653 15 100       47 if ($v2 =~ /[a-z]/) {
    100          
654 11         15 $v2 = substr($v2, 0, 2);
655 11 100       29 return -1 if (!defined($wdV{$v2}));
656 10         14 $v2 = $wd{$v2};
657             } elsif ($v2 =~ /\D/) {
658 1         5 return -1;
659             } else {
660 3         4 $v2--;
661 3 100 100     19 return -1 if ( ($v2 < 0) || ($v2 > 6) );
662             }
663 11 100       19 if ($v1 > $v2) {
664 7 100 100     45 return 1 if ( ($v1 <= $wd) || ($v2 >= $wd) );
665             } else {
666 4 100 100     27 return 1 if ( ($v1 <= $wd) && ($wd <= $v2) );
667             }
668             } else {
669 13 100       54 if ($range =~ /[a-z]/) {
    100          
670 9         17 $range = substr($range, 0, 2);
671 9 100       137 return -1 if (!defined($wdV{$range}));
672 8         14 $range = $wd{$range};
673             } elsif ($range =~ /\D/) {
674 1         4 return -1;
675             } else {
676 3         6 $range--;
677 3 100 100     22 return -1 if ( ($range < 0) || ($range > 6) );
678             }
679 9 100       36 return 1 if ($range == $wd);
680             }
681              
682 5         21 return 0;
683             }
684              
685             sub hr {
686             # A function to determine if a given range is within a given hour.
687             # Returns 1 if it is, 0 if not, and -1 if the supplied range is invalid.
688              
689 33     33 0 62 my($range, $hr) = @_[0,1];
690 33         32 my($v1, $v2);
691              
692 33 100       95 if ($range =~ /(.*)-(.*)/) {
693 22         33 $v1 = $1;
694 22         28 $v2 = $2;
695 22 100       81 if ($v1 =~ /^(\d+)am$/) {
    100          
    100          
696 2 100       5 if ($1 == 12) {
697 1         2 $v1 = 0;
698             } else {
699 1         3 $v1 = $1;
700             }
701             } elsif ($v1 =~ /^(\d+)pm$/) {
702 2 100       8 if ($1 == 12) {
703 1         3 $v1 = $1;
704             } else {
705 1         2 $v1 = $1+12;
706             }
707             } elsif ($v1 =~ /^(\d+)noon$/) {
708 2 100       7 return -1 if ($1 != 12);
709 1         3 $v1 = $1;
710             }
711 21 100       74 if ($v2 =~ /^(\d+)am$/) {
    100          
    100          
712 2 100       8 if ($1 == 12) {
713 1         2 $v2 = 0;
714             } else {
715 1         2 $v2 = $1;
716             }
717             } elsif ($v2 =~ /^(\d+)pm$/) {
718 2 100       7 if ($1 == 12) {
719 1         3 $v2 = $1;
720             } else {
721 1         3 $v2 = $1+12;
722             }
723             } elsif ($v2 =~ /^(\d+)noon$/) {
724 2 100       9 return -1 if ($1 != 12);
725 1         3 $v2 = $1;
726             }
727 20 100 66     126 return -1 if ( ($v1 =~ /\D/) || ($v1 < 0) || ($v1 > 23) );
      100        
728 18 100 66     116 return -1 if ( ($v2 =~ /\D/) || ($v2 < 0) || ($v2 > 23) );
      100        
729              
730 16 100       23 if ($v1 > $v2) {
731 4 100 100     21 return 1 if ( ($v1 <= $hr) || ($v2 >= $hr) );
732             } else {
733 12 100 66     66 return 1 if ( ($v1 <= $hr) && ($hr <= $v2) );
734             }
735             } else {
736 11 100       51 if ($range =~ /^(\d+)am$/) {
    100          
    100          
737 2 100       7 if ($1 == 12) {
738 1         1 $range = 0;
739             } else {
740 1         3 $range = $1;
741             }
742             } elsif ($range =~ /^(\d+)pm$/) {
743 2 100       10 if ($1 == 12) {
744 1         2 $range = $1;
745             } else {
746 1         3 $range = $1+12;
747             }
748             } elsif ($range =~ /^(\d+)noon$/) {
749 2 100       28 return -1 if ($1 != 12);
750 1         3 $range = $1;
751             }
752 10 100 66     71 return -1 if (($range =~ /\D/) || ($range < 0) || ($range > 23));
      100        
753 8 100       42 return 1 if ($range == $hr);
754             }
755              
756 4         6 return 0;
757             }
758              
759             sub min {
760             # A function to determine if a given range is within a given minute.
761             # Returns 1 if it is, 0 if not, and -1 if the supplied range is invalid.
762              
763 15     15 0 30 my($range, $min) = @_[0,1];
764 15         10 my($v1, $v2);
765              
766 15 100       55 if ($range =~ /(.*)-(.*)/) {
767 10         14 $v1 = $1;
768 10         15 $v2 = $2;
769 10 100 100     51 return -1 if ( ($v1 =~ /\D/) || ($v2 =~ /\D/) );
770 8 100 66     37 return -1 if ( ($v1 < 0) || ($v1 > 59) );
771 7 100 66     28 return -1 if ( ($v2 < 0) || ($v2 > 59) );
772 6 100       10 if ($v1 > $v2) {
773 3 100 100     17 return 1 if ( ($v1 <= $min) || ($v2 >= $min) );
774             } else {
775 3 100 66     27 return 1 if ( ($v1 <= $min) && ($min <= $v2) );
776             }
777             } else {
778 5 100 66     44 return -1 if (($range =~ /\D/) || ($range < 0) || ($range > 59));
      100        
779 3 100       10 return 1 if ($range == $min);
780             }
781              
782 4         8 return 0;
783             }
784              
785             sub sec {
786             # A function to determine if a given range is within a given second.
787             # Returns 1 if it is, 0 if not, and -1 if the supplied range is invalid.
788              
789 15     15 0 29 my($range, $sec) = @_[0,1];
790 15         15 my($v1, $v2);
791              
792 15 100       39 if ($range =~ /(.*)-(.*)/) {
793 10         17 $v1 = $1;
794 10         13 $v2 = $2;
795 10 100 100     51 return -1 if ( ($v1 =~ /\D/) || ($v2 =~ /\D/) );
796 8 100 66     37 return -1 if ( ($v1 < 0) || ($v1 > 59) );
797 7 100 66     28 return -1 if ( ($v2 < 0) || ($v2 > 59) );
798 6 100       11 if ($v1 > $v2) {
799 3 100 100     18 return 1 if ( ($v1 <= $sec) || ($v2 >= $sec) );
800             } else {
801 3 100 66     16 return 1 if ( ($v1 <= $sec) && ($sec <= $v2) );
802             }
803             } else {
804 5 100 66     39 return -1 if (($range =~ /\D/) || ($range < 0) || ($range > 59));
      100        
805 3 100       9 return 1 if ($range == $sec);
806             }
807              
808 4         6 return 0;
809             }
810              
811             1;