File Coverage

blib/lib/Acme/PM/Berlin/Meetings.pm
Criterion Covered Total %
statement 59 59 100.0
branch 23 24 95.8
condition 14 17 82.3
subroutine 8 8 100.0
pod 0 2 0.0
total 104 110 94.5


line stmt bran cond sub pod time code
1             # -*- perl -*-
2              
3             #
4             # Author: Slaven Rezic
5             #
6             # Copyright (C) 2010,2012,2015,2016,2017,2020 Slaven Rezic. All rights reserved.
7             # This package is free software; you can redistribute it and/or
8             # modify it under the same terms as Perl itself.
9             #
10             # Mail: slaven@rezic.de
11             # WWW: http://www.rezic.de/eserte/
12             #
13              
14             package Acme::PM::Berlin::Meetings;
15              
16 2     2   103903 use strict;
  2         15  
  2         90  
17             our $VERSION = '202009.26';
18              
19 2     2   10 use Exporter 'import'; # needs Exporter 5.57
  2         4  
  2         83  
20             our @EXPORT = qw(next_meeting);
21              
22 2     2   1841 use DateTime;
  2         1031494  
  2         1193  
23              
24             sub next_meeting {
25 1   50 1 0 130 my $count = shift || 1;
26 1         11 my $dt = DateTime->now(time_zone => 'local');
27 1         5359 map { $dt = next_meeting_dt($dt) } (1 .. $count);
  1         6  
28             }
29              
30             sub next_meeting_dt {
31 13     13 0 18879 my $dt = shift;
32 13         58 my $dt_berlin = $dt->clone->set_time_zone('Europe/Berlin');
33              
34             # Regular exception: December meeting is in January
35 13 100 66     18274 if ($dt_berlin->month == 1 && $dt_berlin->day < 10) {
36 2         35 my $dec_meeting = _get_dec_meeting($dt_berlin);
37 2 100       10 if ($dec_meeting > $dt_berlin) {
38 1         71 return $dec_meeting;
39             }
40             }
41              
42             # Exceptions
43             {
44             # August 2020 (last Wed -> last Tue)
45 12         80 my $dt_aug_2020 = DateTime->new(year=>2020, month=>8, day=>25, hour=>19, time_zone=>"Europe/Berlin");
46 12         7289 my $dt_aug_2020_from = DateTime->new(year=>2020, month=>7, day=>29, hour=>19, time_zone=>"Europe/Berlin");
47 12         6926 my $dt_aug_2020_until = DateTime->new(year=>2020, month=>8, day=>26, hour=>19, time_zone=>"Europe/Berlin");
48 12 100 100     6802 if ($dt_berlin > $dt_aug_2020_from && $dt_berlin < $dt_aug_2020) {
    100 100        
49 1         124 return $dt_aug_2020;
50             } elsif ($dt_berlin >= $dt_aug_2020 && $dt_berlin < $dt_aug_2020_until) {
51 1         238 $dt_berlin = $dt_aug_2020_until;
52             }
53             }
54             {
55             # September 2020 (last Wed, 19h -> pre-last Wed, 18h)
56 12         179 my $dt_sep_2020 = DateTime->new(year=>2020, month=>9, day=>23, hour=>18, time_zone=>"Europe/Berlin");
  11         1752  
  11         45  
57 11         6310 my $dt_sep_2020_from = DateTime->new(year=>2020, month=>8, day=>25, hour=>19, time_zone=>"Europe/Berlin");
58 11         6380 my $dt_sep_2020_until = DateTime->new(year=>2020, month=>9, day=>30, hour=>19, time_zone=>"Europe/Berlin");
59 11 100 100     6536 if ($dt_berlin > $dt_sep_2020_from && $dt_berlin < $dt_sep_2020) {
    100 66        
60 2         230 return $dt_sep_2020;
61             } elsif ($dt_berlin >= $dt_sep_2020 && $dt_berlin < $dt_sep_2020_until) {
62 2         538 $dt_berlin = $dt_sep_2020_until;
63             }
64             }
65              
66             # Regular meetings
67 9         949 my $last_wed_of_month = _get_last_wed_of_month($dt_berlin);
68 9 100       33 if ($last_wed_of_month <= $dt_berlin) {
69 4         310 $dt_berlin->add(months => 1, end_of_month => 'limit');
70 4         5483 $last_wed_of_month = _get_last_wed_of_month($dt_berlin);
71             }
72 9 100       359 if ($last_wed_of_month->month == 12) {
73 2         17 return _get_dec_meeting($last_wed_of_month);
74             }
75 7         80 $last_wed_of_month;
76             }
77              
78             sub _get_last_wed_of_month {
79 13     13   38 my $dt_berlin = shift;
80 13         62 my $last_day_of_month = DateTime->last_day_of_month(year => $dt_berlin->year, month => $dt_berlin->month, time_zone => 'Europe/Berlin');
81 13         8493 my $dow = $last_day_of_month->day_of_week;
82 13 100       110 my $last_wed_of_month = $last_day_of_month->add(days => $dow < 3 ? -$dow-4 : -$dow+3);
83 13         9187 _adjust_hour($last_wed_of_month);
84 13         9092 $last_wed_of_month;
85             }
86              
87             sub _get_dec_meeting {
88 4     4   10 my $dt = shift;
89 4         15 $dt = $dt->clone;
90 4 100       45 if ($dt->month == 12) {
91 2         18 $dt->add(months => 1); # end_of_month does not matter
92             }
93 4         3161 $dt->set(day => 3);
94 4         2843 my $dow = $dt->day_of_week;
95 4 50       34 $dt->add(days => $dow < 4 ? -$dow+3 : -$dow+10);
96 4         5408 _adjust_hour($dt);
97 4         2854 $dt;
98             }
99              
100             sub _adjust_hour {
101 17     17   38 my $dt = shift;
102 17 100       76 if ($dt->year >= 2016) {
103 8         61 $dt->set(hour => 19);
104             } else {
105 9         74 $dt->set(hour => 20);
106             }
107             }
108              
109             1;
110              
111             __END__
112              
113             =head1 NAME
114              
115             Acme::PM::Berlin::Meetings - get the next date of the Berlin PM meeting
116              
117             =head1 SYNOPSIS
118              
119             use Acme::PM::Berlin::Meetings;
120             next_meeting(1)
121              
122             Or use the bundled script:
123              
124             berlin-pm
125              
126             =head1 NOTES
127              
128             This module knows about special Berlin.PM traditions like postponing
129             the December meeting to the first or second week in January.
130              
131             =head1 AUTHOR
132              
133             Slaven Rezic
134              
135             =head1 SEE ALSO
136              
137             L<Acme::PM::Barcelona::Meeting>, L<Acme::PM::Paris::Meetings>.
138              
139             =cut