File Coverage

blib/lib/Algorithm/Time/ToNumber.pm
Criterion Covered Total %
statement 55 55 100.0
branch 2 2 100.0
condition 8 8 100.0
subroutine 15 15 100.0
pod 9 9 100.0
total 89 89 100.0


line stmt bran cond sub pod time code
1             package Algorithm::Time::ToNumber;
2              
3 12     12   2709551 use 5.006;
  12         39  
4 12     12   56 use strict;
  12         56  
  12         309  
5 12     12   57 use warnings;
  12         21  
  12         712  
6 12     12   1714 use Math::Trig qw(pi);
  12         33767  
  12         929  
7 12     12   3932 use Time::Piece;
  12         73014  
  12         68  
8 12     12   906 use XSLoader;
  12         21  
  12         8664  
9              
10             =head1 NAME
11              
12             Algorithm::Time::ToNumber - convert time to a number
13              
14             =head1 VERSION
15              
16             Version 0.1.1
17              
18             =cut
19              
20             our $VERSION = '0.1.1';
21              
22             XSLoader::load(__PACKAGE__, $VERSION);
23              
24             =head1 SYNOPSIS
25              
26             use Algorithm::Time::ToNumber;
27              
28             print "---------------------------------------------------\n";
29             print "------------------noon_fail------------------------\n";
30             print "---------------------------------------------------\n";
31              
32             my $hour=0;
33             while ($hour < 24) {
34             my $time = $hour . ':00';
35             print $time . ' ' . Algorithm::Time::ToNumber->noon_fail($time) . "\n";
36              
37             $time = $hour . ':30';
38             print $time . ' ' . Algorithm::Time::ToNumber->noon_fail($time) . "\n";
39              
40             $hour++;
41             }
42              
43             print "---------------------------------------------------\n";
44             print "----------------midnight_fail----------------------\n";
45             print "---------------------------------------------------\n";
46              
47             $hour=0;
48             while ($hour < 24) {
49             my $time = $hour . ':00';
50             print $time . ' ' . Algorithm::Time::ToNumber->midnight_fail($time) . "\n";
51              
52             $time = $hour . ':30';
53             print $time . ' ' . Algorithm::Time::ToNumber->midnight_fail($time) . "\n";
54              
55             $hour++;
56             }
57              
58             print "---------------------------------------------------\n";
59             print "---------------------circle------------------------\n";
60             print "---------------------------------------------------\n";
61              
62             # this is what you want if you plan to use it with isolation forest
63              
64             $hour=0;
65             while ($hour < 24) {
66             my $time = $hour . ':00';
67             my ($sin_angle, $cos_angle) = Algorithm::Time::ToNumber->circle($time);
68             print $time . ' ' . $sin_angle . ' ' . $cos_angle . "\n";
69              
70             $time = $hour . ':30';
71             ($sin_angle, $cos_angle) = Algorithm::Time::ToNumber->circle($time);
72             print $time . ' ' . $sin_angle . ' ' . $cos_angle . "\n";
73              
74             $hour++;
75             }
76              
77             print "---------------------------------------------------\n";
78             print "----------------------angle------------------------\n";
79             print "---------------------------------------------------\n";
80              
81             $hour=0;
82             while ($hour < 24) {
83             my $time = $hour . ':00';
84             my ($sin_angle) = Algorithm::Time::ToNumber->circle($time);
85             print $time . ' ' . $sin_angle . "\n";
86              
87             $time = $hour . ':30';
88             ($sin_angle, $cos_angle) = Algorithm::Time::ToNumber->circle($time);
89             print $time . ' ' . $sin_angle . "\n";
90              
91             $hour++;
92             }
93              
94             print "---------------------------------------------------\n";
95             print "--------------suricata_to_circle-------------------\n";
96             print "---------------------------------------------------\n";
97              
98             $hour = 0;
99             while ($hour < 24) {
100             my $time = '2026-07-03T' .$hour . ':00:31.121465-0500';
101             my ($sin_angle, $cos_angle) = Algorithm::Time::ToNumber->suricata_to_circle($time);
102             print $time . ' ' . $sin_angle . ' ' . $cos_angle . "\n";
103              
104             $time = '2026-07-03T' .$hour . ':00:31.121465-0500';
105             ($sin_angle, $cos_angle) = Algorithm::Time::ToNumber->suricata_to_circle($time);
106             print $time . ' ' . $sin_angle . ' ' . $cos_angle . "\n";
107              
108             $hour++;
109             }
110              
111             print "---------------------------------------------------\n";
112             print "----------------circle_day-------------------------\n";
113             print "---------------------------------------------------\n";
114              
115             my $day = 0;
116             while ($day < 7) {
117             my ($sin_angle, $cos_angle) = Algorithm::Time::ToNumber->circle_day($day);
118             print $day . ' ' . $sin_angle . ' ' . $cos_angle . "\n";
119             $day++;
120             }
121              
122             print "---------------------------------------------------\n";
123             print "-----------------angle_day-------------------------\n";
124             print "---------------------------------------------------\n";
125              
126             $day = 0;
127             while ($day < 7) {
128             my $sin_angle = Algorithm::Time::ToNumber->angle_day($day);
129             print $day . ' ' . $sin_angle . "\n";
130             $day++;
131             }
132              
133             print "---------------------------------------------------\n";
134             print "----------suricata_to_circle_day-------------------\n";
135             print "---------------------------------------------------\n";
136              
137             $hour = 0;
138             while ($hour < 24) {
139             my $time = '2026-07-03T' . $hour . ':00:31.121465-0500';
140             my ($sin_angle, $cos_angle) = Algorithm::Time::ToNumber->suricata_to_circle_day($time);
141             print $time . ' ' . $sin_angle . ' ' . $cos_angle . "\n";
142             $hour++;
143             }
144              
145             print "---------------------------------------------------\n";
146             print "----------suricata_to_angle_day--------------------\n";
147             print "---------------------------------------------------\n";
148              
149             $hour = 0;
150             while ($hour < 24) {
151             my $time = '2026-07-03T' . $hour . ':00:31.121465-0500';
152             my $sin_angle = Algorithm::Time::ToNumber->suricata_to_angle_day($time);
153             print $time . ' ' . $sin_angle . "\n";
154             $hour++;
155             }
156              
157             print "---------------------------------------------------\n";
158             print "--------suricata_to_circle_both--------------------\n";
159             print "---------------------------------------------------\n";
160              
161             $hour = 0;
162             while ($hour < 24) {
163             my $time = '2026-07-03T' . $hour . ':00:31.121465-0500';
164             my ($sin_angle, $cos_angle) = Algorithm::Time::ToNumber->suricata_to_circle_both($time);
165             print $time . ' ' . $sin_angle . ' ' . $cos_angle . "\n";
166             $hour++;
167             }
168              
169             print "---------------------------------------------------\n";
170             print "---------suricata_to_angle_both--------------------\n";
171             print "---------------------------------------------------\n";
172              
173             $hour = 0;
174             while ($hour < 24) {
175             my $time = '2026-07-03T' . $hour . ':00:31.121465-0500';
176             my $sin_angle = Algorithm::Time::ToNumber->suricata_to_angle_both($time);
177             print $time . ' ' . $sin_angle . "\n";
178             $hour++;
179             }
180              
181             =head1 SUBROUTINES
182              
183             =head2 noon_fail
184              
185             Wraps at 00:00. This will result in a gap at 12:00.
186              
187             my $hour=0;
188              
189             while ($hour < 24) {
190             my $time = $hour . ':00';
191             print $time . ' ' . Algorithm::Time::ToNumber->noon_fail($time) . "\n";
192              
193             $time = $hour . ':30';
194             print $time . ' ' . Algorithm::Time::ToNumber->noon_fail($time) . "\n";
195              
196             $hour++;
197             }
198              
199             =cut
200              
201             sub noon_fail {
202 10     10 1 493 my ($class, $time) = @_;
203 10         30 my ($h, $m, $s) = split(/:/, $time);
204 10   100     37 $s //= 0;
205              
206 10         30 my $hours = $h + $m / 60 + $s / 3600;
207              
208 10 100       21 $hours -= 24 if $hours >= 12;
209              
210 10         35 return $hours;
211             }
212              
213             =head2 midnight_fail
214              
215             Wraps at 12:00. This will result in a gap at 00:00.
216              
217             my $hour=0;
218              
219             while ($hour < 24) {
220             my $time = $hour . ':00';
221             print $time . ' ' . Algorithm::Time::ToNumber->midnight_fail($time) . "\n";
222              
223             $time = $hour . ':30';
224             print $time . ' ' . Algorithm::Time::ToNumber->midnight_fail($time) . "\n";
225              
226             $hour++;
227             }
228              
229             =cut
230              
231             sub midnight_fail {
232 9     9 1 476 my ($class, $time) = @_;
233              
234 9         27 my ($h, $m, $s) = split /:/, $time;
235 9   100     48 $s //= 0;
236              
237 9         49 return $h + $m / 60 + $s / 3600;
238             }
239              
240             =head2 circle
241              
242             This returns two floats.
243              
244             This is suitable for using with isolation forest. When using it with isolation
245             forest one needs to save both returns as their own feature.
246              
247             my $hour=0;
248              
249             while ($hour < 24) {
250             my $time = $hour . ':00';
251             my ($sin_angle, $cos_angle) = Algorithm::Time::ToNumber->circle($time);
252             print $time . ' ' . $sin_angle . ' ' . $cos_angle . "\n";
253              
254             $time = $hour . ':30';
255             ($sin_angle, $cos_angle) = Algorithm::Time::ToNumber->circle($time);
256             print $time . ' ' . $sin_angle . ' ' . $cos_angle . "\n";
257              
258             $hour++;
259             }
260              
261             =cut
262              
263             sub circle {
264 12     12 1 6863 my ($class, $time) = @_;
265              
266 12         61 my ($h, $m, $s) = split( /:/, $time);
267 12   100     69 $s //= 0;
268              
269 12         52 my $angle = 2 * pi * ($h * 3600 + $m * 60 + $s) / 86400;
270              
271 12         97 return (sin($angle), cos($angle));
272             }
273              
274             =head2 angle
275              
276             This returns a continue number, but will result in points overlapping.
277              
278             It is the same as circle, but does not take the time to compute cos.
279              
280             my $hour=0;
281              
282             while ($hour < 24) {
283             my $time = $hour . ':00';
284             my ($sin_angle) = Algorithm::Time::ToNumber->angle($time);
285             print $time . ' ' . $sin_angle . "\n";
286              
287             $time = $hour . ':30';
288             ($sin_angle) = Algorithm::Time::ToNumber->angle($time);
289             print $time . ' ' . $sin_angle . "\n";
290              
291             $hour++;
292             }
293              
294             =cut
295              
296             sub angle {
297 7     7 1 4037 my ($class, $time) = @_;
298            
299 7         22 my ($h, $m, $s) = split( /:/, $time);
300 7   100     29 $s //= 0;
301              
302 7         20 my $angle = 2 * pi * ($h * 3600 + $m * 60 + $s) / 86400;
303              
304 7         50 return sin($angle);
305             }
306              
307             =head2 suricata_to_circle
308              
309             Convert .timestamp from Suricata EVE output to something that
310             circle can parse and return it.
311              
312             my $hour = 0;
313              
314             while ($hour < 24) {
315             my $time = '2026-07-03T' .$hour . ':00:31.121465-0500';
316             my ($sin_angle, $cos_angle) = Algorithm::Time::ToNumber->suricata_to_circle($time);
317             print $time . ' ' . $sin_angle . ' ' . $cos_angle . "\n";
318              
319             $time = '2026-07-03T' .$hour . ':00:31.121465-0500';
320             ($sin_angle, $cos_angle) = Algorithm::Time::ToNumber->suricata_to_circle($time);
321             print $time . ' ' . $sin_angle . ' ' . $cos_angle . "\n";
322              
323             $hour++;
324             }
325              
326             =cut
327              
328             sub suricata_to_circle {
329 5     5 1 6093 my ($class, $time) = @_;
330              
331 5         35 $time =~ s/^.*T//;
332 5         25 $time =~ s/\-.*$//;
333              
334 5         13 return Algorithm::Time::ToNumber->circle($time);
335             }
336              
337             =head2 circle_day
338              
339             Convert day of week to sin/cos values on a circle, suitable for use with
340             isolation forest. The day value should be 0 (Sunday) through 6 (Saturday),
341             matching the convention used by Perl's C.
342              
343             my $day = 0;
344              
345             while ($day < 7) {
346             my ($sin_angle, $cos_angle) = Algorithm::Time::ToNumber->circle_day($day);
347             print $day . ' ' . $sin_angle . ' ' . $cos_angle . "\n";
348             $day++;
349             }
350              
351             =cut
352              
353             sub circle_day {
354 7     7 1 3928 my ($class, $day) = @_;
355              
356 7         21 my $angle = 2 * pi * $day / 7;
357              
358 7         42 return (sin($angle), cos($angle));
359             }
360              
361             =head2 angle_day
362              
363             Like circle_day, but returns only the sin value.
364              
365             my $day = 0;
366              
367             while ($day < 7) {
368             my $sin_angle = Algorithm::Time::ToNumber->angle_day($day);
369             print $day . ' ' . $sin_angle . "\n";
370             $day++;
371             }
372              
373             =cut
374              
375             sub angle_day {
376 7     7 1 2224 my ($class, $day) = @_;
377              
378 7         16 my $angle = 2 * pi * $day / 7;
379              
380 7         39 return sin($angle);
381             }
382              
383             =head2 suricata_to_circle_day
384              
385             Convert .timestamp from Suricata EVE output to a circle_day value based on
386             the day of week.
387              
388             my $hour = 0;
389              
390             while ($hour < 24) {
391             my $time = '2026-07-03T' . $hour . ':00:31.121465-0500';
392             my ($sin_angle, $cos_angle) = Algorithm::Time::ToNumber->suricata_to_circle_day($time);
393             print $time . ' ' . $sin_angle . ' ' . $cos_angle . "\n";
394             $hour++;
395             }
396              
397             =cut
398              
399             sub suricata_to_circle_day {
400 3     3 1 6385 my ($class, $time) = @_;
401              
402 3         26 my ($date) = $time =~ /^(\d{4}-\d{2}-\d{2})T/;
403              
404 3         22 my $t = Time::Piece->strptime($date, '%Y-%m-%d');
405              
406 3         187 return Algorithm::Time::ToNumber->circle_day($t->day_of_week);
407             }
408              
409             =head2 suricata_to_angle_day
410              
411             Convert .timestamp from Suricata EVE output to an angle_day value based on
412             the day of week.
413              
414             my $hour = 0;
415              
416             while ($hour < 24) {
417             my $time = '2026-07-03T' . $hour . ':00:31.121465-0500';
418             my $sin_angle = Algorithm::Time::ToNumber->suricata_to_angle_day($time);
419             print $time . ' ' . $sin_angle . "\n";
420             $hour++;
421             }
422              
423             =cut
424              
425             sub suricata_to_angle_day {
426 3     3 1 1738 my ($class, $time) = @_;
427              
428 3         17 my ($date) = $time =~ /^(\d{4}-\d{2}-\d{2})T/;
429              
430 3         12 my $t = Time::Piece->strptime($date, '%Y-%m-%d');
431              
432 3         117 return Algorithm::Time::ToNumber->angle_day($t->day_of_week);
433             }
434              
435             =head2 suricata_to_circle_both
436              
437             Convert .timestamp from Suricata EVE output to a sin/cos pair encoding both
438             the day of week and time of day as a single position within the week. The
439             week is treated as a 604800-second circle (7 days * 86400 seconds).
440              
441             my $hour = 0;
442              
443             while ($hour < 24) {
444             my $time = '2026-07-03T' . $hour . ':00:31.121465-0500';
445             my ($sin_angle, $cos_angle) = Algorithm::Time::ToNumber->suricata_to_circle_both($time);
446             print $time . ' ' . $sin_angle . ' ' . $cos_angle . "\n";
447             $hour++;
448             }
449              
450             =cut
451              
452              
453             =head2 suricata_to_angle_both
454              
455             Like suricata_to_circle_both, but returns only the sin value.
456              
457             my $hour = 0;
458              
459             while ($hour < 24) {
460             my $time = '2026-07-03T' . $hour . ':00:31.121465-0500';
461             my $sin_angle = Algorithm::Time::ToNumber->suricata_to_angle_both($time);
462             print $time . ' ' . $sin_angle . "\n";
463             $hour++;
464             }
465              
466             =cut
467              
468              
469             =head1 AUTHOR
470              
471             Zane C. Bowers-Hadley, C<< >>
472              
473             =head1 BUGS
474              
475             Please report any bugs or feature requests to C, or through
476             the web interface at L. I will be notified, and then you'll
477             automatically be notified of progress on your bug as I make changes.
478              
479              
480              
481              
482             =head1 SUPPORT
483              
484             You can find documentation for this module with the perldoc command.
485              
486             perldoc Algorithm::Time::ToNumber
487              
488              
489             You can also look for information at:
490              
491             =over 4
492              
493             =item * RT: CPAN's request tracker (report bugs here)
494              
495             L
496              
497             =item * CPAN Ratings
498              
499             L
500              
501             =item * Search CPAN
502              
503             L
504              
505             =back
506              
507              
508             =head1 ACKNOWLEDGEMENTS
509              
510              
511             =head1 LICENSE AND COPYRIGHT
512              
513             This software is Copyright (c) 2026 by Zane C. Bowers-Hadley.
514              
515             This is free software, licensed under:
516              
517             The GNU Lesser General Public License, Version 2.1, February 1999
518              
519             =cut
520              
521             1; # End of Algorithm::Time::ToNumber