File Coverage

blib/lib/File/Find/IncludesTimeRange.pm
Criterion Covered Total %
statement 14 72 19.4
branch 0 40 0.0
condition 0 30 0.0
subroutine 5 6 83.3
pod 1 1 100.0
total 20 149 13.4


line stmt bran cond sub pod time code
1             package File::Find::IncludesTimeRange;
2              
3 1     1   71137 use 5.006;
  1         4  
4 1     1   5 use strict;
  1         3  
  1         33  
5 1     1   6 use warnings;
  1         2  
  1         46  
6 1     1   543 use Time::Piece;
  1         12864  
  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.2.0
15              
16             =cut
17              
18             our $VERSION = '0.2.0';
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   857 my $timestamp_raw = $+{timestamp};
  1         513  
  1         405  
  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 ) ) {
    0 0        
      0        
      0        
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             } elsif ( defined($previous_timestamp)
180             && !$previous_found
181             && $previous_timestamp < $start
182             && $current_timestamp > $end )
183             {
184 0           $previous_found = 1;
185 0           push( @timestamp_to_return, $previous_timestamp );
186             }
187              
188 0           $previous_timestamp = $current_timestamp;
189             } ## end foreach my $current_timestamp (@found_timestamps)
190              
191             # if we did not find anything and we have timestamps,
192             # and the last timestamp is before the end, add it...
193             #
194             # this happens when the time frame desired is after any of the timestamps
195             # such as will happen with a start of now-30 and a end of now
196 0 0 0       if ( !defined( $timestamp_to_return[0] )
      0        
197             && defined( $found_timestamps[0] )
198             && $found_timestamps[$#found_timestamps] <= $end )
199             {
200 0           push( @timestamp_to_return, $found_timestamps[$#found_timestamps] );
201             }
202              
203 0           my $to_return = [];
204              
205             # the second sort is needed as if
206 0           foreach my $item ( sort(@timestamp_to_return) ) {
207 0           foreach my $file ( @{ $found->{$item} } ) {
  0            
208 0           push( @{$to_return}, $file );
  0            
209             }
210             }
211              
212             # the file name to write to
213              
214 0           return $to_return;
215             } ## end sub find
216              
217             =head1 AUTHOR
218              
219             Zane C. Bower-Hadley, C<< >>
220              
221             =head1 BUGS
222              
223             Please report any bugs or feature requests to C, or through
224             the web interface at L. I will be notified, and then you'll
225             automatically be notified of progress on your bug as I make changes.
226              
227              
228              
229              
230             =head1 SUPPORT
231              
232             You can find documentation for this module with the perldoc command.
233              
234             perldoc File::Find::IncludesTimeRange
235              
236              
237             You can also look for information at:
238              
239             =over 4
240              
241             =item * RT: CPAN's request tracker (report bugs here)
242              
243             L
244              
245             =item * Search CPAN
246              
247             L
248              
249             =back
250              
251              
252             =head1 ACKNOWLEDGEMENTS
253              
254              
255             =head1 LICENSE AND COPYRIGHT
256              
257             This software is Copyright (c) 2023 by Zane C. Bower-Hadley.
258              
259             This is free software, licensed under:
260              
261             The Artistic License 2.0 (GPL Compatible)
262              
263              
264             =cut
265              
266             1; # End of File::Find::IncludesTimeRange