File Coverage

blib/lib/WWW/MetaForge/ArcRaiders/Result/EventTimer/TimeSlot.pm
Criterion Covered Total %
statement 36 41 87.8
branch 5 6 83.3
condition 5 12 41.6
subroutine 8 9 88.8
pod 5 5 100.0
total 59 73 80.8


line stmt bran cond sub pod time code
1             package WWW::MetaForge::ArcRaiders::Result::EventTimer::TimeSlot;
2             our $AUTHORITY = 'cpan:GETTY';
3             # ABSTRACT: A time slot with start and end DateTime objects
4             our $VERSION = '0.002';
5              
6 6     6   58 use Moo;
  6         14  
  6         96  
7 6     6   2882 use Types::Standard qw(InstanceOf);
  6         16  
  6         74  
8 6     6   16633 use DateTime;
  6         14  
  6         173  
9 6     6   36 use namespace::clean;
  6         11  
  6         62  
10              
11              
12             has start => (
13             is => 'ro',
14             isa => InstanceOf['DateTime'],
15             required => 1,
16             );
17              
18              
19             has end => (
20             is => 'ro',
21             isa => InstanceOf['DateTime'],
22             required => 1,
23             );
24              
25              
26             sub from_hashref {
27 11     11 1 21751 my ($class, $data) = @_;
28              
29             # New API format: startTime/endTime as millisecond timestamps
30 11 100       43 if (exists $data->{startTime}) {
31 1         6 return $class->from_epoch_ms($data->{startTime}, $data->{endTime});
32             }
33              
34             # Legacy format: start/end as HH:MM strings
35 10         67 my $now = DateTime->now(time_zone => 'UTC');
36 10         5608 my $today = $now->clone->truncate(to => 'day');
37              
38 10         3634 my ($start_h, $start_m) = split /:/, $data->{start};
39 10         58 my ($end_h, $end_m) = split /:/, $data->{end};
40              
41 10         33 my $start = $today->clone->set(hour => $start_h, minute => $start_m);
42 10         6662 my $end = $today->clone->set(hour => $end_h, minute => $end_m);
43              
44             # Handle overnight slots (e.g., 23:00 - 01:00)
45 10 100       6213 if ($end <= $start) {
46             # Slot crosses midnight - determine which day based on current time
47 1 50       91 if ($now < $end) {
48             # Early morning (after midnight, before slot end) - start was yesterday
49 0         0 $start->subtract(days => 1);
50             } else {
51             # Before midnight or after slot end - end is tomorrow
52 1         82 $end->add(days => 1);
53             }
54             }
55              
56 10         2537 return $class->new(
57             start => $start,
58             end => $end,
59             );
60             }
61              
62              
63             sub from_epoch_ms {
64 19     19 1 15934 my ($class, $start_ms, $end_ms) = @_;
65              
66 19         111 my $start = DateTime->from_epoch(
67             epoch => int($start_ms / 1000),
68             time_zone => 'UTC',
69             );
70 19         8304 my $end = DateTime->from_epoch(
71             epoch => int($end_ms / 1000),
72             time_zone => 'UTC',
73             );
74              
75 19         7568 return $class->new(
76             start => $start,
77             end => $end,
78             );
79             }
80              
81              
82             sub contains {
83 30     30 1 3790 my ($self, $dt) = @_;
84 30   33     88 $dt //= DateTime->now(time_zone => 'UTC');
85 30   100     197 return $dt >= $self->start && $dt < $self->end;
86             }
87              
88              
89             sub minutes_until_start {
90 5     5 1 14 my ($self, $dt) = @_;
91 5   33     34 $dt //= DateTime->now(time_zone => 'UTC');
92 5         2282 my $delta = $self->start->epoch - $dt->epoch;
93 5         139 return int($delta / 60);
94             }
95              
96              
97             sub minutes_until_end {
98 0     0 1   my ($self, $dt) = @_;
99 0   0       $dt //= DateTime->now(time_zone => 'UTC');
100 0           my $delta = $self->end->epoch - $dt->epoch;
101 0           return int($delta / 60);
102             }
103              
104              
105             1;
106              
107             __END__
108              
109             =pod
110              
111             =encoding UTF-8
112              
113             =head1 NAME
114              
115             WWW::MetaForge::ArcRaiders::Result::EventTimer::TimeSlot - A time slot with start and end DateTime objects
116              
117             =head1 VERSION
118              
119             version 0.002
120              
121             =head1 SYNOPSIS
122              
123             my $slot = WWW::MetaForge::ArcRaiders::Result::EventTimer::TimeSlot->from_hashref({
124             start => '14:00',
125             end => '15:00',
126             });
127              
128             say $slot->start; # DateTime object
129             say $slot->end; # DateTime object
130              
131             if ($slot->contains) {
132             say "Event is active now!";
133             }
134              
135             =head1 DESCRIPTION
136              
137             Represents a scheduled time slot with DateTime objects for start and end times.
138             All times are in UTC.
139              
140             =head2 start
141              
142             DateTime object for slot start time.
143              
144             =head2 end
145              
146             DateTime object for slot end time.
147              
148             =head2 from_hashref
149              
150             my $slot = TimeSlot->from_hashref({ start => "HH:MM", end => "HH:MM" });
151              
152             Construct from API response hash with HH:MM strings or millisecond timestamps.
153              
154             =head2 from_epoch_ms
155              
156             my $slot = TimeSlot->from_epoch_ms($start_ms, $end_ms);
157              
158             Construct from epoch milliseconds timestamps.
159              
160             =head2 contains
161              
162             if ($slot->contains) { ... }
163             if ($slot->contains($datetime)) { ... }
164              
165             Returns true if the given DateTime (or now) is within this slot.
166              
167             =head2 minutes_until_start
168              
169             my $mins = $slot->minutes_until_start;
170              
171             Returns minutes until this slot starts.
172              
173             =head2 minutes_until_end
174              
175             my $mins = $slot->minutes_until_end;
176              
177             Returns minutes until this slot ends.
178              
179             =head1 SUPPORT
180              
181             =head2 Issues
182              
183             Please report bugs and feature requests on GitHub at
184             L<https://github.com/Getty/p5-www-metaforge/issues>.
185              
186             =head2 IRC
187              
188             You can reach Getty on C<irc.perl.org> for questions and support.
189              
190             =head1 CONTRIBUTING
191              
192             Contributions are welcome! Please fork the repository and submit a pull request.
193              
194             =head1 AUTHOR
195              
196             Torsten Raudssus <torsten@raudssus.de>
197              
198             =head1 COPYRIGHT AND LICENSE
199              
200             This software is copyright (c) 2026 by Torsten Raudssus.
201              
202             This is free software; you can redistribute it and/or modify it under
203             the same terms as the Perl 5 programming language system itself.
204              
205             =cut