1#!/usr/bin/perl
2use strict;
3use warnings;
4
5use Time::Moment;
6use Time::Moment::Adjusters qw[ NextOrSameDayOfWeek
7                                WesternEasterSunday ];
8
9use enum qw[ Monday=1 Tuesday Wednesday Thursday Friday Saturday Sunday ];
10
11# Lag (1989:253) om allmänna helgdagar (Act (1989:253) on public holidays)
12# https://lagen.nu/1989:253
13sub compute_public_holidays {
14    @_ == 1 or die q<Usage: compute_public_holidays(year)>;
15    my ($year) = @_;
16
17    my @dates;
18    my $tm     = Time::Moment->new(year => $year);
19    my $easter = $tm->with(WesternEasterSunday);
20
21    # Nyårsdagen (New Year's Day), January 1.
22    push @dates, $tm->with_month(1)
23                    ->with_day_of_month(1);
24
25    # Trettondagen (Epiphany), January 6.
26    push @dates, $tm->with_month(1)
27                    ->with_day_of_month(6);
28
29    # Långfredagen (Good Friday), the Friday preceding Easter Sunday.
30    push @dates, $easter->minus_days(2);
31
32    # Påskdagen (Easter Sunday), the Sunday immediately following
33    # the full moon that occurs on or next after 21 March.
34    push @dates, $easter;
35
36    # Annandag påsk (Easter Monday), the day after Easter Sunday.
37    push @dates, $easter->plus_days(1);
38
39    # Kristi himmelsfärds dag (Ascension Day), sixth Thursday
40    # after Easter Sunday.
41    push @dates, $easter->plus_days(5*7+4);
42
43    # Pingstdagen (Pentecost), seventh Sunday after Easter Sunday.
44    push @dates, $easter->plus_days(7*7);
45
46    # Annandag pingst (Whit Monday), the day after Pentecost.
47    if ($year <= 2004) {
48        push @dates, $easter->plus_days(7*7+1);
49    }
50
51    # Första maj (First of May), May 1.
52    push @dates, $tm->with_month(5)
53                    ->with_day_of_month(1);
54
55    # Sveriges nationaldag (National Day of Sweden), June 6.
56    if ($year >= 2005) {
57        push @dates, $tm->with_month(6)
58                        ->with_day_of_month(6);
59    }
60
61    # Midsommardagen (Midsummer's Day), Saturday that falls
62    # between June 20th to 26th.
63    push @dates, $tm->with_month(6)
64                    ->with_day_of_month(20)
65                    ->with(NextOrSameDayOfWeek(Saturday));
66
67    # Alla helgons dag (All Saints' Day), Saturday that falls
68    # between Oct 31st to Nov 6th.
69    push @dates, $tm->with_month(10)
70                    ->with_day_of_month(31)
71                    ->with(NextOrSameDayOfWeek(Saturday));
72
73    # Juldagen (Christmas Day), December 25.
74    push @dates, $tm->with_month(12)
75                    ->with_day_of_month(25);
76
77    # Annandag jul (Boxing Day), December 26.
78    push @dates, $tm->with_month(12)
79                    ->with_day_of_month(26);
80
81    return @dates;
82}
83
84# Swedish bank holidays
85# All days except Saturdays, Sundays, Epiphany, Good Friday,
86# Easter Monday, First of May, Ascension Day, Sweden's National
87# Day, Midsummer Eve, Christmas Eve, Christmas Day, Boxing
88# Day, New Year's Eve and New Year's Day (all according to the
89# Swedish calendar), as well as any other days currently stipulated
90# by the Swedish Act (1989:253) on Public Holidays.
91sub compute_bank_holidays {
92    @_ == 1 or die q<Usage: compute_bank_holidays(year)>;
93    my ($year) = @_;
94
95    my @dates = compute_public_holidays($year);
96    my $tm    = Time::Moment->new(year => $year);
97
98    # Midsommarafton (Midsummer's Eve), Friday that falls
99    # between June 19th to 25th.
100    push @dates, $tm->with_month(6)
101                    ->with_day_of_month(19)
102                    ->with(NextOrSameDayOfWeek(Friday));
103
104    # Julafton (Christmas Eve), December 24.
105    push @dates, $tm->with_month(12)
106                    ->with_day_of_month(24);
107
108    # Nyårsafton (New Year's Eve), December 31.
109    push @dates, $tm->with_month(12)
110                    ->with_day_of_month(31);
111
112    return sort { $a <=> $b }
113           grep { $_->day_of_week <= Friday } @dates;
114}
115
116my @tests = (
117    [ 2000, '2000-01-06', '2000-04-21', '2000-04-24', '2000-05-01', '2000-06-01',
118            '2000-06-12', '2000-06-23', '2000-12-25', '2000-12-26'               ],
119    [ 2001, '2001-01-01', '2001-04-13', '2001-04-16', '2001-05-01', '2001-05-24',
120            '2001-06-04', '2001-06-22', '2001-12-24', '2001-12-25', '2001-12-26',
121            '2001-12-31'                                                         ],
122    [ 2002, '2002-01-01', '2002-03-29', '2002-04-01', '2002-05-01', '2002-05-09',
123            '2002-05-20', '2002-06-21', '2002-12-24', '2002-12-25', '2002-12-26',
124            '2002-12-31'                                                         ],
125    [ 2003, '2003-01-01', '2003-01-06', '2003-04-18', '2003-04-21', '2003-05-01',
126            '2003-05-29', '2003-06-09', '2003-06-20', '2003-12-24', '2003-12-25',
127            '2003-12-26', '2003-12-31'                                           ],
128    [ 2004, '2004-01-01', '2004-01-06', '2004-04-09', '2004-04-12', '2004-05-20',
129            '2004-05-31', '2004-06-25', '2004-12-24', '2004-12-31'               ],
130    [ 2005, '2005-01-06', '2005-03-25', '2005-03-28', '2005-05-05', '2005-06-06',
131            '2005-06-24', '2005-12-26'                                           ],
132    [ 2006, '2006-01-06', '2006-04-14', '2006-04-17', '2006-05-01', '2006-05-25',
133            '2006-06-06', '2006-06-23', '2006-12-25', '2006-12-26'               ],
134    [ 2007, '2007-01-01', '2007-04-06', '2007-04-09', '2007-05-01', '2007-05-17',
135            '2007-06-06', '2007-06-22', '2007-12-24', '2007-12-25', '2007-12-26',
136            '2007-12-31'                                                         ],
137    [ 2008, '2008-01-01', '2008-03-21', '2008-03-24', '2008-05-01', '2008-05-01',
138            '2008-06-06', '2008-06-20', '2008-12-24', '2008-12-25', '2008-12-26',
139            '2008-12-31'                                                         ],
140    [ 2009, '2009-01-01', '2009-01-06', '2009-04-10', '2009-04-13', '2009-05-01',
141            '2009-05-21', '2009-06-19', '2009-12-24', '2009-12-25', '2009-12-31' ],
142    [ 2010, '2010-01-01', '2010-01-06', '2010-04-02', '2010-04-05', '2010-05-13',
143            '2010-06-25', '2010-12-24', '2010-12-31'                             ],
144    [ 2011, '2011-01-06', '2011-04-22', '2011-04-25', '2011-06-02', '2011-06-06',
145            '2011-06-24', '2011-12-26'                                           ],
146    [ 2012, '2012-01-06', '2012-04-06', '2012-04-09', '2012-05-01', '2012-05-17',
147            '2012-06-06', '2012-06-22', '2012-12-24', '2012-12-25', '2012-12-26',
148            '2012-12-31'                                                         ],
149    [ 2013, '2013-01-01', '2013-03-29', '2013-04-01', '2013-05-01', '2013-05-09',
150            '2013-06-06', '2013-06-21', '2013-12-24', '2013-12-25', '2013-12-26',
151            '2013-12-31'                                                         ],
152    [ 2014, '2014-01-01', '2014-01-06', '2014-04-18', '2014-04-21', '2014-05-01',
153            '2014-05-29', '2014-06-06', '2014-06-20', '2014-12-24', '2014-12-25',
154            '2014-12-26', '2014-12-31'                                           ],
155    [ 2015, '2015-01-01', '2015-01-06', '2015-04-03', '2015-04-06', '2015-05-01',
156            '2015-05-14', '2015-06-19', '2015-12-24', '2015-12-25', '2015-12-31' ],
157    [ 2016, '2016-01-01', '2016-01-06', '2016-03-25', '2016-03-28', '2016-05-05',
158            '2016-06-06', '2016-06-24', '2016-12-26'                             ],
159    [ 2017, '2017-01-06', '2017-04-14', '2017-04-17', '2017-05-01', '2017-05-25',
160            '2017-06-06', '2017-06-23', '2017-12-25', '2017-12-26'               ],
161    [ 2018, '2018-01-01', '2018-03-30', '2018-04-02', '2018-05-01', '2018-05-10',
162            '2018-06-06', '2018-06-22', '2018-12-24', '2018-12-25', '2018-12-26',
163            '2018-12-31'                                                         ],
164    [ 2019, '2019-01-01', '2019-04-19', '2019-04-22', '2019-05-01', '2019-05-30',
165            '2019-06-06', '2019-06-21', '2019-12-24', '2019-12-25', '2019-12-26',
166            '2019-12-31'                                                         ],
167    [ 2020, '2020-01-01', '2020-01-06', '2020-04-10', '2020-04-13', '2020-05-01',
168            '2020-05-21', '2020-06-19', '2020-12-24', '2020-12-25', '2020-12-31' ],
169);
170
171use Test::More 0.88;
172
173foreach my $test (@tests) {
174    my ($year, @exp) = @$test;
175    my @got = map {
176        $_->strftime('%Y-%m-%d')
177    } compute_bank_holidays($year);
178    is_deeply([@got], [@exp], "Swedish bank holidays for year $year");
179}
180
181done_testing();
182
183