File Coverage

blib/lib/Time/Period.pm
Criterion Covered Total %
statement 265 265 100.0
branch 273 276 98.9
condition 170 192 88.5
subroutine 11 11 100.0
pod 0 11 0.0
total 719 755 95.2


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.25
164              
165             =head1 HISTORY
166              
167             Version 1.25
168             ------------
169             - Fixed a bug with matching week on Sundays
170             (https://rt.cpan.org/Public/Bug/Display.html?id=100850)
171              
172             Version 1.24
173             ------------
174             - Minor doc update.
175              
176             Version 1.23
177             ------------
178             - Bug fixes:
179             - Validate min and max for right side of hour ranges (e.g.
180             hr { 20-25 } now correctly returns -1)
181             - Range for yd is now 1 to 366
182             - Years are no longer considered to be 365 days long for
183             calculating a 4-digit year.
184              
185             Version 1.22
186             ------------
187             - Fixed tests
188              
189             Version 1.21
190             ------------
191             - Bug fix: Stopped using $' and $`.
192              
193             Version 1.20
194             ------------
195             - Added the ability to specify no time period.
196              
197             Version 1.13
198             ------------
199             - Cleaned up the error checking code.
200              
201             Version 1.12
202             ------------
203             - Updated email and web space information.
204              
205             Version 1.11
206             ------------
207             - Minor bug fix in 1.10.
208              
209             Version 1.10
210             ------------
211             - Released.
212              
213             =head1 AUTHOR
214              
215             Patrick Ryan wrote it.
216              
217             Paul Boyd fixed a few bugs.
218              
219             =head1 COPYRIGHT
220              
221             Copyright (c) 1997 Patrick Ryan. All rights reserved. This Perl module
222             uses the conditions given by Perl. This module may only be distributed
223             and or modified under the conditions given by Perl.
224              
225             =cut
226              
227             package Time::Period;
228              
229             require 5.001;
230             require Exporter;
231             @ISA = qw(Exporter);
232             @EXPORT = qw(inPeriod);
233              
234             $VERSION = "1.25";
235              
236             sub inPeriod {
237              
238 211     211 0 3010 my($time, $period) = @_[0,1];
239 211         242 my(%scaleCode, %scaleCodeV, $result, $i, $lb, $rb, @subPeriods, $subPeriod,
240             @scales, %scaleResults, $rangeData, @ranges, $range, $v1, $v2);
241 211         385 local($scale, $yr, $mo, $wk, $yd, $md, $wd, $hr, $min, $sec);
242              
243             # $scale, $yr, $mo, $wk, $yd, $md, $wd, $hr, $min, and $sec are declared
244             # with local() because they are referenced symbolically.
245              
246              
247             # Test $period and $time for validity. Return -1 if $time contains
248             # non-numbers or is null. Return 1 if $time is numeric but $period is all
249             # whitespace. No period means all times are within the period because
250             # period is not restricted. Return 0 if $period is "none". Also make
251             # $period all lowercase.
252              
253 211         1352 $time =~ s/^\s*(.*)/$1/;
254 211         1313 $time =~ s/\s*$//;
255 211 100 100     1200 return -1 if ( ($time =~ /\D/) || ($time eq "") );
256              
257 209 100       413 return 1 if (!defined($period));
258 208         633 $period =~ s/^\s*(.*)/$1/;
259 208         753 $period =~ s/\s*$//;
260 208         501 $period = lc($period);
261 208 100       414 return 1 if ($period eq "");
262              
263 207 100       485 return 0 if ($period eq "none");
264              
265             # Thise two associative arrays are used to map and validate scales.
266              
267 206         1371 %scaleCode = ('year' => 'yr', 'month' => 'mo', 'week' => 'wk', 'mday' => 'md',
268             'wday' => 'wd', 'yday' => 'yd', 'hour' => 'hr',
269             'minute' => 'min', 'second' => 'sec');
270 206         837 %scaleCodeV = ('yr' => 1, 'mo' => 1, 'wk' => 1, 'md' => 1, 'wd' => 1,
271             'yd' => 1, 'hr' => 1, 'min' => 1, 'sec' => 1);
272              
273              
274             # The names of these variables must correlate with the scale codes.
275              
276 206         360 ($yr, $mo, $wk, $yd, $md, $wd, $hr, $min, $sec) = getTimeVars($time);
277              
278              
279             # The first step is to break $period up into all its sub periods.
280              
281 206         826 @subPeriods = split(/\s*,\s*/, $period);
282              
283             # Evaluate each sub-period to see if $time falls within it. If it does
284             # then return 1, if $time does not fall within any of the sub-periods,
285             # return 0.
286              
287 206         353 foreach $subPeriod (@subPeriods) {
288              
289             # Do a validity check for braces. Make sure the number of {s equals the
290             # number of }s. If there aren't any, return -1 as well.
291              
292 206         317 $lb = $subPeriod =~ tr/{//;
293 206         226 $rb = $subPeriod =~ tr/}//;
294 206 100 100     920 return -1 if ( ($lb != $rb) || ($lb == 0) );
295              
296              
297 203         852 @scales = split(/}\s*/, $subPeriod);
298              
299             # Make sure that the number of {s are equal to the number of scales
300             # found. If it is not, return -1.
301              
302 203 50       583 return -1 if ($lb != @scales);
303              
304              
305             # Evaluate each scale, one by one, in the sub-period. Once this
306             # completes, there will be a hash called %scaleResults which will contain
307             # boolean values. The key to this hash will be the code version of
308             # each scale in @scales, if it was a valid scale. If an invalid string
309             # is found, -1 will be returned. The boolean value will indicate
310             # whether $time falls within the particular scale in question.
311              
312 203         321 foreach $scale (@scales) {
313 205 100       1021 return -1 if ($scale !~ /^([a-z]*)\s*{\s*(.*)/);
314 204         483 $scale = $1;
315 204         297 $rangeData = $2;
316              
317             # Check to see if $scale is a valid scale. If it is, make sure
318             # it is in code form.
319              
320             # Is it possibly the long form?
321 204 100       569 if (length($scale) > 3) {
    100          
322             # If it doesn't map to a code...
323 8 100       41 return -1 if (!defined($scaleCode{$scale}));
324 7         12 $scale = $scaleCode{$scale};
325             # Okay, it's not longer than 3 characters, is it 2 or 3 characters long?
326             } elsif (length($scale) > 1) {
327             # Is it an invalid code?
328 195 100       454 return -1 if (!defined($scaleCodeV{$scale}));
329             # It must be zero or one character long, which is an invalid scale.
330             } else {
331 1         11 return -1;
332             }
333              
334             # $scale is a valid scale and it is now in code form.
335              
336             # Erase any whitespace between any "v - v"s so that they become "v-v".
337 201         931 $rangeData =~ s/(\w+)\s*-\s*(\w+)/$1-$2/g;
338              
339 201         433 @ranges = split(/\s+/, $rangeData);
340              
341 201 50       570 $scaleResults{$scale} = 0 if (!defined($scaleResults{$scale}));
342              
343             # Alright, $range is one of the ranges (could be the only one) for
344             # $scale. If $range is valid within the context of $scale and $time,
345             # set $scaleResults{$scale} to 1.
346              
347 201         259 foreach $range (@ranges) {
348              
349 200 100       540 if ($range =~ /(.*)-(.*)/) {
350 127         235 $v1 = $1;
351 127         154 $v2 = $2;
352 127 100 66     791 return -1 if ($v1 !~ /\w/ || $v2 !~ /\w/);
353             } else {
354 73 100       265 return -1 if ($range !~ /\w/);
355             }
356              
357             # This line calls the function named by $scale and feeds it the
358             # variable $range and the variable named by $scale.
359              
360 197         1012 $result = &$scale($range, $$scale);
361              
362 197 100       1062 return -1 if ($result == -1);
363 119 100       552 $scaleResults{$scale} = 1 if ($result == 1);
364             }
365             }
366              
367             # Now, there is a boolean value associated with each scale. If every
368             # scale is 1, then $time falls within this sub-period, which means $time
369             # falls within the period, so return 1. If that condition isn't met,
370             # then the loop will test other sub-periods, if they exist, and return 0
371             # if none of them cover $time.
372              
373 118         125 $i = 1;
374 118         268 foreach $scale (keys %scaleResults) {
375 120 100       302 $i = 0 if ($scaleResults{$scale} == 0);
376             }
377              
378             # This is a sub-period where the time falls into all of the scales
379             # specified.
380 118 100       1744 return 1 if ($i == 1);
381              
382             # Reset scale for a new sub-period.
383 38         111 %scaleResults = ();
384             }
385              
386             # $time didn't fall into any of the sub-periods. :(
387              
388 38         315 return 0;
389             }
390              
391             sub getTimeVars {
392             # This function takes $time (seconds past 0000 Jan 1, 1970) and returns
393             # it in component form. Specifically, this function returns
394             # ($year, $month, $week, $yday, $mday, $wday, $hour, $minute, $second).
395              
396 206     206 0 295 my($time) = $_[0];
397 206         199 my($sec, $min, $hr, $md, $mo, $yr, $wd, $yd, @pwd, @wd, $wk, $i);
398              
399              
400             # Now, break $time into $yr, $mo, $wk, $md, $wd, $yd, $hr, $min, and $sec.
401              
402 206         4859 ($sec, $min, $hr, $md, $mo, $yr, $wd, $yd) = localtime($time);
403              
404             # The assumption for the ranges from localtime are
405             # Year ($yr) = 0-99
406             # Month ($mo) = 0-11
407             # Year Day ($yd) = 0-365
408             # Month Day ($md) = 1-31
409             # Week Day ($wd) = 0-6
410             # Hour ($hr) = 0-23
411             # Minute ($min) = 0-59
412             # Second ($sec) = 0-59
413              
414             # Calculate the full year (yyyy).
415 206         455 $yr += 1900;
416              
417             # Figure out which week $time is in ($wk) so that $wk goes from 0-5.
418              
419             # Set up an array where a week day maps to the previous week day.
420 206         432 @pwd = (6, 0, 1, 2, 3, 4, 5);
421              
422             # Define an array @wd from 1 to $md that maps $md to its corresponding
423             # day of the week.
424              
425 206         295 $wd[$md] = $wd;
426 206         558 for ($i = $md - 1; $i >= 0; $i--) {
427 1141         1987 $wd[$i] = $pwd[$wd[$i+1]];
428             }
429              
430              
431             # Calculate which week it is.
432              
433 206         195 $wk = 0;
434              
435 206         513 for ($i = 1; $i <= $md; $i++) {
436             # Itterate $i from 1 to $md. If $i happens to land on a Sunday,
437             # increment $wk unless $i is also 1, which means its still week 0.
438 1141 100 100     2776 if ( $wd[$i] == 0 && $i != 1 ) {
439 132         198 $wk++;
440             }
441             }
442              
443 206         926 return ($yr, $mo, $wk, $yd, $md, $wd, $hr, $min, $sec);
444             }
445              
446             sub yr {
447             # A function to determine if a given range is within a given year.
448             # Returns 1 if it is, 0 if not, and -1 if the supplied range is invalid.
449              
450 17     17 0 35 my($range, $yr) = @_[0,1];
451 17         15 my($v1, $v2);
452              
453 17 100       44 if ($range =~ /(.*)-(.*)/) {
454 9         13 $v1 = $1;
455 9         9 $v2 = $2;
456 9 100 100     46 return -1 if ( ($v1 =~ /\D/) || ($v2 =~ /\D/) );
457 7 50 33     34 return -1 if ( ($v1 < 0) || ($v2 < 0) );
458 7 100 100     26 return -1 if ( ($v1 > 99) && ($v1 < 1970) );
459 6 100 100     21 return -1 if ( ($v2 > 99) && ($v2 < 1970) );
460 5 100       13 $v1 = (100 * substr($yr, 0, 2) + $v1) if ($v1 <= 99);
461 5 100       9 $v2 = (100 * substr($yr, 0, 2) + $v2) if ($v2 <= 99);
462 5 100       11 if ($v1 > $v2) {
463 1         11 $i = $v2;
464 1         2 $v2 = $v1;
465 1         1 $v1 = $i;
466             }
467              
468 5 100 100     30 return 1 if ( ($v1 <= $yr) && ($yr <= $v2) );
469             } else {
470 8 100 66     80 return -1 if ( ($range =~ /\D/) || ($range < 0) ||
      100        
      66        
471             ( ($range > 99) && ($range < 1970) ) );
472 6 100       18 $range = (100 * substr($yr, 0, 2) + $range) if ($range <= 99);
473              
474 6 100       34 return 1 if ($range == $yr);
475             }
476              
477 4         9 return 0;
478             }
479              
480             sub mo {
481             # A function to determine if a given range is within a given month.
482             # Returns 1 if it is, 0 if not, and -1 if the supplied range is invalid.
483              
484 27     27 0 57 my($range, $mo) = @_[0,1];
485 27         30 my(%mo, %moV, $v1, $v2);
486              
487             # These associative arrays are used to validate months and to map the
488             # letter designations to their numeric equivalents.
489              
490 27         178 %mo = ('jan' => 0, 'feb' => 1, 'mar' => 2, 'apr' => 3, 'may' => 4,
491             'jun' => 5, 'jul' => 6, 'aug' => 7, 'sep' => 8, 'oct' => 9,
492             'nov' => 10, 'dec' => 11);
493 27         137 %moV = ('jan' => 1, 'feb' => 1, 'mar' => 1, 'apr' => 1, 'may' => 1,
494             'jun' => 1, 'jul' => 1, 'aug' => 1, 'sep' => 1, 'oct' => 1,
495             'nov' => 1, 'dec' => 1);
496              
497 27 100       87 if ($range =~ /(.*)-(.*)/) {
498 19         36 $v1 = $1;
499 19         21 $v2 = $2;
500 19 100       60 if ($v1 =~ /[a-z]/) {
    100          
501 12         24 $v1 = substr($v1, 0, 3);
502 12 100       36 return -1 if (!defined($moV{$v1}));
503 11         16 $v1 = $mo{$v1};
504             } elsif ($v1 =~ /\D/) {
505 1         7 return -1;
506             } else {
507 6         10 $v1--;
508 6 100 100     86 return -1 if ( ($v1 < 0) || ($v1 > 11) );
509             }
510 15 100       48 if ($v2 =~ /[a-z]/) {
    100          
511 11         20 $v2 = substr($v2, 0, 3);
512 11 100       28 return -1 if (!defined($moV{$v2}));
513 10         15 $v2 = $mo{$v2};
514             } elsif ($v2 =~ /\D/) {
515 1         8 return -1;
516             } else {
517 3         5 $v2--;
518 3 100 100     27 return -1 if ( ($v2 < 0) || ($v2 > 11) );
519             }
520 11 100       18 if ($v1 > $v2) {
521 6 100 66     54 return 1 if ( ($v1 <= $mo) || ($v2 >= $mo) );
522             } else {
523 5 100 66     37 return 1 if ( ($v1 <= $mo) && ($mo <= $v2) );
524             }
525             } else {
526 8 100       46 if ($range =~ /[a-z]/) {
    100          
527 4         8 $range = substr($range, 0, 3);
528 4 100       19 return -1 if (!defined($moV{$range}));
529 3         5 $range = $mo{$range};
530             } elsif ($range =~ /\D/) {
531 1         7 return -1;
532             } else {
533 3         7 $range--;
534 3 100 100     33 return -1 if ( ($range < 0) || ($range > 11) );
535             }
536 4 100       22 return 1 if ($range == $mo);
537             }
538              
539 4         19 return 0;
540             }
541              
542             sub wk {
543             # A function to determine if a given range is within a given week.
544             # Returns 1 if it is, 0 if not, and -1 if the supplied range is invalid.
545              
546 20     20 0 35 my($range, $wk) = @_[0,1];
547 20         13 my($v1, $v2);
548              
549 20 100       41 if ($range =~ /(.*)-(.*)/) {
550 12         14 $v1 = $1;
551 12         11 $v2 = $2;
552 12 100 100     49 return -1 if ( ($v1 =~ /\D/) || ($v2 =~ /\D/) );
553 10         17 $v1--;
554 10         8 $v2--;
555 10 100 100     32 return -1 if ( ($v1 < 0) || ($v1 > 5) );
556 8 100 100     30 return -1 if ( ($v2 < 0) || ($v2 > 5) );
557 6 100       8 if ($v1 > $v2) {
558 3 100 100     14 return 1 if ( ($v1 <= $wk) || ($v2 >= $wk) );
559             } else {
560 3 100 100     12 return 1 if ( ($v1 <= $wk) && ($wk <= $v2) );
561             }
562             } else {
563 8 100       21 return -1 if ($range =~ /\D/);
564 7         10 $range--;
565 7 100 100     40 return -1 if ( ($range < 0) || ($range > 5) );
566 5 100       15 return 1 if ($range == $wk);
567             }
568              
569 4         7 return 0;
570             }
571              
572             sub yd {
573             # A function to determine if a given range is within a given day of the
574             # year. Returns 1 if it is, 0 if not, and -1 if the supplied range is
575             # invalid.
576              
577 19     19 0 30 my($range, $yd) = @_[0,1];
578 19         17 my($v1, $v2);
579              
580 19 100       39 if ($range =~ /(.*)-(.*)/) {
581 12         14 $v1 = $1;
582 12         13 $v2 = $2;
583 12 100 100     45 return -1 if ( ($v1 =~ /\D/) || ($v2 =~ /\D/) );
584 10         14 $v1--;
585 10         7 $v2--;
586 10 100 100     42 return -1 if ( ($v1 < 0) || ($v1 > 365) );
587 8 100 100     27 return -1 if ( ($v2 < 0) || ($v2 > 365) );
588 6 100       9 if ($v1 > $v2) {
589 3 100 100     14 return 1 if ( ($v1 <= $yd) || ($v2 >= $yd) );
590             } else {
591 3 100 66     44 return 1 if ( ($v1 <= $yd) && ($yd <= $v2) );
592             }
593             } else {
594 7         13 $range--;
595 7 100 66     53 return -1 if (($range =~ /\D/) || ($range < 0) || ($range > 365));
      100        
596 4 100       24 return 1 if ($range == $yd);
597             }
598              
599 4         6 return 0;
600             }
601              
602             sub md {
603             # A function to determine if a given range is within a given day of the
604             # month. Returns 1 if it is, 0 if not, and -1 if the supplied range is
605             # invalid.
606              
607 18     18 0 26 my($range, $md) = @_[0,1];
608 18         17 my($v1, $v2);
609              
610 18 100       34 if ($range =~ /(.*)-(.*)/) {
611 12         14 $v1 = $1;
612 12         10 $v2 = $2;
613 12 100 100     43 return -1 if ( ($v1 =~ /\D/) || ($v2 =~ /\D/) );
614 10 100 100     37 return -1 if ( ($v1 < 1) || ($v1 > 31) );
615 8 100 100     26 return -1 if ( ($v2 < 1) || ($v2 > 31) );
616 6 100       9 if ($v1 > $v2) {
617 3 100 100     13 return 1 if ( ($v1 <= $md) || ($v2 >= $md) );
618             } else {
619 3 100 66     12 return 1 if ( ($v1 <= $md) && ($md <= $v2) );
620             }
621             } else {
622 6 100 100     40 return -1 if (($range =~ /\D/) || ($range < 1) || ($range > 31));
      100        
623 3 100       8 return 1 if ($range == $md);
624             }
625              
626 4         4 return 0;
627             }
628              
629             sub wd {
630             # A function to determine if a given range is within a given day of the
631             # week. Returns 1 if it is, 0 if not, and -1 if the supplied range is
632             # invalid.
633              
634 33     33 0 78 my($range, $wd) = @_[0,1];
635 33         38 my(%wd, %wdV, $v1, $v2);
636              
637             # These associative arrays are used to validate week days and to map the
638             # letter designations to their numeric equivalents.
639              
640 33         327 %wd = ('su' => 0, 'mo' => 1, 'tu' => 2, 'we' => 3, 'th' => 4, 'fr' => 5,
641             'sa' => 6);
642 33         114 %wdV = ('su' => 1, 'mo' => 1, 'tu' => 1, 'we' => 1, 'th' => 1, 'fr' => 1,
643             'sa' => 1);
644              
645 33 100       124 if ($range =~ /(.*)-(.*)/) {
646 19         35 $v1 = $1;
647 19         31 $v2 = $2;
648 19 100       65 if ($v1 =~ /[a-z]/) {
    100          
649 11         26 $v1 = substr($v1, 0, 2);
650 11 100       35 return -1 if (!defined($wdV{$v1}));
651 10         15 $v1 = $wd{$v1};
652             } elsif ($v1 =~ /\D/) {
653 1         7 return -1;
654             } else {
655 7         16 $v1--;
656 7 100 100     46 return -1 if ( ($v1 < 0) || ($v1 > 6) );
657             }
658 15 100       55 if ($v2 =~ /[a-z]/) {
    100          
659 11         21 $v2 = substr($v2, 0, 2);
660 11 100       31 return -1 if (!defined($wdV{$v2}));
661 10         15 $v2 = $wd{$v2};
662             } elsif ($v2 =~ /\D/) {
663 1         5 return -1;
664             } else {
665 3         5 $v2--;
666 3 100 100     23 return -1 if ( ($v2 < 0) || ($v2 > 6) );
667             }
668 11 100       20 if ($v1 > $v2) {
669 7 100 100     53 return 1 if ( ($v1 <= $wd) || ($v2 >= $wd) );
670             } else {
671 4 100 100     35 return 1 if ( ($v1 <= $wd) && ($wd <= $v2) );
672             }
673             } else {
674 14 100       82 if ($range =~ /[a-z]/) {
    100          
675 10         28 $range = substr($range, 0, 2);
676 10 100       35 return -1 if (!defined($wdV{$range}));
677 9         16 $range = $wd{$range};
678             } elsif ($range =~ /\D/) {
679 1         6 return -1;
680             } else {
681 3         6 $range--;
682 3 100 100     23 return -1 if ( ($range < 0) || ($range > 6) );
683             }
684 10 100       58 return 1 if ($range == $wd);
685             }
686              
687 5         20 return 0;
688             }
689              
690             sub hr {
691             # A function to determine if a given range is within a given hour.
692             # Returns 1 if it is, 0 if not, and -1 if the supplied range is invalid.
693              
694 33     33 0 59 my($range, $hr) = @_[0,1];
695 33         23 my($v1, $v2);
696              
697 33 100       73 if ($range =~ /(.*)-(.*)/) {
698 22         25 $v1 = $1;
699 22         22 $v2 = $2;
700 22 100       72 if ($v1 =~ /^(\d+)am$/) {
    100          
    100          
701 2 100       8 if ($1 == 12) {
702 1         2 $v1 = 0;
703             } else {
704 1         2 $v1 = $1;
705             }
706             } elsif ($v1 =~ /^(\d+)pm$/) {
707 2 100       6 if ($1 == 12) {
708 1         2 $v1 = $1;
709             } else {
710 1         2 $v1 = $1+12;
711             }
712             } elsif ($v1 =~ /^(\d+)noon$/) {
713 2 100       10 return -1 if ($1 != 12);
714 1         3 $v1 = $1;
715             }
716 21 100       72 if ($v2 =~ /^(\d+)am$/) {
    100          
    100          
717 2 100       6 if ($1 == 12) {
718 1         3 $v2 = 0;
719             } else {
720 1         2 $v2 = $1;
721             }
722             } elsif ($v2 =~ /^(\d+)pm$/) {
723 2 100       7 if ($1 == 12) {
724 1         2 $v2 = $1;
725             } else {
726 1         2 $v2 = $1+12;
727             }
728             } elsif ($v2 =~ /^(\d+)noon$/) {
729 2 100       7 return -1 if ($1 != 12);
730 1         2 $v2 = $1;
731             }
732 20 100 66     117 return -1 if ( ($v1 =~ /\D/) || ($v1 < 0) || ($v1 > 23) );
      100        
733 18 100 66     101 return -1 if ( ($v2 =~ /\D/) || ($v2 < 0) || ($v2 > 23) );
      100        
734              
735 16 100       23 if ($v1 > $v2) {
736 4 100 100     19 return 1 if ( ($v1 <= $hr) || ($v2 >= $hr) );
737             } else {
738 12 100 66     61 return 1 if ( ($v1 <= $hr) && ($hr <= $v2) );
739             }
740             } else {
741 11 100       48 if ($range =~ /^(\d+)am$/) {
    100          
    100          
742 2 100       8 if ($1 == 12) {
743 1         2 $range = 0;
744             } else {
745 1         2 $range = $1;
746             }
747             } elsif ($range =~ /^(\d+)pm$/) {
748 2 100       10 if ($1 == 12) {
749 1         2 $range = $1;
750             } else {
751 1         3 $range = $1+12;
752             }
753             } elsif ($range =~ /^(\d+)noon$/) {
754 2 100       19 return -1 if ($1 != 12);
755 1         2 $range = $1;
756             }
757 10 100 66     73 return -1 if (($range =~ /\D/) || ($range < 0) || ($range > 23));
      100        
758 8 100       36 return 1 if ($range == $hr);
759             }
760              
761 4         6 return 0;
762             }
763              
764             sub min {
765             # A function to determine if a given range is within a given minute.
766             # Returns 1 if it is, 0 if not, and -1 if the supplied range is invalid.
767              
768 15     15 0 24 my($range, $min) = @_[0,1];
769 15         13 my($v1, $v2);
770              
771 15 100       34 if ($range =~ /(.*)-(.*)/) {
772 10         12 $v1 = $1;
773 10         10 $v2 = $2;
774 10 100 100     42 return -1 if ( ($v1 =~ /\D/) || ($v2 =~ /\D/) );
775 8 100 66     34 return -1 if ( ($v1 < 0) || ($v1 > 59) );
776 7 100 66     27 return -1 if ( ($v2 < 0) || ($v2 > 59) );
777 6 100       9 if ($v1 > $v2) {
778 3 100 100     14 return 1 if ( ($v1 <= $min) || ($v2 >= $min) );
779             } else {
780 3 100 66     14 return 1 if ( ($v1 <= $min) && ($min <= $v2) );
781             }
782             } else {
783 5 100 66     35 return -1 if (($range =~ /\D/) || ($range < 0) || ($range > 59));
      100        
784 3 100       10 return 1 if ($range == $min);
785             }
786              
787 4         758 return 0;
788             }
789              
790             sub sec {
791             # A function to determine if a given range is within a given second.
792             # Returns 1 if it is, 0 if not, and -1 if the supplied range is invalid.
793              
794 15     15 0 28 my($range, $sec) = @_[0,1];
795 15         17 my($v1, $v2);
796              
797 15 100       35 if ($range =~ /(.*)-(.*)/) {
798 10         13 $v1 = $1;
799 10         11 $v2 = $2;
800 10 100 100     45 return -1 if ( ($v1 =~ /\D/) || ($v2 =~ /\D/) );
801 8 100 66     34 return -1 if ( ($v1 < 0) || ($v1 > 59) );
802 7 100 66     28 return -1 if ( ($v2 < 0) || ($v2 > 59) );
803 6 100       9 if ($v1 > $v2) {
804 3 100 100     14 return 1 if ( ($v1 <= $sec) || ($v2 >= $sec) );
805             } else {
806 3 100 66     18 return 1 if ( ($v1 <= $sec) && ($sec <= $v2) );
807             }
808             } else {
809 5 100 66     37 return -1 if (($range =~ /\D/) || ($range < 0) || ($range > 59));
      100        
810 3 100       10 return 1 if ($range == $sec);
811             }
812              
813 4         6 return 0;
814             }
815              
816             1;