File Coverage

blib/lib/Date/Holidays/FR.pm
Criterion Covered Total %
statement 59 59 100.0
branch 1 2 50.0
condition 2 3 66.6
subroutine 17 17 100.0
pod 7 7 100.0
total 86 88 97.7


line stmt bran cond sub pod time code
1             # -*- encoding: utf-8; indent-tabs-mode: nil -*-
2             #
3             # Perl module to compute the French holidays in a given year.
4             # Copyright (c) 2004, 2019, 2021 Fabien Potencier and Jean Forget, all rights reserved
5             #
6             # See the license in the embedded documentation below.
7             #
8             package Date::Holidays::FR;
9              
10 3     3   130865 use utf8;
  3         18  
  3         18  
11 3     3   88 use strict;
  3         6  
  3         53  
12 3     3   26 use warnings;
  3         11  
  3         133  
13 3     3   1568 use Time::Local qw(timelocal_modern);
  3         6971  
  3         193  
14 3     3   1396 use Date::Easter;
  3         1970  
  3         167  
15 3     3   1787 use Readonly;
  3         11173  
  3         165  
16 3     3   23 use Exporter;
  3         5  
  3         2281  
17             our @ISA = qw(Exporter);
18             our @EXPORT = qw(is_fr_holiday is_holiday holidays fr_holidays);
19              
20             our $VERSION = '0.04';
21              
22             Readonly::Scalar my $easter_offset => 1;
23             Readonly::Scalar my $ascension_offset => 39;
24             Readonly::Scalar my $pentecost_offset => 50;
25             Readonly::Scalar my $seconds_in_day => 60 * 60 * 24;
26             Readonly::Scalar my $false => 0;
27              
28             Readonly::Scalar my $localtime_month_idx => 4;
29             Readonly::Scalar my $localtime_day_idx => 3;
30              
31             sub get_easter {
32 276     276 1 442 my ($year) = @_;
33              
34 276         638 return Date::Easter::easter($year);
35             }
36              
37             sub get_ascension {
38 1     1 1 916 my ($year) = @_;
39              
40 1         3 return _compute_date_from_easter($year, $ascension_offset);
41             }
42              
43             sub get_pentecost {
44 1     1 1 846 my ($year) = @_;
45              
46 1         3 return _compute_date_from_easter($year, $pentecost_offset);
47             }
48              
49             sub _compute_date_from_easter {
50 275     275   418 my ($year, $delta) = @_;
51              
52 275         427 my ($easter_month, $easter_day) = get_easter($year);
53 275         4946 my $easter_date = timelocal_modern(0, 0, 1, $easter_day, $easter_month - 1, $year);
54 275         18524 my ($date_month, $date_day) = (localtime($easter_date + $delta * $seconds_in_day))[$localtime_month_idx, $localtime_day_idx];
55 275         671 $date_month++;
56              
57 275         563 return ($date_month, $date_day);
58             }
59              
60             sub is_holiday {
61 16     16 1 41 return is_fr_holiday(@_);
62             }
63              
64             sub is_fr_holiday {
65 89     89 1 7365 my ($year, $month, $day) = @_;
66              
67 89         163 my $date = _format_segment($month) . _format_segment($day);
68 89         160 my $dates = _get_dates($year);
69              
70 89   66     662 return $dates->{$date} || $false;
71             }
72              
73             sub holidays {
74 1     1 1 4 return fr_holidays(shift);
75             }
76              
77             sub fr_holidays {
78 2     2 1 4 my $year = shift;
79              
80 2         4 my $holidays = {};
81              
82 2         4 my $dates = _get_dates($year);
83              
84 2         3 foreach my $date (keys %{$dates}) {
  2         9  
85 22         113 my ($month, $day) = $date =~ m/(\d{2})(\d{2})/;
86              
87 22         37 my $holiday = is_fr_holiday($year, $month, $day);
88              
89 22 50       41 if ($holiday) {
90 22         44 $holidays->{$date} = $holiday;
91             }
92             };
93              
94 2         17 return $holidays;
95             }
96              
97             sub _get_dates {
98 91     91   148 my $year = shift;
99              
100 91         426 my $dates = {
101             '0101' => 'Nouvel an',
102             '0501' => 'Fête du travail',
103             '0508' => 'Armistice 1939-1945',
104             '0714' => 'Fête nationale',
105             '0815' => 'Assomption',
106             '1101' => 'Toussaint',
107             '1111' => 'Armistice 1914-1918',
108             '1225' => 'Noël',
109             };
110              
111 91         170 my ($easter_month, $easter_day) = _compute_date_from_easter($year, $easter_offset);
112 91         183 my ($ascension_month, $ascension_day) = _compute_date_from_easter($year, $ascension_offset);
113 91         178 my ($pentecost_month, $pentecost_day) = _compute_date_from_easter($year, $pentecost_offset);
114              
115 91         193 $dates->{_format_segment($easter_month) . _format_segment($easter_day)} = 'Lundi de Pâques';
116 91         198 $dates->{_format_segment($ascension_month) . _format_segment($ascension_day)} = 'Ascension';
117 91         167 $dates->{_format_segment($pentecost_month) . _format_segment($pentecost_day)} = 'Lundi de Pentecôte';
118              
119 91         187 return $dates;
120             }
121              
122             sub _format_segment {
123 724     724   1901 return sprintf('%02d', shift);
124             }
125              
126             # And instead of a plain, boring "1" to end the module source, let us
127             # celebrate the 14th of July, closely associated with the Bastille:
128              
129             "-- À la Bastille on l'aime bien Nini Peau-d'chien,
130             Elle est si douce et si gentille !
131             On l'aime bien...
132             -- QUI ÇA ?
133             -- Nini Peau-d'chien...
134             -- OÙ ÇA ?
135             -- À la Basti-i-ille";
136              
137             __END__