File Coverage

blib/lib/File/Find/IncludesTimeRange.pm
Criterion Covered Total %
statement 14 70 20.0
branch 0 38 0.0
condition 0 21 0.0
subroutine 5 6 83.3
pod 1 1 100.0
total 20 136 14.7


line stmt bran cond sub pod time code
1             package File::Find::IncludesTimeRange;
2              
3 1     1   69951 use 5.006;
  1         4  
4 1     1   7 use strict;
  1         2  
  1         33  
5 1     1   5 use warnings;
  1         3  
  1         36  
6 1     1   601 use Time::Piece;
  1         13349  
  1         4  
7              
8             =head1 NAME
9              
10             File::Find::IncludesTimeRange - Takes a array of time stamped items(largely meant for use with files) returns ones that include the specified time range.
11              
12             =head1 VERSION
13              
14             Version 0.1.1
15              
16             =cut
17              
18             our $VERSION = '0.1.1';
19              
20             =head1 SYNOPSIS
21              
22             use File::Find::IncludesTimeRange;
23             uses Time::Piece;
24             use Data::Dumper;
25              
26             my @files=(
27             'daemonlogger.1677468390.pcap',
28             'daemonlogger.1677468511.pcap',
29             'daemonlogger.1677468632.pcap',
30             'daemonlogger.1677468753.pcap',
31             'daemonlogger.1677468874.pcap',
32             'daemonlogger.1677468995.pcap',
33             'daemonlogger.1677469116.pcap',
34             'daemonlogger.1677469237.pcap',
35             'daemonlogger.1677469358.pcap',
36             'daemonlogger.1677469479.pcap',
37             'daemonlogger.1677469600.pcap',
38             'daemonlogger.1677469721.pcap',
39             );
40              
41             print Dumper(\@files);
42              
43             my $start=Time::Piece->strptime('1677468620', '%s');
44             my $end=Time::Piece->strptime( '1677468633', '%s');
45              
46             my $found=File::Find::IncludesTimeRange->find(
47             items=>\@files,
48             start=>$start,
49             end=>$end,
50             regex=>'(?\d\d\d\d\d\d+)(\.pcap|(?\.\d+)\.pcap)$',
51             strptime=>'%s',
52             );
53             print Dumper($found);
54              
55             # do similar, but skip parsing the time stamp as it is already in unixtime
56             my $found=File::Find::IncludesTimeRange->find(
57             items=>\@files,
58             start=>$start,
59             end=>$end,
60             regex=>'(?(\d\d\d\d\d\d+|\d\d\d\d\d\+.\d+))\.pcap$',
61             ts_is_unixtime => 1,
62             );
63             print Dumper($found);
64              
65             =head1 SUBROUTINES
66              
67             =head2 find
68              
69             Searches through a list of items , finds the ones that appear to be timestamped.
70             It will then sort the found time stamps and return the ones that include the
71             specified time periods.
72              
73             There following options are taken.
74              
75             - items :: A array ref of items to examine.
76              
77             - start :: A Time::Piece object set to the start time.
78              
79             - end :: A Time::Piece object set to the end time.
80              
81             - regex :: A regex to use for matching the files. Requires uses of the named
82             group 'timestamp' for capturing the timestamp. If it includes micro
83             seconds in it, since Time::Piece->strptime does not handle those,
84             those can be captured via the group 'subsec'. They will then be
85             appended to to the epoch time of any parsed timestamp for sorting
86             purposes.
87             - Default :: (?\d\d\d\d\d\d+)(\.pcap|(?\.\d+)\.pcap)$
88              
89             - strptime :: The format for use with L->strptime.
90             - Default :: %s
91              
92             - ts_is_unixtime :: Skips using Time::Piece and strptime as it is just a simple
93             numeric test. For this subsecs should be included in the
94             capture group 'timestamp' for the regex.
95              
96             =cut
97              
98             sub find {
99 0     0 1   my ( $blank, %opts ) = @_;
100              
101             # some basic error checking
102 0 0         if ( !defined( $opts{start} ) ) {
    0          
    0          
    0          
    0          
    0          
    0          
103 0           die('$opts{start} is undef');
104             } elsif ( !defined( $opts{end} ) ) {
105 0           die('$opts{end} is undef');
106             } elsif ( !defined( $opts{items} ) ) {
107 0           die('$opts{items} is undef');
108             } elsif ( ref( $opts{start} ) ne 'Time::Piece' ) {
109 0           die('$opts{start} is not a Time::Piece object');
110             } elsif ( ref( $opts{end} ) ne 'Time::Piece' ) {
111 0           die('$opts{end} is not a Time::Piece object');
112             } elsif ( ref( $opts{items} ) ne 'ARRAY' ) {
113 0           die('$opts{items} is not a ARRAY');
114             } elsif ( $opts{start} > $opts{end} ) {
115 0           die('$opts{start} is greater than $opts{end}');
116             }
117              
118 0 0         if ( !defined( $opts{strptime} ) ) {
119 0           $opts{strptime} = '%s';
120             }
121              
122 0 0         if ( !defined( $opts{regex} ) ) {
123 0           $opts{regex} = '(?\d\d\d\d\d\d+)(\.pcap|(?\.\d+)\.pcap)$';
124             }
125              
126 0           my $start = $opts{start}->epoch;
127 0           my $end = $opts{end}->epoch;
128              
129             # a HoA of found timestamps
130             # each value is a array containing files for that time stamp
131 0           my $found = {};
132 0           foreach my $item ( @{ $opts{items} } ) {
  0            
133 0 0         if ( $item =~ /$opts{regex}/ ) {
134 0           my $subsec = '';
135 1     1   839 my $timestamp_raw = $+{timestamp};
  1         433  
  1         379  
  0            
136 0 0         if ( defined( $+{subsec} ) ) {
137 0           $subsec = $+{subsec};
138             }
139              
140 0           my $timestamp;
141             my $full_timestamp;
142 0 0         if ( !$opts{ts_is_unixtime} ) {
143             # we have one we actually need to parse.... attempt to
144             # and if we can get the time stamp
145 0           eval { $timestamp = Time::Piece->strptime( $timestamp_raw, $opts{strptime} ); };
  0            
146 0 0 0       if ( !$@ && defined($timestamp) ) {
147 0           $full_timestamp = $timestamp->epoch . $subsec;
148             }
149             } else {
150             # if ts_is_unixtime, then no need to parse it... just go ahead and use it
151 0           $full_timestamp = $timestamp_raw;
152             }
153              
154             # only not going to be defined if the eval above failed for Time::Piece->strptime
155 0 0         if ( defined($full_timestamp) ) {
156 0 0         if ( !defined( $found->{$full_timestamp} ) ) {
157 0           $found->{$full_timestamp} = [];
158             }
159 0           push( @{ $found->{$full_timestamp} }, $item );
  0            
160             }
161             } ## end if ( $item =~ /$opts{regex}/ )
162             } ## end foreach my $item ( @{ $opts{items} } )
163              
164 0           my @found_timestamps = sort( keys( %{$found} ) );
  0            
165 0           my $previous_timestamp;
166             my $previous_found;
167 0           my @timestamp_to_return;
168 0           foreach my $current_timestamp (@found_timestamps) {
169 0 0 0       if ( ( $start <= $current_timestamp ) && ( $current_timestamp <= $end ) ) {
170 0           push( @timestamp_to_return, $current_timestamp );
171              
172             # if we find one that it is between, but not equal, then add the previous as that contains the start
173 0 0 0       if ( defined($previous_timestamp) && !$previous_found && ( $start != $current_timestamp ) ) {
    0 0        
      0        
174 0           $previous_found = 1;
175 0           push( @timestamp_to_return, $previous_timestamp );
176             } elsif ( !$previous_found && ( $start == $current_timestamp ) ) {
177 0           $previous_found = 1;
178             }
179             } ## end if ( ( $start <= $current_timestamp ) && (...))
180              
181 0           $previous_timestamp = $current_timestamp;
182             } ## end foreach my $current_timestamp (@found_timestamps)
183              
184             # if we did not find anything and we have timestamps,
185             # and the last timestamp is before the end, add it...
186             #
187             # this happens when the time frame desired is after any of the timestamps
188             # such as will happen with a start of now-30 and a end of now
189 0 0 0       if ( !defined( $timestamp_to_return[0] )
      0        
190             && defined( $found_timestamps[0] )
191             && $found_timestamps[$#found_timestamps] <= $end )
192             {
193 0           push( @timestamp_to_return, $found_timestamps[$#found_timestamps] );
194             }
195              
196 0           my $to_return = [];
197              
198             # the second sort is needed as if
199 0           foreach my $item ( sort(@timestamp_to_return) ) {
200 0           foreach my $file ( @{ $found->{$item} } ) {
  0            
201 0           push( @{$to_return}, $file );
  0            
202             }
203             }
204              
205             # the file name to write to
206              
207 0           return $to_return;
208             } ## end sub find
209              
210             =head1 AUTHOR
211              
212             Zane C. Bower-Hadley, C<< >>
213              
214             =head1 BUGS
215              
216             Please report any bugs or feature requests to C, or through
217             the web interface at L. I will be notified, and then you'll
218             automatically be notified of progress on your bug as I make changes.
219              
220              
221              
222              
223             =head1 SUPPORT
224              
225             You can find documentation for this module with the perldoc command.
226              
227             perldoc File::Find::IncludesTimeRange
228              
229              
230             You can also look for information at:
231              
232             =over 4
233              
234             =item * RT: CPAN's request tracker (report bugs here)
235              
236             L
237              
238             =item * Search CPAN
239              
240             L
241              
242             =back
243              
244              
245             =head1 ACKNOWLEDGEMENTS
246              
247              
248             =head1 LICENSE AND COPYRIGHT
249              
250             This software is Copyright (c) 2023 by Zane C. Bower-Hadley.
251              
252             This is free software, licensed under:
253              
254             The Artistic License 2.0 (GPL Compatible)
255              
256              
257             =cut
258              
259             1; # End of File::Find::IncludesTimeRange