File Coverage

blib/lib/Calendar/Indonesia/Holiday.pm
Criterion Covered Total %
statement 231 281 82.2
branch 52 128 40.6
condition 17 54 31.4
subroutine 27 32 84.3
pod 2 2 100.0
total 329 497 66.2


line stmt bran cond sub pod time code
1             package Calendar::Indonesia::Holiday;
2              
3 1     1   107193 use 5.010001;
  1         13  
4 1     1   5 use strict;
  1         2  
  1         33  
5 1     1   6 use warnings;
  1         2  
  1         27  
6             #use Log::ger;
7              
8 1     1   968 use DateTime;
  1         575189  
  1         68  
9 1     1   895 use Function::Fallback::CoreOrPP qw(clone);
  1         928  
  1         115  
10 1     1   818 use Perinci::Sub::Gen::AccessTable qw(gen_read_table_func);
  1         49609  
  1         77  
11 1     1   10 use Perinci::Sub::Util qw(err gen_modified_sub);
  1         3  
  1         10696  
12              
13             require Exporter;
14              
15             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
16             our $DATE = '2023-07-11'; # DATE
17             our $DIST = 'Calendar-Indonesia-Holiday'; # DIST
18             our $VERSION = '0.351'; # VERSION
19              
20             our @ISA = qw(Exporter);
21             our @EXPORT_OK = (
22             'list_idn_holidays',
23             'list_idn_workdays',
24             'count_idn_workdays',
25             'is_idn_holiday',
26             'is_idn_workday',
27             );
28              
29             our %SPEC;
30              
31             our %argspecs_date_or_day_month_year = (
32             day => {schema=>['int*', between=>[1,31]]},
33             month => {schema=>['int*', between=>[1, 12]]},
34             year => {schema=>'int*'},
35             date => {schema=>'str*', pos=>0},
36             );
37              
38             our %argsrels_date_or_day_month_year = (
39             choose_all => [qw/day month year/],
40             req_one => [qw/day date/],
41             );
42              
43             $SPEC{':package'} = {
44             v => 1.1,
45             summary => 'List Indonesian public holidays',
46             };
47              
48             my @fixed_holidays = (
49             my $newyear = {
50             day => 1, month => 1,
51             ind_name => "Tahun Baru",
52             eng_name => "New Year",
53             tags => [qw/international/],
54             fixed_date => 1,
55             },
56             my $indep = {
57             day => 17, month => 8,
58             ind_name => "Proklamasi",
59             eng_name => "Declaration Of Independence",
60             tags => [qw/national/],
61             fixed_date => 1,
62             },
63             my $christmas = {
64             day => 25, month => 12,
65             ind_name => "Natal",
66             eng_name => "Christmas",
67             tags => [qw/international religious religion=christianity/],
68             fixed_date => 1,
69             },
70             my $labord = {
71             day => 1, month => 5,
72             year_start => 2014,
73             ind_name => "Hari Buruh",
74             eng_name => "Labor Day",
75             tags => [qw/international/],
76             decree_date => "2013-04-29",
77             decree_note => "Labor day becomes national holiday since 2014, ".
78             "decreed by president",
79             fixed_date => 1,
80             },
81             my $pancasilad = {
82             day => 1, month => 6,
83             year_start => 2017,
84             ind_name => "Hari Lahir Pancasila",
85             eng_name => "Pancasila Day",
86             tags => [qw/national/],
87             decree_date => "2016-06-01",
88             decree_note => "Pancasila day becomes national holiday since 2017, ".
89             "decreed by president (Keppres 24/2016)",
90             # ref: http://www.kemendagri.go.id/media/documents/2016/08/03/k/e/keppres_no.24_th_2016.pdf
91             fixed_date => 1,
92             },
93             );
94              
95             sub _add_original_date {
96 369     369   617 my ($r, $opts) = @_;
97 369 100       850 if ($opts->{original_date}) {
98 14         36 $r->{ind_name} .= " (diperingati $opts->{original_date})";
99 14         34 $r->{eng_name} .= " (commemorated on $opts->{original_date})";
100             }
101             }
102              
103             sub _h_chnewyear {
104 22     22   46 my ($r, $opts) = @_;
105             $r->{ind_name} = "Tahun Baru Imlek".
106 22 50       86 ($opts->{hyear} ? " $opts->{hyear}":"");
107             $r->{eng_name} = "Chinese New Year".
108 22 50       67 ($opts->{hyear} ? " $opts->{hyear}":"");
109 22         57 _add_original_date($r, $opts);
110 22         38 $r->{ind_aliases} = [];
111 22         48 $r->{eng_aliases} = [];
112 22         39 $r->{is_holiday} = 1;
113 22         57 $r->{year_start} = 2003; # decreed in 2002 by megawati soekarnoputri
114 22         45 $r->{tags} = [qw/international calendar=lunar/];
115 22         89 ($r);
116             }
117              
118             sub _h_mawlid {
119 34     34   71 my ($r, $opts) = @_;
120 34         64 $r->{ind_name} = "Maulid Nabi Muhammad";
121 34         60 $r->{eng_name} = "Mawlid";
122 34         81 _add_original_date($r, $opts);
123 34         80 $r->{ind_aliases} = [qw/Maulud/];
124 34         83 $r->{eng_aliases} = ["Mawlid An-Nabi"];
125 34         58 $r->{is_holiday} = 1;
126 34         116 $r->{tags} = [qw/religious religion=islam calendar=lunar/];
127 34         105 ($r);
128             }
129              
130             sub _h_nyepi {
131 34     34   64 my ($r, $opts) = @_;
132             $r->{ind_name} = "Nyepi".
133 34 50       133 ($opts->{hyear} ? " $opts->{hyear}":"");
134             $r->{eng_name} = "Nyepi".
135 34 50       100 ($opts->{hyear} ? " $opts->{hyear}":"");
136 34         80 _add_original_date($r, $opts);
137 34         113 $r->{ind_aliases} = ["Tahun Baru Saka"];
138 34         82 $r->{eng_aliases} = ["Bali New Year", "Bali Day Of Silence"];
139 34         55 $r->{is_holiday} = 1;
140 34         91 $r->{tags} = [qw/religious religion=hinduism calendar=saka/];
141 34         103 ($r);
142             }
143              
144             sub _h_goodfri {
145 34     34   69 my ($r, $opts) = @_;
146 34         58 $r->{ind_name} = "Jum'at Agung";
147 34         74 $r->{eng_name} = "Good Friday";
148 34         82 _add_original_date($r, $opts);
149 34         80 $r->{ind_aliases} = ["Wafat Isa Al-Masih"];
150 34         80 $r->{eng_aliases} = [];
151 34         57 $r->{is_holiday} = 1;
152 34         103 $r->{tags} = [qw/religious religion=christianity/];
153 34         105 ($r);
154             }
155              
156             sub _h_vesakha {
157 34     34   71 my ($r, $opts) = @_;
158             $r->{ind_name} = "Waisyak".
159 34 50       131 ($opts->{hyear} ? " $opts->{hyear}":"");
160             $r->{eng_name} = "Vesakha".
161 34 50       99 ($opts->{hyear} ? " $opts->{hyear}":"");
162 34         79 _add_original_date($r, $opts);
163 34         77 $r->{ind_aliases} = [];
164 34         108 $r->{eng_aliases} = ["Vesak"];
165 34         59 $r->{is_holiday} = 1;
166 34         141 $r->{tags} = [qw/religious religion=buddhism/];
167 34         127 ($r);
168             }
169              
170             sub _h_ascension {
171 34     34   71 my ($r, $opts) = @_;
172 34         60 $r->{ind_name} = "Kenaikan Isa Al-Masih";
173 34         58 $r->{eng_name} = "Ascension Day";
174 34         78 _add_original_date($r, $opts);
175 34         82 $r->{ind_aliases} = [];
176 34         72 $r->{eng_aliases} = [];
177 34         68 $r->{is_holiday} = 1;
178 34         92 $r->{tags} = [qw/religious religion=christianity/];
179 34         142 ($r);
180             }
181              
182             sub _h_isramiraj {
183 35     35   83 my ($r, $opts) = @_;
184 35         65 $r->{ind_name} = "Isra Miraj";
185 35         62 $r->{eng_name} = "Isra And Miraj";
186 35         79 _add_original_date($r, $opts);
187 35         66 $r->{ind_aliases} = [];
188 35         76 $r->{eng_aliases} = [];
189 35         59 $r->{is_holiday} = 1;
190 35         123 $r->{tags} = [qw/religious religion=islam calendar=lunar/];
191 35         142 ($r);
192             }
193              
194             sub _h_eidulf {
195 71     71   148 my ($r, $opts) = @_;
196 71   50     138 $opts //= {};
197             my $ind_name0 = "Idul Fitri".
198 71 50       217 ($opts->{hyear} ? " $opts->{hyear}H":"");
199             my $eng_name0 = "Eid Ul-Fitr".
200 71 50       161 ($opts->{hyear} ? " $opts->{hyear}H":"");
201 71 50       226 $r->{ind_name} = $ind_name0.($opts->{day} ? " (Hari $opts->{day})":"");
202 71 50       195 $r->{eng_name} = $eng_name0.($opts->{day} ? " (Day $opts->{day})":"");
203 71         178 _add_original_date($r, $opts);
204 71         149 $r->{ind_aliases} = ["Lebaran"];
205 71         136 $r->{eng_aliases} = [];
206 71         114 $r->{is_holiday} = 1;
207 71         228 $r->{tags} = [qw/religious religion=islam calendar=lunar/];
208 71         244 ($r);
209             }
210              
211             sub _h_eidula {
212 35     35   72 my ($r, $opts) = @_;
213 35         59 $r->{ind_name} = "Idul Adha";
214 35         60 $r->{eng_name} = "Eid Al-Adha";
215 35         79 _add_original_date($r, $opts);
216 35         86 $r->{ind_aliases} = ["Idul Kurban"];
217 35         63 $r->{eng_aliases} = [];
218 35         52 $r->{is_holiday} = 1;
219 35         147 $r->{tags} = [qw/religious religion=islam calendar=lunar/];
220 35         139 ($r);
221             }
222              
223             sub _h_hijra {
224 35     35   63 my ($r, $opts) = @_;
225 35   50     76 $opts //= {};
226             $r->{ind_name} = "Tahun Baru Hijriyah".
227 35 50       130 ($opts->{hyear} ? " $opts->{hyear}H":"");
228             $r->{eng_name} = "Hijra".
229 35 50       111 ($opts->{hyear} ? " $opts->{hyear}H":"");
230 35         87 _add_original_date($r, $opts);
231 35         74 $r->{ind_aliases} = ["1 Muharam"];
232 35         62 $r->{eng_aliases} = [];
233 35         58 $r->{is_holiday} = 1;
234 35         123 $r->{tags} = [qw/calendar=lunar/];
235 35         139 ($r);
236             }
237              
238             sub _h_lelection {
239 7     7   13 my ($r, $opts) = @_;
240 7         13 $r->{ind_name} = "Pemilu Legislatif (Pileg)";
241 7         11 $r->{eng_name} = "Legislative Election";
242 7         12 $r->{is_holiday} = 1;
243 7         22 $r->{tags} = [qw/political/];
244              
245 7         17 for (qw(decree_date decree_note)) {
246 14 100       33 $r->{$_} = $opts->{$_} if defined $opts->{$_};
247             }
248 7         22 ($r);
249             }
250              
251             sub _h_pelection {
252 2     2   5 my ($r, $opts) = @_;
253 2         4 $r->{ind_name} = "Pemilu Presiden (Pilpres)";
254 2         3 $r->{eng_name} = "Presidential Election";
255 2         3 $r->{is_holiday} = 1;
256 2         4 $r->{tags} = [qw/political/];
257              
258 2         5 for (qw(decree_date decree_note)) {
259 4 50       21 $r->{$_} = $opts->{$_} if defined $opts->{$_};
260             }
261 2         5 ($r);
262             }
263              
264             sub _h_jrelection {
265 3     3   8 my ($r, $opts) = @_;
266 3         6 $r->{ind_name} = "Pilkada Serentak";
267 3         6 $r->{eng_name} = "Joint Regional Election";
268 3         7 $r->{is_holiday} = 1;
269 3         6 $r->{tags} = [qw/political/];
270              
271 3         7 for (qw(decree_date decree_note)) {
272 6 100       20 $r->{$_} = $opts->{$_} if defined $opts->{$_};
273             }
274 3         9 ($r);
275             }
276              
277             sub _jointlv {
278 97     97   182 my ($r, $opts) = @_;
279 97   50     177 $opts //= {};
280 97         160 my $h = $opts->{holiday};
281             $r->{ind_name} = "Cuti Bersama".
282 97 100 33     434 ($h ? " (".($h->{ind_name0} // $h->{ind_name}).")": "");
283             $r->{eng_name} = "Joint Leave".
284 97 100 33     369 ($h ? " (".($h->{eng_name0} // $h->{eng_name}).")": "");
285 97         259 $r->{ind_aliases} = [];
286 97         167 $r->{eng_aliases} = [];
287 97         150 $r->{is_joint_leave} = 1;
288 97         225 $r->{tags} = [];
289 97         351 ($r);
290             }
291              
292             # can operate on a single holiday or multiple ones
293             sub _make_tentative {
294 0     0   0 for my $arg (@_) {
295 0 0       0 push @{ $arg->{tags} }, 'tentative' unless grep { $_ eq 'tentative' } @{ $arg->{tags} };
  0         0  
  0         0  
  0         0  
296             }
297 0         0 @_;
298             }
299              
300             sub _make_jl_tentative {
301 0     0   0 my ($holidays) = @_;
302 0         0 for (@$holidays) {
303 0 0       0 _make_tentative($_) if $_->{is_joint_leave};
304             }
305 0         0 $holidays;
306             }
307              
308             sub _expand_dm {
309 477 50   477   1634 $_[0] =~ m!(\d+)[-/](\d+)! or die "Bug: bad dm syntax $_[0]";
310 477         3021 return (day => $1+0, month => $2+0);
311             }
312              
313             sub _uniquify_holidays {
314 34     34   101 my @holidays = @_;
315              
316 34         58 my %seen; # key=mm-dd (e.g. 12-25), val=[hol1, hol2, ...]
317 34         67 for my $h (@holidays) {
318 596         1835 my $k = sprintf "%02d-%02d", $h->{month}, $h->{day};
319 596   100     3155 $seen{$k} //= [];
320 596         840 push @{ $seen{$k} }, $h;
  596         1364  
321             }
322              
323 34         154 for my $k (keys %seen) {
324 594 100       775 if (@{ $seen{$k} } == 1) {
  594         1000  
325 592         1048 $seen{$k} = $seen{$k}[0];
326             } else {
327             my $h_mult = {
328             multiple => 1,
329 4         24 ind_name => join(", ", map {$_->{ind_name}} @{ $seen{$k} }),
  2         8  
330 4         21 eng_name => join(", ", map {$_->{eng_name}} @{ $seen{$k} }),
  2         9  
331 2         17 holidays => $seen{$k},
332             };
333             # join all the tags
334 2         11 my @tags;
335 2         4 for my $h (@{ $seen{$k} }) {
  2         7  
336 4 50       16 next unless $h->{tags};
337 4         7 for my $t (@{ $h->{tags} }) {
  4         10  
338 8 100       16 push @tags, $t unless grep { $_ eq $t } @tags;
  11         27  
339             }
340             }
341 2         41 $h_mult->{tags} = \@tags;
342             # join all the properties
343             PROP:
344 2         8 for my $prop (keys %{ $seen{$k}[0] }) {
  2         11  
345 24 100       49 next if exists $h_mult->{$prop};
346 18         29 my %vals;
347 18         27 for my $h (@{ $seen{$k} }) {
  18         33  
348 36 50       71 next PROP unless defined $h->{$prop};
349 36         113 $vals{ $h->{$prop} }++;
350             }
351 18 100       41 next if keys(%vals) > 1;
352 14         39 $h_mult->{$prop} = $seen{$k}[0]{$prop};
353             }
354 2         7 $seen{$k} = $h_mult;
355             }
356             }
357              
358 34         287 map { $seen{$_} } sort keys %seen;
  594         1315  
359             }
360              
361             sub _get_date_day_month_year {
362 0     0   0 my $args = shift;
363              
364 0         0 my ($y, $m, $d, $date);
365 0 0       0 if (defined $args->{date}) {
366 0 0       0 $args->{date} =~ /\A(\d{4})-(\d{1,2})-(\d{1,2})\z/
367             or return [400, "Invalid date syntax, please use 'YYYY-MM-DD' format"];
368 0         0 ($y, $m, $d) = ($1, $2, $3);
369             } else {
370             ($y = $args->{year}) && ($m = $args->{month}) && ($d = $args->{day})
371 0 0 0     0 or return [400, "Please specify day/month/year or date"];
      0        
372             }
373 0         0 $date = sprintf "%04d-%02d-%02d", $y, $m, $d;
374 0         0 [200, "OK", [$date, $y, $m, $d]];
375             }
376              
377             our %year_holidays;
378              
379             # decreed ?
380             # source: https://id.wikipedia.org/wiki/1990
381             {
382             $year_holidays{1990} = [
383             _h_isramiraj ({_expand_dm("23-02")}, {hyear=>1410}),
384             _h_nyepi ({_expand_dm("27-03")}, {hyear=>1912}),
385             _h_goodfri ({_expand_dm("13-04")}),
386             _h_eidulf ({_expand_dm("26-04")}, {hyear=>1410, day=>1}),
387             _h_eidulf ({_expand_dm("27-04")}, {hyear=>1410, day=>2}),
388             _h_vesakha ({_expand_dm("10-05")}, {hyear=>2534}),
389             _h_ascension ({_expand_dm("24-05")}),
390             _h_eidula ({_expand_dm("03-07")}, {hyear=>1410}),
391             _h_hijra ({_expand_dm("23-07")}, {hyear=>1411}),
392             _h_mawlid ({_expand_dm("01-10")}, {hyear=>1411}),
393             ];
394             }
395              
396             # decreed ?
397             # source: https://id.wikipedia.org/wiki/1991
398             {
399             $year_holidays{1991} = [
400             _h_isramiraj ({_expand_dm("12-02")}, {hyear=>1411}),
401             _h_nyepi ({_expand_dm("17-03")}, {hyear=>1913}),
402             _h_goodfri ({_expand_dm("29-03")}),
403             _h_eidulf ({_expand_dm("16-04")}, {hyear=>1411, day=>1}),
404             _h_eidulf ({_expand_dm("17-04")}, {hyear=>1411, day=>2}),
405             _h_ascension ({_expand_dm("09-05")}),
406             _h_vesakha ({_expand_dm("28-05")}, {hyear=>2535}),
407             _h_eidula ({_expand_dm("23-06")}, {hyear=>1411}),
408             _h_hijra ({_expand_dm("13-07")}, {hyear=>1412}),
409             _h_mawlid ({_expand_dm("21-09")}, {hyear=>1412}),
410             ];
411             }
412              
413             # decreed ?
414             # source: https://id.wikipedia.org/wiki/1992
415             {
416             $year_holidays{1992} = [
417             _h_isramiraj ({_expand_dm("01-02")}, {hyear=>1412}),
418             _h_nyepi ({_expand_dm("05-03")}, {hyear=>1914}),
419             _h_eidulf ({_expand_dm("05-04")}, {hyear=>1412, day=>1}),
420             _h_eidulf ({_expand_dm("06-04")}, {hyear=>1412, day=>2}),
421             _h_goodfri ({_expand_dm("17-04")}),
422             _h_vesakha ({_expand_dm("16-05")}, {hyear=>2536}),
423             _h_ascension ({_expand_dm("28-05")}),
424             _h_lelection ({_expand_dm("09-06")}, {}),
425             _h_eidula ({_expand_dm("11-06")}, {hyear=>1412}),
426             _h_hijra ({_expand_dm("02-07")}, {hyear=>1413}),
427             _h_mawlid ({_expand_dm("09-09")}, {hyear=>1413}),
428             ];
429             }
430              
431             # decreed ?
432             # source: https://id.wikipedia.org/wiki/1993
433             {
434             $year_holidays{1993} = [
435             _h_isramiraj ({_expand_dm("20-01")}, {hyear=>1413}),
436             _h_nyepi ({_expand_dm("24-03")}, {hyear=>1915}),
437             _h_eidulf ({_expand_dm("25-03")}, {hyear=>1413, day=>1}),
438             _h_eidulf ({_expand_dm("26-03")}, {hyear=>1413, day=>2}),
439             _h_goodfri ({_expand_dm("09-04")}),
440             _h_vesakha ({_expand_dm("06-05")}, {hyear=>2537}),
441             _h_ascension ({_expand_dm("20-05")}),
442             _h_eidula ({_expand_dm("01-06")}, {hyear=>1413}),
443             _h_hijra ({_expand_dm("21-06")}, {hyear=>1414}),
444             _h_mawlid ({_expand_dm("30-08")}, {hyear=>1414}),
445             ];
446             }
447              
448             # decreed ?
449             # source: https://id.wikipedia.org/wiki/1994
450             {
451             $year_holidays{1994} = [
452             _h_isramiraj ({_expand_dm("10-01")}, {hyear=>1414}),
453             _h_eidulf ({_expand_dm("14-03")}, {hyear=>1414, day=>1}),
454             _h_eidulf ({_expand_dm("15-03")}, {hyear=>1414, day=>2}),
455             _h_goodfri ({_expand_dm("01-04")}),
456             _h_nyepi ({_expand_dm("12-04")}, {hyear=>1916}),
457             _h_ascension ({_expand_dm("12-05")}),
458             _h_eidula ({_expand_dm("21-05")}, {hyear=>1414}),
459             _h_vesakha ({_expand_dm("25-05")}, {hyear=>2538}),
460             _h_hijra ({_expand_dm("11-06")}, {hyear=>1415}),
461             _h_mawlid ({_expand_dm("20-08")}, {hyear=>1415}),
462             _h_isramiraj ({_expand_dm("30-12")}, {hyear=>1415}),
463             ];
464             }
465              
466             # decreed ?
467             # source: https://id.wikipedia.org/wiki/1995
468             {
469             $year_holidays{1995} = [
470             _h_eidulf ({_expand_dm("03-03")}, {hyear=>1415, day=>1}),
471             _h_eidulf ({_expand_dm("04-03")}, {hyear=>1415, day=>2}),
472             _h_nyepi ({_expand_dm("01-04")}, {hyear=>1917}),
473             _h_goodfri ({_expand_dm("14-04")}),
474             _h_eidula ({_expand_dm("10-05")}, {hyear=>1415}),
475             _h_vesakha ({_expand_dm("15-05")}, {hyear=>2539}),
476             _h_ascension ({_expand_dm("25-05")}),
477             _h_hijra ({_expand_dm("31-05")}, {hyear=>1416}),
478             _h_mawlid ({_expand_dm("09-08")}, {hyear=>1416}),
479             _h_isramiraj ({_expand_dm("20-12")}, {hyear=>1416}),
480             ];
481             }
482              
483             # decreed ?
484             # source: https://id.wikipedia.org/wiki/1996
485             {
486             $year_holidays{1996} = [
487             _h_eidulf ({_expand_dm("20-02")}, {hyear=>1416, day=>1}),
488             _h_eidulf ({_expand_dm("21-02")}, {hyear=>1416, day=>2}),
489             _h_nyepi ({_expand_dm("21-03")}, {hyear=>1918}),
490             _h_goodfri ({_expand_dm("05-04")}),
491             _h_eidula ({_expand_dm("28-04")}, {hyear=>1416}),
492             _h_ascension ({_expand_dm("16-05")}),
493             _h_hijra ({_expand_dm("19-05")}, {hyear=>1417}),
494             _h_vesakha ({_expand_dm("02-06")}, {hyear=>2540}),
495             _h_mawlid ({_expand_dm("28-07")}, {hyear=>1417}),
496             _h_isramiraj ({_expand_dm("08-12")}, {hyear=>1417}),
497             ];
498             }
499              
500             # decreed ?
501             # source: https://id.wikipedia.org/wiki/1997
502             {
503             $year_holidays{1997} = [
504             _h_eidulf ({_expand_dm("09-02")}, {hyear=>1417, day=>1}),
505             _h_eidulf ({_expand_dm("10-02")}, {hyear=>1417, day=>2}),
506             _h_goodfri ({_expand_dm("28-03")}),
507             _h_nyepi ({_expand_dm("09-04")}, {hyear=>1919}),
508             _h_eidula ({_expand_dm("18-04")}, {hyear=>1417}),
509             _h_hijra ({_expand_dm("08-05")}, {hyear=>1418}), # coincide
510             _h_ascension ({_expand_dm("08-05")}), # coincide
511             _h_vesakha ({_expand_dm("22-05")}, {hyear=>2541}),
512             _h_lelection ({_expand_dm("29-05")}, {}),
513             _h_mawlid ({_expand_dm("17-07")}, {hyear=>1418}),
514             _h_isramiraj ({_expand_dm("28-11")}, {hyear=>1418}),
515             ];
516             }
517              
518             # decreed ?
519             # source: https://id.wikipedia.org/wiki/1998
520             {
521             $year_holidays{1998} = [
522             _h_eidulf ({_expand_dm("30-01")}, {hyear=>1418, day=>1}),
523             _h_eidulf ({_expand_dm("31-01")}, {hyear=>1418, day=>2}),
524             _h_nyepi ({_expand_dm("29-03")}, {hyear=>1920}),
525             _h_eidula ({_expand_dm("07-04")}, {hyear=>1418}),
526             _h_goodfri ({_expand_dm("10-04")}),
527             _h_hijra ({_expand_dm("28-04")}, {hyear=>1419}),
528             _h_vesakha ({_expand_dm("11-05")}, {hyear=>2542}),
529             _h_ascension ({_expand_dm("21-05")}),
530             _h_mawlid ({_expand_dm("06-07")}, {hyear=>1419}),
531             _h_isramiraj ({_expand_dm("17-11")}, {hyear=>1419}),
532             ];
533             }
534              
535             # decreed ?
536             # source: https://id.wikipedia.org/wiki/1999
537             {
538             $year_holidays{1999} = [
539             _h_eidulf ({_expand_dm("19-01")}, {hyear=>1419, day=>1}),
540             _h_eidulf ({_expand_dm("20-01")}, {hyear=>1419, day=>2}),
541             _h_nyepi ({_expand_dm("18-03")}, {hyear=>1921}),
542             _h_eidula ({_expand_dm("28-03")}, {hyear=>1419}),
543             _h_goodfri ({_expand_dm("02-04")}),
544             _h_hijra ({_expand_dm("17-04")}, {hyear=>1420}),
545             _h_ascension ({_expand_dm("13-05")}),
546             _h_vesakha ({_expand_dm("30-05")}, {hyear=>2543}),
547             _h_lelection ({_expand_dm("07-06")}, {}),
548             _h_mawlid ({_expand_dm("26-06")}, {hyear=>1420}),
549             _h_isramiraj ({_expand_dm("06-11")}, {hyear=>1420}),
550             ];
551             }
552              
553             # decreed ?
554             # source: https://id.wikipedia.org/wiki/2000
555             {
556             $year_holidays{2000} = [
557             _h_eidulf ({_expand_dm("08-01")}, {hyear=>1420, day=>1}),
558             _h_eidulf ({_expand_dm("09-01")}, {hyear=>1420, day=>2}),
559             _h_eidula ({_expand_dm("16-03")}, {hyear=>1420}),
560             _h_nyepi ({_expand_dm("04-04")}, {hyear=>1922}),
561             _h_hijra ({_expand_dm("06-04")}, {hyear=>1421}),
562             _h_goodfri ({_expand_dm("21-04")}),
563             _h_vesakha ({_expand_dm("18-05")}, {hyear=>2544}),
564             _h_ascension ({_expand_dm("01-06")}),
565             _h_mawlid ({_expand_dm("15-06")}, {hyear=>1421}),
566             _h_isramiraj ({_expand_dm("25-10")}, {hyear=>1421}),
567             _h_eidulf ({_expand_dm("16-12")}, {hyear=>1422, day=>1}),
568             _h_eidulf ({_expand_dm("17-12")}, {hyear=>1422, day=>2}),
569             ];
570             }
571              
572             # decreed ?
573             # source: https://id.wikipedia.org/wiki/2001
574             {
575             $year_holidays{2001} = [
576             _h_eidula ({_expand_dm("05-03")}, {hyear=>1421}),
577             _h_nyepi ({_expand_dm("25-03")}, {hyear=>1923}),
578             _h_hijra ({_expand_dm("26-03")}, {hyear=>1422}),
579             _h_goodfri ({_expand_dm("13-04")}),
580             _h_vesakha ({_expand_dm("07-05")}, {hyear=>2545}),
581             _h_ascension ({_expand_dm("24-05")}),
582             _h_mawlid ({_expand_dm("04-06")}, {hyear=>1422}),
583             _h_isramiraj ({_expand_dm("15-10")}, {hyear=>1422}),
584             _h_eidulf ({_expand_dm("16-12")}, {hyear=>1422, day=>1}),
585             _h_eidulf ({_expand_dm("17-12")}, {hyear=>1422, day=>2}),
586             ];
587             }
588              
589             # decreed ?
590             {
591             my $eidulf2002;
592             $year_holidays{2002} = [
593             _h_chnewyear ({_expand_dm("12-02")}, {hyear=>2553}),
594             _h_eidula ({_expand_dm("23-02")}, {hyear=>1422}),
595             _h_hijra ({_expand_dm("15-03")}, {hyear=>1423}),
596             _h_goodfri ({_expand_dm("29-03")}),
597             _h_nyepi ({_expand_dm("13-04")}, {hyear=>1924}),
598             _h_ascension ({_expand_dm("09-05")}),
599             _h_mawlid ({_expand_dm("25-05")}, {hyear=>1423, original_date=>'2003-05-14'}),
600             _h_vesakha ({_expand_dm("26-05")}, {hyear=>2546}),
601             _h_isramiraj ({_expand_dm("04-10")}, {hyear=>1423}),
602             ($eidulf2002 =
603             _h_eidulf ({_expand_dm("06-12")}, {hyear=>1423, day=>1})),
604             _h_eidulf ({_expand_dm("07-12")}, {hyear=>1423, day=>2}),
605              
606             _jointlv ({_expand_dm("05-12")}, {holiday=>$eidulf2002}),
607             _jointlv ({_expand_dm("09-12")}, {holiday=>$eidulf2002}),
608             _jointlv ({_expand_dm("10-12")}, {holiday=>$eidulf2002}),
609             _jointlv ({_expand_dm("26-12")}, {holiday=>$christmas}),
610             ];
611             }
612              
613             # decreed nov 25, 2002
614             {
615             my $eidulf2003;
616             $year_holidays{2003} = [
617             _h_chnewyear ({_expand_dm("01-02")}, {hyear=>2554}),
618             _h_eidula ({_expand_dm("12-02")}, {hyear=>1423}),
619             _h_hijra ({_expand_dm("03-03")}, {hyear=>1424, original_date=>'2003-03-04'}),
620             _h_nyepi ({_expand_dm("02-04")}, {hyear=>1925}),
621             _h_goodfri ({_expand_dm("18-04")}),
622             _h_mawlid ({_expand_dm("15-05")}, {original_date=>'2003-05-14'}),
623             _h_vesakha ({_expand_dm("16-05")}, {hyear=>2547}),
624             _h_ascension ({_expand_dm("30-05")}, {original_date=>'2003-05-29'}),
625             _h_isramiraj ({_expand_dm("22-09")}, {original_date=>'2003-09-24'}),
626             ($eidulf2003 =
627             _h_eidulf ({_expand_dm("25-11")}, {hyear=>1424, day=>1})),
628             _h_eidulf ({_expand_dm("26-11")}, {hyear=>1424, day=>2}),
629              
630             _jointlv ({_expand_dm("24-11")}, {holiday=>$eidulf2003}),
631             _jointlv ({_expand_dm("27-11")}, {holiday=>$eidulf2003}),
632             _jointlv ({_expand_dm("28-11")}, {holiday=>$eidulf2003}),
633             _jointlv ({_expand_dm("26-12")}, {holiday=>$christmas}),
634             ];
635             my $indep2003 = clone($indep); $indep2003->{day} = 18;
636             _add_original_date($indep2003, {original_date=>'2003-08-17'});
637             }
638              
639             # decreed jul 17, 2003
640             {
641             my $eidulf2004;
642             $year_holidays{2004} = [
643             _h_chnewyear ({_expand_dm("22-01")}, {hyear=>2555}),
644             _h_eidula ({_expand_dm("02-02")}, {hyear=>1424, original_date=>'2004-02-01'}),
645             _h_hijra ({_expand_dm("23-02")}, {hyear=>1425, original_date=>'2004-02-01'}),
646             _h_nyepi ({_expand_dm("22-03")}, {hyear=>1926}),
647             _h_lelection ({_expand_dm("05-04")}, {}),
648             _h_goodfri ({_expand_dm("09-04")}),
649             _h_mawlid ({_expand_dm("03-05")}, {original_date=>'2004-05-02'}),
650             _h_ascension ({_expand_dm("20-05")}),
651             _h_vesakha ({_expand_dm("03-06")}, {hyear=>2548}),
652             _h_isramiraj ({_expand_dm("13-09")}, {original_date=>'2004-09-12'}),
653             ($eidulf2004 =
654             _h_eidulf ({_expand_dm("14-11")}, {hyear=>1425, day=>1})),
655             _h_eidulf ({_expand_dm("15-11")}, {hyear=>1425, day=>2}),
656             _h_eidulf ({_expand_dm("16-11")}, {hyear=>1425, day=>3}),
657              
658             _jointlv ({_expand_dm("17-11")}, {holiday=>$eidulf2004}),
659             _jointlv ({_expand_dm("18-11")}, {holiday=>$eidulf2004}),
660             _jointlv ({_expand_dm("19-11")}, {holiday=>$eidulf2004}),
661             ];
662             }
663              
664             # decreed jul ??, 2004
665             {
666             my $eidulf2005;
667             $year_holidays{2005} = [
668             _h_eidula ({_expand_dm("21-01")}, {hyear=>1425}),
669             _h_chnewyear ({_expand_dm("09-02")}, {hyear=>2556}),
670             _h_hijra ({_expand_dm("10-02")}, {hyear=>1426}),
671             _h_nyepi ({_expand_dm("11-03")}, {hyear=>1927}),
672             _h_goodfri ({_expand_dm("25-03")}),
673             _h_mawlid ({_expand_dm("22-04")}, {original_date=>'2005-21-04'}),
674             _h_ascension ({_expand_dm("05-05")}),
675             _h_vesakha ({_expand_dm("24-05")}, {hyear=>2549}),
676             _h_isramiraj ({_expand_dm("02-09")}, {original_date=>'2005-09-01'}),
677             ($eidulf2005 =
678             _h_eidulf ({_expand_dm("03-11")}, {hyear=>1426, day=>1})),
679             _h_eidulf ({_expand_dm("04-11")}, {hyear=>1426, day=>2}),
680              
681             _jointlv ({_expand_dm("02-11")}, {holiday=>$eidulf2005}),
682             _jointlv ({_expand_dm("05-11")}, {holiday=>$eidulf2005}),
683             _jointlv ({_expand_dm("07-11")}, {holiday=>$eidulf2005}),
684             _jointlv ({_expand_dm("08-11")}, {holiday=>$eidulf2005}),
685             ];
686             }
687              
688             # decreed mar 22, 2006 (?)
689             {
690             my $nyepi2006;
691             my $ascension2006;
692             my $eidulf2006;
693             $year_holidays{2006} = [
694             _h_eidula ({_expand_dm("10-01")}, {hyear=>1426}),
695             _h_hijra ({_expand_dm("31-01")}, {hyear=>1427}),
696             _h_chnewyear ({_expand_dm("29-01")}, {hyear=>2557}),
697             ($nyepi2006 =
698             _h_nyepi ({_expand_dm("30-03")}, {hyear=>1928})),
699             _h_mawlid ({_expand_dm("10-04")}),
700             _h_goodfri ({_expand_dm("14-04")}),
701             _h_vesakha ({_expand_dm("13-05")}, {hyear=>2550}),
702             ($ascension2006 =
703             _h_ascension ({_expand_dm("25-05")})),
704             _h_isramiraj ({_expand_dm("21-08")}),
705             ($eidulf2006 =
706             _h_eidulf ({_expand_dm("24-10")}, {hyear=>1427, day=>1})),
707             _h_eidulf ({_expand_dm("25-10")}, {hyear=>1427, day=>2}),
708             _h_eidula ({_expand_dm("31-12")}, {hyear=>1427}),
709              
710             _jointlv ({_expand_dm("31-03")}, {holiday=>$nyepi2006}),
711             _jointlv ({_expand_dm("26-05")}, {holiday=>$ascension2006}),
712             _jointlv ({_expand_dm("18-08")}, {holiday=>$indep}),
713             _jointlv ({_expand_dm("23-10")}, {holiday=>$eidulf2006}),
714             _jointlv ({_expand_dm("26-10")}, {holiday=>$eidulf2006}),
715             _jointlv ({_expand_dm("27-10")}, {holiday=>$eidulf2006}),
716             ];
717             }
718              
719             # decreed jul 24, 2006
720             {
721             my $ascension2007;
722             my $eidulf2007;
723             $year_holidays{2007} = [
724             _h_hijra ({_expand_dm("20-01")}, {hyear=>1428}),
725             _h_chnewyear ({_expand_dm("18-02")}, {hyear=>2558}),
726             _h_nyepi ({_expand_dm("19-03")}, {hyear=>1929}),
727             _h_mawlid ({_expand_dm("31-03")}),
728             _h_goodfri ({_expand_dm("06-04")}),
729             ($ascension2007 =
730             _h_ascension ({_expand_dm("17-05")})),
731             _h_vesakha ({_expand_dm("01-06")}, {hyear=>2551}),
732             _h_isramiraj ({_expand_dm("11-08")}),
733             ($eidulf2007 =
734             _h_eidulf ({_expand_dm("13-10")}, {hyear=>1428, day=>1})),
735             _h_eidulf ({_expand_dm("14-10")}, {hyear=>1428, day=>2}),
736             _h_eidula ({_expand_dm("20-12")}, {hyear=>1428}),
737              
738             _jointlv ({_expand_dm("18-05")}, {holiday=>$ascension2007}),
739             _jointlv ({_expand_dm("12-10")}, {holiday=>$eidulf2007}),
740             _jointlv ({_expand_dm("15-10")}, {holiday=>$eidulf2007}),
741             _jointlv ({_expand_dm("16-10")}, {holiday=>$eidulf2007}),
742             _jointlv ({_expand_dm("21-12")}, {holiday=>$christmas}),
743             _jointlv ({_expand_dm("24-12")}, {holiday=>$christmas}),
744             ];
745             }
746              
747             # decreed feb 5, 2008 (?)
748             {
749             my $hijra2008a;
750             my $eidulf2008;
751             $year_holidays{2008} = [
752             ($hijra2008a =
753             _h_hijra ({_expand_dm("10-01")}, {hyear=>1429})),
754             _h_chnewyear ({_expand_dm("07-02")}, {hyear=>2559}),
755             _h_nyepi ({_expand_dm("07-03")}, {hyear=>1930}),
756             _h_mawlid ({_expand_dm("20-03")}),
757             _h_goodfri ({_expand_dm("21-03")}),
758             _h_ascension ({_expand_dm("01-05")}),
759             _h_vesakha ({_expand_dm("20-05")}, {hyear=>2552}),
760             _h_isramiraj ({_expand_dm("30-07")}),
761             ($eidulf2008 =
762             _h_eidulf ({_expand_dm("01-10")}, {hyear=>1429, day=>1})),
763             _h_eidulf ({_expand_dm("02-10")}, {hyear=>1429, day=>2}),
764             _h_eidula ({_expand_dm("08-12")}),
765             _h_hijra ({_expand_dm("29-12")}, {hyear=>1430}),
766              
767             _jointlv ({_expand_dm("11-01")}, {holiday=>$hijra2008a}),
768             _jointlv ({_expand_dm("29-09")}, {holiday=>$eidulf2008}),
769             _jointlv ({_expand_dm("30-09")}, {holiday=>$eidulf2008}),
770             _jointlv ({_expand_dm("03-10")}, {holiday=>$eidulf2008}),
771             _jointlv ({_expand_dm("26-12")}, {holiday=>$christmas}),
772             ];
773             }
774              
775             # decreed juni 9, 2008
776             {
777             my $eidulf2009;
778             $year_holidays{2009} = [
779             _h_chnewyear ({_expand_dm("26-01")}, {hyear=>2560}),
780             _h_mawlid ({_expand_dm("09-03")}),
781             _h_nyepi ({_expand_dm("26-03")}, {hyear=>1931}),
782             _h_lelection ({_expand_dm("09-04")}, {}),
783             _h_goodfri ({_expand_dm("10-04")}),
784             _h_vesakha ({_expand_dm("09-05")}, {hyear=>2553}),
785             _h_ascension ({_expand_dm("21-05")}),
786             _h_pelection ({_expand_dm("08-07")}, {}),
787             _h_isramiraj ({_expand_dm("20-07")}),
788             ($eidulf2009 =
789             _h_eidulf ({_expand_dm("21-09")}, {hyear=>1430, day=>1})),
790             _h_eidulf ({_expand_dm("22-09")}, {hyear=>1430, day=>2}),
791             _h_eidula ({_expand_dm("27-11")}),
792             _h_hijra ({_expand_dm("18-12")}, {hyear=>1431}),
793              
794             _jointlv ({_expand_dm("02-01")}, {holiday=>$newyear}),
795             _jointlv ({_expand_dm("18-09")}, {holiday=>$eidulf2009}),
796             _jointlv ({_expand_dm("23-09")}, {holiday=>$eidulf2009}),
797             _jointlv ({_expand_dm("24-12")}, {holiday=>$christmas}),
798             ];
799             }
800              
801             # decreed aug 7, 2009
802             {
803             my $eidulf2010;
804             $year_holidays{2010} = [
805             _h_chnewyear ({_expand_dm("14-02")}, {hyear=>2561}),
806             _h_mawlid ({_expand_dm("26-02")}),
807             _h_nyepi ({_expand_dm("16-03")}, {hyear=>1932}),
808             _h_goodfri ({_expand_dm("02-04")}),
809             _h_vesakha ({_expand_dm("28-05")}, {hyear=>2554}),
810             _h_ascension ({_expand_dm("02-06")}),
811             _h_isramiraj ({_expand_dm("10-07")}),
812             ($eidulf2010 =
813             _h_eidulf ({_expand_dm("10-09")}, {hyear=>1431, day=>1})),
814             _h_eidulf ({_expand_dm("11-09")}, {hyear=>1431, day=>2}),
815             _h_eidula ({_expand_dm("17-11")}),
816             _h_hijra ({_expand_dm("07-12")}, {hyear=>1432}),
817              
818             _jointlv ({_expand_dm("09-09")}, {holiday=>$eidulf2010}),
819             _jointlv ({_expand_dm("13-09")}, {holiday=>$eidulf2010}),
820             _jointlv ({_expand_dm("24-12")}, {holiday=>$christmas}),
821             ];
822             }
823              
824             # decreed jun 15, 2010
825             {
826             my $eidulf2011;
827             $year_holidays{2011} = [
828             _h_chnewyear ({_expand_dm("03-02")}, {hyear=>2562}),
829             _h_mawlid ({_expand_dm("16-02")}),
830             _h_nyepi ({_expand_dm("05-03")}, {hyear=>1933}),
831             _h_goodfri ({_expand_dm("22-04")}),
832             _h_vesakha ({_expand_dm("17-05")}, {hyear=>2555}),
833             _h_ascension ({_expand_dm("02-06")}),
834             _h_isramiraj ({_expand_dm("29-06")}),
835             ($eidulf2011 =
836             _h_eidulf ({_expand_dm("30-08")}, {hyear=>1432, day=>1})),
837             _h_eidulf ({_expand_dm("31-08")}, {hyear=>1432, day=>2}),
838             _h_eidula ({_expand_dm("07-11")}),
839             _h_hijra ({_expand_dm("27-11")}, {hyear=>1433}),
840              
841             _jointlv ({_expand_dm("29-08")}, {holiday=>$eidulf2011}),
842             _jointlv ({_expand_dm("01-09")}, {holiday=>$eidulf2011}),
843             _jointlv ({_expand_dm("02-09")}, {holiday=>$eidulf2011}),
844             _jointlv ({_expand_dm("26-12")}, {holiday=>$christmas}),
845             ];
846             }
847              
848             # decreed may 16, 2011
849             {
850             my $eidulf2012;
851             $year_holidays{2012} = [
852             _h_chnewyear ({_expand_dm("23-01")}, {hyear=>2563}),
853             _h_mawlid ({_expand_dm("04-02")}),
854             _h_nyepi ({_expand_dm("23-03")}, {hyear=>1934}),
855             _h_goodfri ({_expand_dm("06-04")}),
856             _h_vesakha ({_expand_dm("06-05")}, {hyear=>2556}),
857             _h_ascension ({_expand_dm("17-05")}),
858             _h_isramiraj ({_expand_dm("16-06")}),
859             ($eidulf2012 =
860             _h_eidulf ({_expand_dm("19-08")}, {hyear=>1433, day=>1})),
861             _h_eidulf ({_expand_dm("20-08")}, {hyear=>1433, day=>2}),
862             _h_eidula ({_expand_dm("26-10")}),
863             _h_hijra ({_expand_dm("15-11")}, {hyear=>1434}),
864              
865             _jointlv ({_expand_dm("21-08")}, {holiday=>$eidulf2012}),
866             _jointlv ({_expand_dm("22-08")}, {holiday=>$eidulf2012}),
867             _jointlv ({_expand_dm("26-12")}, {holiday=>$christmas}),
868             ];
869             }
870              
871             # decreed jul 19, 2012
872             {
873             my $eidulf2013;
874             my $eidula2013;
875             $year_holidays{2013} = [
876             _h_mawlid ({_expand_dm("24-01")}),
877             _h_chnewyear ({_expand_dm("10-02")}, {hyear=>2564}),
878             _h_nyepi ({_expand_dm("12-03")}, {hyear=>1935}),
879             _h_goodfri ({_expand_dm("29-03")}),
880             _h_ascension ({_expand_dm("09-05")}),
881             _h_vesakha ({_expand_dm("25-05")}, {hyear=>2557}),
882             _h_isramiraj ({_expand_dm("06-06")}),
883             ($eidulf2013 =
884             _h_eidulf ({_expand_dm("08-08")}, {hyear=>1434, day=>1})),
885             _h_eidulf ({_expand_dm("09-08")}, {hyear=>1434, day=>2}),
886             ($eidula2013 =
887             _h_eidula ({_expand_dm("15-10")})),
888             _h_hijra ({_expand_dm("05-11")}, {hyear=>1435}),
889              
890             _jointlv ({_expand_dm("05-08")}, {holiday=>$eidulf2013}),
891             _jointlv ({_expand_dm("06-08")}, {holiday=>$eidulf2013}),
892             _jointlv ({_expand_dm("07-08")}, {holiday=>$eidulf2013}),
893             _jointlv ({_expand_dm("14-10")}, {holiday=>$eidula2013}),
894             _jointlv ({_expand_dm("26-12")}, {holiday=>$christmas}),
895             ];
896             }
897              
898             # decreed aug 21, 2013
899             #
900             # Surat Keputusan Bersama MenPAN dan RB, Menteri Tenaga Kerja dan Transmigrasi,
901             # dan Menteri Agama, Rabu (21/8/2013).
902             #
903             # ref:
904             # - http://www.menpan.go.id/berita-terkini/1713-tahun-2014-libur-nasional-dan-cuti-bersama-19-hari
905             # - http://nasional.kompas.com/read/2013/08/21/1314422/2014.Ada.19.Hari.Libur.Nasional.dan.Cuti.Bersama
906             # - http://www.kaskus.co.id/thread/52145f5359cb175740000007/jadwal-hari-libur-nasional-amp-cuti-bersama-tahun-2014-resmi--download-kalender/
907             {
908             my $eidulf2014;
909             my $eidula2014;
910             $year_holidays{2014} = [
911             _h_mawlid ({_expand_dm("14-01")}),
912             _h_chnewyear ({_expand_dm("31-01")}, {hyear=>2565}),
913             _h_nyepi ({_expand_dm("31-03")}, {hyear=>1936}),
914             _h_lelection ({_expand_dm("09-04")}, {decree_date=>'2014-04-03', decree_note=>"Keppres 14/2014"}),
915             _h_goodfri ({_expand_dm("18-04")}),
916             _h_vesakha ({_expand_dm("15-05")}, {hyear=>2558}),
917             _h_isramiraj ({_expand_dm("27-05")}),
918             _h_ascension ({_expand_dm("29-05")}),
919              
920             # sudah ditetapkan KPU tapi belum ada keppres
921             _h_pelection ({_expand_dm("09-07")}, {}),
922              
923             ($eidulf2014 =
924             _h_eidulf ({_expand_dm("28-07")}, {hyear=>1435, day=>1})),
925             _h_eidulf ({_expand_dm("29-07")}, {hyear=>1435, day=>2}),
926             ($eidula2014 =
927             _h_eidula ({_expand_dm("05-10")}, {hyear=>1435})),
928             _h_hijra ({_expand_dm("25-10")}, {hyear=>1436}),
929              
930             _jointlv ({_expand_dm("30-07")}, {holiday=>$eidulf2014}),
931             _jointlv ({_expand_dm("31-07")}, {holiday=>$eidulf2014}),
932             _jointlv ({_expand_dm("01-08")}, {holiday=>$eidulf2014}),
933             _jointlv ({_expand_dm("26-12")}, {holiday=>$christmas}),
934             ];
935             }
936              
937             # decreed may 7, 2014
938             #
939             # Surat Keputusan Bersama Libur Nasional dan Cuti Bersama
940             #
941             # ref:
942             # - http://nasional.kompas.com/read/2014/05/07/1805155/Hari.Libur.dan.Cuti.Bersama.2015.Banyak.Long.Weekend.dan.Harpitnas.3
943             {
944             my $eidulf2015;
945             $year_holidays{2015} = [
946             _h_mawlid ({_expand_dm("03-01")}),
947             _h_chnewyear ({_expand_dm("19-02")}, {hyear=>2566}),
948             _h_nyepi ({_expand_dm("21-03")}, {hyear=>1937}),
949             _h_goodfri ({_expand_dm("03-04")}),
950             _h_ascension ({_expand_dm("14-05")}),
951             _h_isramiraj ({_expand_dm("16-05")}),
952             _h_vesakha ({_expand_dm("02-06")}, {hyear=>2559}),
953              
954             ($eidulf2015 =
955             _h_eidulf ({_expand_dm("17-07")}, {hyear=>1436, day=>1})),
956             _h_eidulf ({_expand_dm("18-07")}, {hyear=>1436, day=>2}),
957             _h_eidula ({_expand_dm("24-09")}, {hyear=>1436}),
958             _h_hijra ({_expand_dm("14-10")}, {hyear=>1437}),
959             _h_jrelection({_expand_dm("09-12")}, {decree_date => "2015-11-23"}),
960              
961             _jointlv ({_expand_dm("16-07")}, {holiday=>$eidulf2015}),
962             _jointlv ({_expand_dm("20-07")}, {holiday=>$eidulf2015}),
963             _jointlv ({_expand_dm("21-07")}, {holiday=>$eidulf2015}),
964             _jointlv ({_expand_dm("24-12")}, {holiday=>$christmas}),
965             ];
966             }
967              
968             # decreed jun 25, 2015
969             #
970             # ref:
971             # - http://id.wikipedia.org/wiki/2016#Hari_libur_nasional_di_Indonesia
972             # - http://www.merdeka.com/peristiwa/ini-daftar-hari-libur-nasional-dan-cuti-bersama-2016.html
973             {
974             my $eidulf2016;
975             $year_holidays{2016} = [
976             _h_chnewyear ({_expand_dm("08-02")}, {hyear=>2567}),
977             _h_nyepi ({_expand_dm("09-03")}, {hyear=>1938}),
978             _h_goodfri ({_expand_dm("25-03")}),
979             _h_ascension ({_expand_dm("05-05")}),
980             _h_isramiraj ({_expand_dm("06-05")}),
981             _h_vesakha ({_expand_dm("22-05")}, {hyear=>2560}),
982             ($eidulf2016 =
983             _h_eidulf ({_expand_dm("06-07")}, {hyear=>1437, day=>1})),
984             _h_eidulf ({_expand_dm("07-07")}, {hyear=>1437, day=>2}),
985             _h_eidula ({_expand_dm("12-09")}, {hyear=>1437}),
986             _h_hijra ({_expand_dm("02-10")}, {hyear=>1438}),
987             _h_mawlid ({_expand_dm("12-12")}),
988              
989             _jointlv ({_expand_dm("04-07")}, {holiday=>$eidulf2016}),
990             _jointlv ({_expand_dm("05-07")}, {holiday=>$eidulf2016}),
991             _jointlv ({_expand_dm("08-07")}, {holiday=>$eidulf2016}),
992             _jointlv ({_expand_dm("26-12")}, {holiday=>$christmas}),
993             ];
994             }
995              
996             # decreed apr 14, 2016
997             #
998             # ref:
999             # - https://id.wikipedia.org/wiki/2017#Hari_libur_nasional_di_Indonesia
1000             # - http://www.kemenkopmk.go.id/artikel/penetapan-hari-libur-nasional-dan-cuti-bersama-tahun-2017
1001             {
1002             my $eidulf2017;
1003             $year_holidays{2017} = [
1004             _h_chnewyear ({_expand_dm("28-01")}, {hyear=>2568}),
1005             _h_nyepi ({_expand_dm("28-03")}, {hyear=>1939}),
1006             _h_goodfri ({_expand_dm("14-04")}),
1007             _h_isramiraj ({_expand_dm("24-04")}, {hyear=>1438}),
1008             _h_vesakha ({_expand_dm("11-05")}, {hyear=>2561}),
1009             _h_ascension ({_expand_dm("25-05")}),
1010             ($eidulf2017 =
1011             _h_eidulf ({_expand_dm("25-06")}, {hyear=>1438, day=>1})),
1012             _h_eidulf ({_expand_dm("26-06")}, {hyear=>1438, day=>2}),
1013             _h_eidula ({_expand_dm("01-09")}, {hyear=>1438}),
1014             _h_hijra ({_expand_dm("21-09")}, {hyear=>1439}),
1015             _h_mawlid ({_expand_dm("01-12")}, {hyear=>1439}),
1016              
1017             _jointlv ({_expand_dm("23-06")}, {holiday=>$eidulf2017}), # ref: Keppres 18/2017 (2017-06-15)
1018             _jointlv ({_expand_dm("27-06")}, {holiday=>$eidulf2017}),
1019             _jointlv ({_expand_dm("28-06")}, {holiday=>$eidulf2017}),
1020             _jointlv ({_expand_dm("29-06")}, {holiday=>$eidulf2017}),
1021             _jointlv ({_expand_dm("30-06")}, {holiday=>$eidulf2017}),
1022             ];
1023             }
1024              
1025             # decreed oct 3, 2017
1026             #
1027             # ref:
1028             # - https://id.wikipedia.org/wiki/2018
1029             # - https://www.kemenkopmk.go.id/artikel/rakor-skb-3-menteri-tentang-hari-libur-nasional-dan-cuti-bersama-2018 (mar 13, 2017)
1030             # - http://news.liputan6.com/read/3116580/pemerintah-tetapkan-hari-libur-nasional-dan-cuti-bersama-2018
1031             {
1032             my $eidulf2018;
1033             $year_holidays{2018} = [
1034             # - new year
1035             _h_chnewyear ({_expand_dm("16-02")}, {hyear=>2569}),
1036             _h_nyepi ({_expand_dm("17-03")}, {hyear=>1940}),
1037             _h_goodfri ({_expand_dm("30-03")}),
1038             _h_isramiraj ({_expand_dm("14-04")}, {hyear=>1439}),
1039              
1040             # - labor day
1041             _h_ascension ({_expand_dm("10-05")}),
1042             _h_vesakha ({_expand_dm("29-05")}, {hyear=>2562}),
1043             # - pancasila day
1044             ($eidulf2018 =
1045             _h_eidulf ({_expand_dm("15-06")}, {hyear=>1439, day=>1})),
1046              
1047             _h_eidulf ({_expand_dm("16-06")}, {hyear=>1439, day=>2}),
1048             _h_jrelection({_expand_dm("27-06")}, {decree_date=>"2018-06-25"}),
1049             # - independence day
1050             _h_eidula ({_expand_dm("22-08")}, {hyear=>1439}),
1051             _h_hijra ({_expand_dm("11-09")}, {hyear=>1440}),
1052             _h_mawlid ({_expand_dm("20-11")}, {hyear=>1440}),
1053              
1054             # - christmas
1055              
1056             _jointlv ({_expand_dm("11-06")}, {holiday=>$eidulf2018}),
1057             _jointlv ({_expand_dm("12-06")}, {holiday=>$eidulf2018}),
1058             _jointlv ({_expand_dm("13-06")}, {holiday=>$eidulf2018}),
1059             _jointlv ({_expand_dm("14-06")}, {holiday=>$eidulf2018}),
1060             _jointlv ({_expand_dm("18-06")}, {holiday=>$eidulf2018}),
1061             _jointlv ({_expand_dm("19-06")}, {holiday=>$eidulf2018}),
1062             _jointlv ({_expand_dm("20-06")}, {holiday=>$eidulf2018}),
1063             _jointlv ({_expand_dm("24-12")}, {holiday=>$christmas}),
1064             ];
1065             }
1066              
1067             # decreed nov 13, 2018
1068             #
1069             # ref:
1070             # - https://jpp.go.id/humaniora/sosial-budaya/327328-pemerintah-resmi-menetapkan-hari-libur-nasional-2019
1071             {
1072             my $eidulf2019;
1073             $year_holidays{2019} = [
1074             # - new year
1075             _h_chnewyear ({_expand_dm("05-02")}, {hyear=>2570}),
1076             _h_nyepi ({_expand_dm("07-03")}, {hyear=>1941}),
1077             _h_isramiraj ({_expand_dm("03-04")}, {hyear=>1440}),
1078             _h_lelection ({_expand_dm("17-04")}),
1079             _h_goodfri ({_expand_dm("19-04")}),
1080             # - labor day
1081             _h_vesakha ({_expand_dm("19-05")}, {hyear=>2563}),
1082             _h_ascension ({_expand_dm("30-05")}),
1083             # - pancasila day
1084             ($eidulf2019 =
1085             _h_eidulf ({_expand_dm("05-06")}, {hyear=>1440, day=>1})),
1086             _h_eidulf ({_expand_dm("06-06")}, {hyear=>1440, day=>2}),
1087             _h_eidula ({_expand_dm("11-08")}, {hyear=>1440}),
1088             # - independence day
1089             _h_hijra ({_expand_dm("01-09")}, {hyear=>1441}),
1090             _h_mawlid ({_expand_dm("09-11")}, {hyear=>1441}),
1091             # - christmas
1092              
1093             _jointlv ({_expand_dm("03-06")}, {holiday=>$eidulf2019}),
1094             _jointlv ({_expand_dm("04-06")}, {holiday=>$eidulf2019}),
1095             _jointlv ({_expand_dm("07-06")}, {holiday=>$eidulf2019}),
1096             _jointlv ({_expand_dm("24-12")}, {holiday=>$christmas}),
1097             ];
1098             }
1099              
1100             # decreed aug 28, 2019 (SKB ministry of religion, ministry of employment, ministry of state apparatus empowerment 728/2019, 213/2019, 01/2019)
1101             # revised mar 9, 2020 (SKB 174/2020, 01/2020, 01/2020)
1102             # revised apr 9, 2020 (SKB 391/2020, 02/2020, 02/2020)
1103             # revised may 20, 2020 (SKB 440/2020, 03/2020, 03/2020)
1104             # revised dec 1, 2020 (SKB 744/2020, 05/2020, 06/2020)
1105             #
1106             # ref:
1107             # - https://www.kemenkopmk.go.id/sites/default/files/artikel/2020-04/SKB%20Perubahan%20Kedua%20Libnas%20%26%20Cutber%202020.pdf
1108             # - https://www.kemenkopmk.go.id/sites/default/files/pengumuman/2020-12/SKB%203%20Menteri%20tentang%20Perubahan%20ke-4%20Libnas%20%26%20Cutber%202020_0.pdf
1109              
1110             {
1111             my $hijra2020;
1112             my $eidula2020;
1113             my $eidulf2020;
1114             my $mawlid2020;
1115             $year_holidays{2020} = [
1116             # - new year
1117             _h_chnewyear ({_expand_dm("25-01")}, {hyear=>2571}),
1118             _h_isramiraj ({_expand_dm("22-03")}, {hyear=>1441}),
1119             _h_nyepi ({_expand_dm("25-03")}, {hyear=>1942}),
1120             _h_goodfri ({_expand_dm("10-04")}),
1121             # - labor day
1122             _h_vesakha ({_expand_dm("07-05")}, {hyear=>2564}),
1123             _h_ascension ({_expand_dm("21-05")}),
1124             _h_eidulf ({_expand_dm("24-05")}, {hyear=>1441, day=>1}),
1125             ($eidulf2020 = _h_eidulf({_expand_dm("25-05")}, {hyear=>1441, day=>2})),
1126             # - pancasila day
1127             ($eidula2020 = _h_eidula ({_expand_dm("31-07")}, {hyear=>1441})),
1128             # - independence day
1129             ($hijra2020 = _h_hijra ({_expand_dm("20-08")}, {hyear=>1442})),
1130             ($mawlid2020 = _h_mawlid({_expand_dm("29-10")}, {hyear=>1442})),
1131             _h_jrelection({_expand_dm("09-12")}, {decree_date => "2015-11-27"}), # keppres 20/2020 (2020-11-27), surat edaran menaker m/14/hk.04/xii/2020 (2020-12-07)
1132             # - christmas
1133             ];
1134              
1135             push @{ $year_holidays{2020} }, (
1136             _jointlv ({_expand_dm("21-08")}, {holiday=>$hijra2020}),
1137             _jointlv ({_expand_dm("28-10")}, {holiday=>$mawlid2020}),
1138             _jointlv ({_expand_dm("30-10")}, {holiday=>$mawlid2020}),
1139             _jointlv ({_expand_dm("24-12")}, {holiday=>$christmas}),
1140             _jointlv ({_expand_dm("31-12")}, {holiday=>$eidulf2020}),
1141             );
1142             }
1143              
1144             # decreed sep 10, 2020 (SKB No 642/2020, 4/2020, 4/2020)
1145             #
1146             # ref:
1147             # - https://www.menpan.go.id/site/berita-terkini/libur-nasional-dan-cuti-bersama-tahun-2021-sebanyak-23-hari
1148             # - https://www.kemenkopmk.go.id/sites/default/files/artikel/2020-09/SKB%20Cuti%20Bersama%20Tahun%202021.pdf
1149             #
1150             # revised feb 22, 2021: joint leave days reduced from 7 days to 2 days (SKB No. 281/2021, No. 1/2021, No. 1/2021)
1151             # ref:
1152             # - https://www.menpan.go.id/site/berita-terkini/cegah-penularan-covid-19-pemerintah-pangkas-cuti-bersama-2021-jadi-2-hari
1153             # - https://www.kemenkopmk.go.id/sites/default/files/pengumuman/2021-02/SKB%203%20Menteri%20tentang%20Perubahan%20Libnas%20%26%20Cutber%202021%20.pdf
1154             #
1155             # revised jun 18, 2021: hijra moved from aug 10 to aug 11, mawlid moved from oct 19 to oct 20, remove christmas/new year joint leave so total reduced from 2 -> 1 (SKB No. 712/2021, 1/2021, 3/2021)
1156             # ref:
1157             # - https://www.kemenkopmk.go.id/sites/default/files/pengumuman/2021-06/SKB%203%20Menteri%20tentang%20Perubahan%20kedua%20Libur%20Nasional%20dan%20Cuti%20Bersama%202021.pdf
1158              
1159             {
1160             my $isramiraj2021;
1161             my $eidulf2021;
1162             $year_holidays{2021} = [
1163             # - new year
1164             _h_chnewyear ({_expand_dm("12-02")}, {hyear=>2572}),
1165             _h_isramiraj ({_expand_dm("11-03")}, {hyear=>1442}),
1166             _h_nyepi ({_expand_dm("14-03")}, {hyear=>1943}),
1167             _h_goodfri ({_expand_dm("02-04")}),
1168             # - labor day
1169             _h_ascension ({_expand_dm("13-05")}),
1170             ($eidulf2021 = _h_eidulf ({_expand_dm("13-05")}, {hyear=>1442, day=>1})),
1171             _h_eidulf ({_expand_dm("14-05")}, {hyear=>1442, day=>2}),
1172             _h_vesakha ({_expand_dm("26-05")}, {hyear=>2565}),
1173             # - pancasila day
1174             _h_eidula ({_expand_dm("20-07")}, {hyear=>1442}),
1175             _h_hijra ({_expand_dm("11-08")}, {hyear=>1443, original_date=>"2021-08-10"}),
1176             # - independence day
1177             _h_mawlid ({_expand_dm("20-10")}, {hyear=>1443, original_date=>"2021-10-19"}),
1178             # - christmas
1179             ];
1180              
1181             push @{ $year_holidays{2021} }, (
1182             _jointlv ({_expand_dm("12-05")}, {holiday=>$eidulf2021}),
1183             );
1184             }
1185              
1186             # decreed sep 22, 2021 (SKB No 963/2021, 3/2021, 4/2021)
1187             #
1188             # ref:
1189             # - https://www.kemenkopmk.go.id/sites/default/files/pengumuman/2021-09/SKB%20Libnas%20%26%20Cuti%20Bersama%20Tahun%202022.pdf
1190             #
1191             # Eid Al-Adha is changed from 9 jul to 10 jul, ref:
1192             # - https://www.kemenag.go.id/read/pemerintah-tetapkan-iduladha-1443-h-jatuh-pada-10-juli-2022
1193             {
1194             $year_holidays{2022} = [
1195             # - new year
1196             _h_chnewyear ({_expand_dm("01-02")}, {hyear=>2573}),
1197             _h_isramiraj ({_expand_dm("28-02")}, {hyear=>1443}),
1198             _h_nyepi ({_expand_dm("03-03")}, {hyear=>1944}),
1199             _h_goodfri ({_expand_dm("15-04")}),
1200             # - labor day
1201             _h_eidulf ({_expand_dm("02-05")}, {hyear=>1443, day=>1}),
1202             _h_eidulf ({_expand_dm("03-05")}, {hyear=>1443, day=>2}),
1203             _h_vesakha ({_expand_dm("16-05")}, {hyear=>2566}),
1204             _h_ascension ({_expand_dm("26-05")}),
1205             # - pancasila day
1206             _h_eidula ({_expand_dm("10-07")}, {hyear=>1443}),
1207             _h_hijra ({_expand_dm("30-07")}, {hyear=>1444}),
1208             # - independence day
1209             _h_mawlid({_expand_dm("08-10")}, {hyear=>1444}),
1210             # - christmas
1211             ];
1212              
1213             # no joint leave days
1214             push @{ $year_holidays{2022} }, (
1215             );
1216             }
1217              
1218             # decreed oct 11, 2022 (SKB No 1066/2022, 3/2022, 3/2022)
1219             #
1220             # ref:
1221             # - https://www.kemenkopmk.go.id/pemerintah-terapkan-hari-libur-nasional-dan-cuti-bersama-tahun-2023
1222             #
1223             # superseded mar 29, 2023 (SKB No 327/2023, 1/2023, 1/2023)
1224             # ref:
1225             # - https://setkab.go.id/pemerintah-terbitkan-skb-perubahan-libur-nasional-dan-cuti-bersama-2023/
1226             #
1227             # superseded jun 16, 2023 (SKB No 624/2023, 2/2023, 2/2023)
1228             # ref:
1229             # -
1230             {
1231             # 2023 holidays
1232             my ($chnewyear2023, $nyepi2023, $eidulf2023, $eidula2023, $vesakha2023, $christmas);
1233             $year_holidays{2023} = [
1234             # - new year
1235             ($chnewyear2023 = _h_chnewyear ({_expand_dm("22-01")}, {hyear=>2574})),
1236             _h_isramiraj ({_expand_dm("18-02")}, {hyear=>1444}),
1237             ($nyepi2023 = _h_nyepi ({_expand_dm("22-03")}, {hyear=>1945})),
1238             _h_goodfri ({_expand_dm("07-04")}),
1239             ($eidulf2023 = _h_eidulf ({_expand_dm("22-04")}, {hyear=>1444, day=>1})),
1240             _h_eidulf ({_expand_dm("23-04")}, {hyear=>1444, day=>2}),
1241             # - labor day
1242             _h_ascension ({_expand_dm("18-05")}),
1243             # - pancasila day
1244             _h_vesakha ({_expand_dm("04-06")}, {hyear=>2567}),
1245             ($eidula2023 = _h_eidula ({_expand_dm("29-06")}, {hyear=>1444})),
1246             _h_hijra ({_expand_dm("19-07")}, {hyear=>1445}),
1247             # - independence day
1248             _h_mawlid({_expand_dm("28-09")}, {hyear=>1445}),
1249             # - christmas
1250             ];
1251              
1252             push @{ $year_holidays{2023} }, (
1253             _jointlv ({_expand_dm("23-01")}, {holiday=>$chnewyear2023}),
1254             _jointlv ({_expand_dm("23-03")}, {holiday=>$nyepi2023}),
1255             _jointlv ({_expand_dm("19-04")}, {holiday=>$eidulf2023}),
1256             _jointlv ({_expand_dm("20-04")}, {holiday=>$eidulf2023}),
1257             _jointlv ({_expand_dm("21-04")}, {holiday=>$eidulf2023}),
1258             _jointlv ({_expand_dm("24-04")}, {holiday=>$eidulf2023}),
1259             _jointlv ({_expand_dm("25-04")}, {holiday=>$eidulf2023}),
1260             _jointlv ({_expand_dm("02-06")}, {holiday=>$vesakha2023}),
1261             _jointlv ({_expand_dm("28-06")}, {holiday=>$eidula2023}),
1262             _jointlv ({_expand_dm("30-06")}, {holiday=>$eidula2023}),
1263             _jointlv ({_expand_dm("26-12")}, {holiday=>$christmas}),
1264             );
1265             }
1266              
1267             {
1268             # 2024 holidays
1269             1;
1270             }
1271              
1272             {
1273             # 2025 holidays
1274             #_h_jrelection({_expand_dm("09-12")}, {decree_date => "2025-11-xx"}), # keppres xx/2025, surat edaran menaker xxx.../2020 (2020-12-xx)
1275             1;
1276             }
1277              
1278             {
1279             # 2026 holidays
1280             1;
1281             }
1282              
1283              
1284             my @years = sort keys %year_holidays;
1285             our $min_year = $years[0];
1286             our $max_year = $years[-1];
1287             our $max_joint_leave_year;
1288             for my $y (reverse @years) {
1289             if (grep {$_->{is_joint_leave}} @{$year_holidays{$y}}) {
1290             $max_joint_leave_year = $y;
1291             last;
1292             }
1293             }
1294              
1295             my @holidays;
1296             for my $year ($min_year .. $max_year) {
1297             my @hf;
1298             for my $h0 (@fixed_holidays) {
1299             next if $h0->{year_start} && $year < $h0->{year_start};
1300             next if $h0->{year_en} && $year > $h0->{year_end};
1301             my $h = clone $h0;
1302             push @{$h->{tags}}, "fixed-date";
1303             $h->{is_holiday} = 1;
1304             $h->{is_joint_leave} = 0;
1305             push @hf, $h;
1306             }
1307              
1308             my @hy;
1309             for my $h0 (@{$year_holidays{$year}}) {
1310             my $h = clone $h0;
1311             $h->{is_holiday} //= 0;
1312             $h->{is_joint_leave} //= 0;
1313             delete $h->{ind_name0};
1314             delete $h->{eng_name0};
1315             push @hy, $h;
1316             }
1317              
1318             for my $h (@hf, @hy) {
1319             $h->{year} = $year;
1320             my $dt = DateTime->new(year=>$year, month=>$h->{month}, day=>$h->{day});
1321             $h->{date} = $dt->ymd;
1322             $h->{dow} = $dt->day_of_week;
1323             }
1324              
1325             push @holidays, _uniquify_holidays(@hf, @hy);
1326             }
1327              
1328             my $res = gen_read_table_func(
1329             name => 'list_idn_holidays',
1330             table_data => \@holidays,
1331             table_spec => {
1332             fields => {
1333             date => {
1334             schema => 'date*',
1335             pos => 0,
1336             },
1337             day => {
1338             schema => 'int*',
1339             pos => 1,
1340             },
1341             month => {
1342             schema => 'int*',
1343             pos => 2,
1344             },
1345             year => {
1346             schema => 'int*',
1347             pos => 3,
1348             },
1349             dow => {
1350             schema => 'int*',
1351             summary => 'Day of week (1-7, Monday is 1)',
1352             pos => 4,
1353             },
1354             eng_name => {
1355             schema => 'str*',
1356             summary => 'English name',
1357             pos => 5,
1358             },
1359             ind_name => {
1360             schema => 'str*',
1361             summary => 'Indonesian name',
1362             pos => 6,
1363             },
1364             eng_aliases => {
1365             schema => ['array*'=>{of=>'str*'}],
1366             summary => 'English other names, if any',
1367             pos => 7,
1368             },
1369             ind_aliases => {
1370             schema => ['array*'=>{of=>'str*'}],
1371             summary => 'Indonesian other names, if any',
1372             pos => 8,
1373             },
1374             is_holiday => {
1375             schema => 'bool*',
1376             pos => 9,
1377             },
1378             is_joint_leave => {
1379             schema => 'bool*',
1380             summary => 'Whether this date is a joint leave day '.
1381             '("cuti bersama")',
1382             pos => 10,
1383             },
1384             decree_date => {
1385             schema => 'str',
1386             pos => 11,
1387             },
1388             decree_note => {
1389             schema => 'str',
1390             pos => 12,
1391             },
1392             note => {
1393             schema => 'str',
1394             pos => 13,
1395             },
1396             tags => {
1397             schema => 'array*',
1398             pos => 14,
1399             },
1400             },
1401             pk => 'date',
1402             },
1403             langs => ['en_US', 'id_ID'],
1404             );
1405              
1406             die "BUG: Can't generate func: $res->[0] - $res->[1]"
1407             unless $res->[0] == 200;
1408              
1409             delete $SPEC{list_idn_holidays}{args}{queries}{pos};
1410             delete $SPEC{list_idn_holidays}{args}{queries}{slurpy};
1411             $SPEC{list_idn_holidays}{args}{year}{pos} = 0;
1412             $SPEC{list_idn_holidays}{args}{month}{pos} = 1;
1413              
1414             my $TEXT_AVAILABLE_YEARS = "Contains data from years $min_year to $max_year";
1415              
1416             my $TEXT_WORKDAY_DEFINITION = <<'_';
1417             Working day is defined as day that is not Saturday*/Sunday/holiday/joint leave
1418             days*. If work_saturdays is set to true, Saturdays are also counted as working
1419             days. If observe_joint_leaves is set to false, joint leave days are also counted
1420             as working days.
1421             _
1422              
1423             my $meta = $res->[2]{meta};
1424             $meta->{summary} = "List Indonesian holidays in calendar";
1425             $meta->{description} = <<"_";
1426              
1427             List holidays and joint leave days ("cuti bersama").
1428              
1429             $TEXT_AVAILABLE_YEARS
1430              
1431             _
1432              
1433             sub _check_date_arg {
1434 6     6   26 my ($date) = @_;
1435 6 50 33     52 if (ref($date) && $date->isa('DateTime')) {
    50          
1436 0         0 return $date;
1437             } elsif ($date =~ /\A(\d{4})-(\d{2})-(\d{2})\z/) {
1438 6         29 return DateTime->new(year=>$1, month=>$2, day=>$3);
1439             } else {
1440 0         0 return;
1441             }
1442             }
1443              
1444             $SPEC{list_idn_workdays} = {
1445             v => 1.1,
1446             summary => 'List working days (non-holiday business days) for a certain period',
1447             description => <<"_",
1448              
1449             $TEXT_WORKDAY_DEFINITION
1450              
1451             $TEXT_AVAILABLE_YEARS
1452              
1453             _
1454             args => {
1455             start_date => {
1456             summary => 'Starting date',
1457             schema => 'str*',
1458             pos => 0,
1459             description => <<'_',
1460              
1461             Defaults to start of current month. Either a string in the form of "YYYY-MM-DD",
1462             or a DateTime object, is accepted.
1463              
1464             _
1465             },
1466             end_date => {
1467             summary => 'End date',
1468             schema => 'str*',
1469             pos => 1,
1470             description => <<'_',
1471              
1472             Defaults to end of current month. Either a string in the form of "YYYY-MM-DD",
1473             or a DateTime object, is accepted.
1474              
1475             _
1476             },
1477             work_saturdays => {
1478             schema => ['bool' => {default=>0}],
1479             summary => 'If set to 1, Saturday is a working day',
1480             },
1481             observe_joint_leaves => {
1482             summary => 'If set to 0, do not observe joint leave as holidays',
1483             schema => ['bool' => {default => 1}],
1484             cmdline_aliases => {j=>{}},
1485             },
1486             },
1487             };
1488             sub list_idn_workdays {
1489 3     3 1 24 my %args = @_;
1490              
1491             # XXX args
1492 3         20 my $now = DateTime->now;
1493 3         1179 my $som = DateTime->new(year => $now->year, month => $now->month, day => 1);
1494 3         1048 my $eom = $som->clone->add(months=>1)->subtract(days=>1);
1495 3 50 33     7439 my $start_date = _check_date_arg($args{start_date} // $som) or
1496             return [400, "Invalid start_date, must be string 'YYYY-MM-DD' ".
1497             "or DateTime object"];
1498 3 50 33     920 my $end_date = _check_date_arg($args{end_date} // $eom) or
1499             return [400, "Invalid end_date, must be string 'YYYY-MM-DD' ".
1500             "or DateTime object"];
1501 3         934 for ($start_date, $end_date) {
1502 6 50       37 return [400, "Sorry, no data for year earlier than $min_year available"]
1503             if $_->year < $min_year;
1504 6 50       52 return [400, "Sorry, no data for year newer than $max_year available"]
1505             if $_->year > $max_year;
1506             }
1507 3   100     58 my $work_saturdays = $args{work_saturdays} // 0;
1508 3   100     19 my $observe_joint_leaves = $args{observe_joint_leaves} // 1;
1509              
1510 3         5 my @args;
1511 3         11 push @args, "year.min"=>$start_date->year;
1512 3         22 push @args, "year.max"=>$end_date->year;
1513 3 100       23 push @args, (is_holiday=>1) if !$observe_joint_leaves;
1514 3         15 my $res = list_idn_holidays(@args);
1515 3 50       68763 return err(500, "Can't list holidays", $res)
1516             unless $res->[0] == 200;
1517             #use Data::Dump; dd $res;
1518              
1519 3         7 my @wd;
1520 3         14 my $dt = $start_date->clone->subtract(days=>1);
1521 3         3627 while (1) {
1522 99         422 $dt->add(days=>1);
1523 99 100       97657 next if $dt->day_of_week == 7;
1524 84 100 100     456 next if $dt->day_of_week == 6 && !$work_saturdays;
1525 74 100       389 last if DateTime->compare($dt, $end_date) > 0;
1526 71         5235 my $ymd = $dt->ymd;
1527 71 100       985 next if grep { $_ eq $ymd } @{$res->[2]};
  1190         2057  
  71         186  
1528 69         186 push @wd, $ymd;
1529             }
1530              
1531 3         292 [200, "OK", \@wd];
1532             }
1533              
1534             gen_modified_sub(
1535             output_name => 'count_idn_workdays',
1536             summary => "Count working days (non-holiday business days) for a certain period",
1537              
1538             base_name => 'list_idn_workdays',
1539             output_code => sub {
1540 3     3   62650 my $res = list_idn_workdays(@_);
1541 3 50       16 return $res unless $res->[0] == 200;
1542 3         9 $res->[2] = @{$res->[2]};
  3         8  
1543 3         21 $res;
1544             },
1545             );
1546              
1547             $SPEC{is_idn_holiday} = {
1548             v => 1.1,
1549             summary => 'Check whether a date is an Indonesian holiday',
1550             description => <<'_',
1551              
1552             Will return boolean if a given date is a holiday. A joint leave day will not
1553             count as holiday unless you specify `include_joint_leave` option.
1554              
1555             Date can be given using separate `day` (of month), `month`, and `year`, or as a
1556             single YYYY-MM-DD date.
1557              
1558             Will return undef (exit code 2 on CLI) if year is not within range of the
1559             holiday data.
1560              
1561             Note that you can also use `list_idn_holidays` to check whether a `date` (or a
1562             combination of `day`, `month`, and `year`) is a holiday , but `is_idn_holiday`
1563             is slightly more efficient, its `include_joint_leave` option is more convenient,
1564             and it offers a few more options.
1565              
1566             _
1567             args => {
1568             %argspecs_date_or_day_month_year,
1569              
1570             include_joint_leave => {schema=>'bool*', cmdline_aliases=>{j=>{}}},
1571             reverse => {schema=>'bool*', cmdline_aliases=>{r=>{}}},
1572             quiet => {schema=>'bool*', cmdline_aliases=>{q=>{}}},
1573             detail => {schema=>'bool*', cmdline_aliases=>{l=>{}}},
1574             },
1575             args_rels => {
1576             %argsrels_date_or_day_month_year,
1577             },
1578             };
1579             sub is_idn_holiday {
1580 0     0 1   my %args = @_;
1581              
1582 0           my $res = _get_date_day_month_year(\%args);
1583 0 0         return $res unless $res->[0] == 200;
1584 0           my ($date, $y, $m, $d) = @{ $res->[2] };
  0            
1585              
1586 0           for my $e (@fixed_holidays) {
1587 0 0 0       next if defined $e->{year_start} && $y < $e->{year_start};
1588 0 0 0       next if defined $e->{year_end} && $y > $e->{year_end};
1589 0 0 0       next unless $e->{day} == $d && $e->{month} == $m;
1590             return [200, "OK", ($args{reverse} ? 0 : ($args{detail} ? $e : 1)), {
1591             'cmdline.exit_code' => ($args{reverse} ? 1 : 0),
1592             ('cmdline.result' => ($args{quiet} ? '' : "Date $date IS a holiday")) x !$args{detail},
1593 0 0         }];
    0          
    0          
    0          
1594             }
1595              
1596 0 0 0       unless ($y >= $min_year && $y <= $max_year) {
1597             return [200, "OK", undef, {
1598             'cmdline.exit_code' => 2,
1599 0 0         'cmdline.result' => ($args{quiet} ? '' : "Date year ($y) is not within range of holiday data ($min_year-$max_year)"),
1600             }];
1601             }
1602              
1603 0           for my $e (@{ $year_holidays{$y} }) {
  0            
1604 0 0 0       next unless $e->{day} == $d && $e->{month} == $m;
1605 0 0 0       next if $e->{is_joint_leave} && !$args{include_joint_leave};
1606             return [200, "OK", ($args{reverse} ? 0 : ($args{detail} ? $e : 1)), {
1607             'cmdline.exit_code' => ($args{reverse} ? 1 : 0),
1608             ('cmdline.result' => ($args{quiet} ? '' : "Date $date IS a holiday")) x !$args{detail},
1609 0 0         }];
    0          
    0          
    0          
1610             }
1611              
1612             return [200, "OK", ($args{reverse} ? 1:0), {
1613             'cmdline.exit_code' => ($args{reverse} ? 0 : 1),
1614 0 0         'cmdline.result' => ($args{quiet} ? '' : "Date $date is NOT a holiday"),
    0          
    0          
1615             }];
1616             }
1617              
1618             gen_modified_sub(
1619             output_name => 'is_idn_workday',
1620             summary => "Check whether a date is a working day (non-holiday business day)",
1621             base_name => 'count_idn_workdays',
1622             modify_meta => sub {
1623             my $meta = shift;
1624             delete $meta->{args}{start_date};
1625             delete $meta->{args}{end_date};
1626             $meta->{args}{$_} = $argspecs_date_or_day_month_year{$_}
1627             for keys %argspecs_date_or_day_month_year;
1628             $meta->{args_rels} = {
1629             %argsrels_date_or_day_month_year,
1630             };
1631             },
1632             output_code => sub {
1633 0     0     my %args = @_;
1634              
1635 0           my $res = _get_date_day_month_year(\%args);
1636 0 0         return $res unless $res->[0] == 200;
1637 0           my ($date, $y, $m, $d) = @{ $res->[2] };
  0            
1638              
1639 0           delete $args{date};
1640 0           delete $args{day};
1641 0           delete $args{month};
1642 0           delete $args{year};
1643              
1644 0           $res = count_idn_workdays(%args, start_date=>$date, end_date=>$date);
1645 0 0         return $res unless $res->[0] == 200;
1646 0           $res;
1647             },
1648             );
1649              
1650             1;
1651             # ABSTRACT: List Indonesian public holidays
1652              
1653             __END__
1654              
1655             =pod
1656              
1657             =encoding UTF-8
1658              
1659             =head1 NAME
1660              
1661             Calendar::Indonesia::Holiday - List Indonesian public holidays
1662              
1663             =head1 VERSION
1664              
1665             This document describes version 0.351 of Calendar::Indonesia::Holiday (from Perl distribution Calendar-Indonesia-Holiday), released on 2023-07-11.
1666              
1667             =head1 SYNOPSIS
1668              
1669             use Calendar::Indonesia::Holiday qw(
1670             list_idn_holidays
1671             list_idn_workdays
1672              
1673             count_idn_workdays
1674              
1675             is_idn_holiday
1676             is_idn_workday
1677             );
1678              
1679             This lists Indonesian holidays for the year 2011, without the joint leave days
1680             ("cuti bersama"), showing only the dates:
1681              
1682             my $res = list_idn_holidays(year => 2011, is_joint_leave=>0);
1683              
1684             Sample result:
1685              
1686             [200, "OK", [
1687             '2011-01-01',
1688             '2011-02-03',
1689             '2011-02-16',
1690             '2011-03-05',
1691             '2011-04-22',
1692             '2011-05-17',
1693             '2011-06-02',
1694             '2011-06-29',
1695             '2011-08-17',
1696             '2011-08-31',
1697             '2011-09-01',
1698             '2011-11-07',
1699             '2011-11-27',
1700             '2011-12-25',
1701             ]];
1702              
1703             This lists religious Indonesian holidays, showing full details:
1704              
1705             my $res = list_idn_holidays(year => 2011,
1706             "tags.has" => ['religious'], detail=>1);
1707              
1708             Sample result:
1709              
1710             [200, "OK", [
1711             {date => '2011-02-16',
1712             day => 16,
1713             month => 2,
1714             year => 2011,
1715             ind_name => 'Maulid Nabi Muhammad',
1716             eng_name => 'Mawlid',
1717             eng_aliases => ['Mawlid An-Nabi'],
1718             ind_aliases => ['Maulud'],
1719             is_holiday => 1,
1720             tags => [qw/religious religion=islam calendar=lunar/],
1721             },
1722             ...
1723             ]];
1724              
1725             This checks whether 2011-02-16 is a holiday:
1726              
1727             my $res = is_idn_holiday(date => '2011-02-16');
1728             print "2011-02-16 is a holiday\n" if $res->[2];
1729              
1730             This checks whether 2021-03-11 is a working day:
1731              
1732             my $res = is_idn_workday(date => '2021-03-11');
1733             print "2011-02-16 is a holiday\n" if $res->[2];
1734              
1735             This lists working days for a certain period:
1736              
1737             my $res = list_idn_workdays(start_date=>'2021-01-01', end_date=>'2021-06-30');
1738              
1739             Idem, but returns a number instead. If unspecified, C<start_date> defaults to
1740             start of current month and C<end_date> defaults to end of current month. So this
1741             returns the number of working days in the current month:
1742              
1743             my $res = count_idn_workdays();
1744              
1745             =head1 DESCRIPTION
1746              
1747             This module provides functions to list Indonesian holidays. There is a
1748             command-line script interface for this module: L<list-idn-holidays> and a few
1749             others distributed in L<App::IndonesianHolidayUtils> distribution.
1750              
1751             Calendar years supported: 1990-2023.
1752              
1753             Note: Note that sometimes the holiday (as set by law) falls at a different date
1754             than the actual religious commemoration date. When you use the C<detail> option,
1755             the C<original_date> key will show you the actual religious date.
1756              
1757             Note: it is also possible that multiple (religious, cultural) holidays fall on
1758             the same national holiday. An example is May 8, 1997 which is commemorated as
1759             Hijra 1418H as well as Ascension Day. When this happens, the C<holidays> key
1760             will contain the details of each religious/cultural holiday.
1761              
1762             Caveat: aside from national holidays, some provinces sometimes declare their own
1763             (e.g. governor election day for East Java province, etc). This is currently not
1764             yet included in this module.
1765              
1766             =head1 DEVELOPER NOTES
1767              
1768             To mark that a holiday has been moved from its original date, use the
1769             C<original_date> option. For example, Mawlid in 2021 has been moved from its
1770             original date 2021-11-19 (this is the day it is actually observed/commemorated)
1771             to 2021-11-20 (this is the day the holiday is in effect where offices and public
1772             places are closed). By adding this option, the summary will reflect this
1773             information:
1774              
1775             date: 2021-12-20
1776             eng_name: Mawlid (commemorated on 2021-12-19)
1777             ind_name: Maulid Nabi Muhammad (diperingati 2021-12-19)
1778              
1779             =head1 FUNCTIONS
1780              
1781              
1782             =head2 count_idn_workdays
1783              
1784             Usage:
1785              
1786             count_idn_workdays(%args) -> [$status_code, $reason, $payload, \%result_meta]
1787              
1788             Count working days (non-holiday business days) for a certain period.
1789              
1790             Working day is defined as day that is not Saturday*/Sunday/holiday/joint leave
1791             days*. If work_saturdays is set to true, Saturdays are also counted as working
1792             days. If observe_joint_leaves is set to false, joint leave days are also counted
1793             as working days.
1794              
1795             Contains data from years 1990 to 2023
1796              
1797             This function is not exported by default, but exportable.
1798              
1799             Arguments ('*' denotes required arguments):
1800              
1801             =over 4
1802              
1803             =item * B<end_date> => I<str>
1804              
1805             End date.
1806              
1807             Defaults to end of current month. Either a string in the form of "YYYY-MM-DD",
1808             or a DateTime object, is accepted.
1809              
1810             =item * B<observe_joint_leaves> => I<bool> (default: 1)
1811              
1812             If set to 0, do not observe joint leave as holidays.
1813              
1814             =item * B<start_date> => I<str>
1815              
1816             Starting date.
1817              
1818             Defaults to start of current month. Either a string in the form of "YYYY-MM-DD",
1819             or a DateTime object, is accepted.
1820              
1821             =item * B<work_saturdays> => I<bool> (default: 0)
1822              
1823             If set to 1, Saturday is a working day.
1824              
1825              
1826             =back
1827              
1828             Returns an enveloped result (an array).
1829              
1830             First element ($status_code) is an integer containing HTTP-like status code
1831             (200 means OK, 4xx caller error, 5xx function error). Second element
1832             ($reason) is a string containing error message, or something like "OK" if status is
1833             200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
1834             element (%result_meta) is called result metadata and is optional, a hash
1835             that contains extra information, much like how HTTP response headers provide additional metadata.
1836              
1837             Return value: (any)
1838              
1839              
1840              
1841             =head2 is_idn_holiday
1842              
1843             Usage:
1844              
1845             is_idn_holiday(%args) -> [$status_code, $reason, $payload, \%result_meta]
1846              
1847             Check whether a date is an Indonesian holiday.
1848              
1849             Will return boolean if a given date is a holiday. A joint leave day will not
1850             count as holiday unless you specify C<include_joint_leave> option.
1851              
1852             Date can be given using separate C<day> (of month), C<month>, and C<year>, or as a
1853             single YYYY-MM-DD date.
1854              
1855             Will return undef (exit code 2 on CLI) if year is not within range of the
1856             holiday data.
1857              
1858             Note that you can also use C<list_idn_holidays> to check whether a C<date> (or a
1859             combination of C<day>, C<month>, and C<year>) is a holiday , but C<is_idn_holiday>
1860             is slightly more efficient, its C<include_joint_leave> option is more convenient,
1861             and it offers a few more options.
1862              
1863             This function is not exported by default, but exportable.
1864              
1865             Arguments ('*' denotes required arguments):
1866              
1867             =over 4
1868              
1869             =item * B<date> => I<str>
1870              
1871             (No description)
1872              
1873             =item * B<day> => I<int>
1874              
1875             (No description)
1876              
1877             =item * B<detail> => I<bool>
1878              
1879             (No description)
1880              
1881             =item * B<include_joint_leave> => I<bool>
1882              
1883             (No description)
1884              
1885             =item * B<month> => I<int>
1886              
1887             (No description)
1888              
1889             =item * B<quiet> => I<bool>
1890              
1891             (No description)
1892              
1893             =item * B<reverse> => I<bool>
1894              
1895             (No description)
1896              
1897             =item * B<year> => I<int>
1898              
1899             (No description)
1900              
1901              
1902             =back
1903              
1904             Returns an enveloped result (an array).
1905              
1906             First element ($status_code) is an integer containing HTTP-like status code
1907             (200 means OK, 4xx caller error, 5xx function error). Second element
1908             ($reason) is a string containing error message, or something like "OK" if status is
1909             200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
1910             element (%result_meta) is called result metadata and is optional, a hash
1911             that contains extra information, much like how HTTP response headers provide additional metadata.
1912              
1913             Return value: (any)
1914              
1915              
1916              
1917             =head2 is_idn_workday
1918              
1919             Usage:
1920              
1921             is_idn_workday(%args) -> [$status_code, $reason, $payload, \%result_meta]
1922              
1923             Check whether a date is a working day (non-holiday business day).
1924              
1925             Working day is defined as day that is not Saturday*/Sunday/holiday/joint leave
1926             days*. If work_saturdays is set to true, Saturdays are also counted as working
1927             days. If observe_joint_leaves is set to false, joint leave days are also counted
1928             as working days.
1929              
1930             Contains data from years 1990 to 2023
1931              
1932             This function is not exported by default, but exportable.
1933              
1934             Arguments ('*' denotes required arguments):
1935              
1936             =over 4
1937              
1938             =item * B<date> => I<str>
1939              
1940             (No description)
1941              
1942             =item * B<day> => I<int>
1943              
1944             (No description)
1945              
1946             =item * B<month> => I<int>
1947              
1948             (No description)
1949              
1950             =item * B<observe_joint_leaves> => I<bool> (default: 1)
1951              
1952             If set to 0, do not observe joint leave as holidays.
1953              
1954             =item * B<work_saturdays> => I<bool> (default: 0)
1955              
1956             If set to 1, Saturday is a working day.
1957              
1958             =item * B<year> => I<int>
1959              
1960             (No description)
1961              
1962              
1963             =back
1964              
1965             Returns an enveloped result (an array).
1966              
1967             First element ($status_code) is an integer containing HTTP-like status code
1968             (200 means OK, 4xx caller error, 5xx function error). Second element
1969             ($reason) is a string containing error message, or something like "OK" if status is
1970             200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
1971             element (%result_meta) is called result metadata and is optional, a hash
1972             that contains extra information, much like how HTTP response headers provide additional metadata.
1973              
1974             Return value: (any)
1975              
1976              
1977              
1978             =head2 list_idn_holidays
1979              
1980             Usage:
1981              
1982             list_idn_holidays(%args) -> [$status_code, $reason, $payload, \%result_meta]
1983              
1984             List Indonesian holidays in calendar.
1985              
1986             List holidays and joint leave days ("cuti bersama").
1987              
1988             Contains data from years 1990 to 2023
1989              
1990             This function is not exported by default, but exportable.
1991              
1992             Arguments ('*' denotes required arguments):
1993              
1994             =over 4
1995              
1996             =item * B<date> => I<date>
1997              
1998             Only return records where the 'date' field equals specified value.
1999              
2000             =item * B<date.in> => I<array[date]>
2001              
2002             Only return records where the 'date' field is in the specified values.
2003              
2004             =item * B<date.is> => I<date>
2005              
2006             Only return records where the 'date' field equals specified value.
2007              
2008             =item * B<date.isnt> => I<date>
2009              
2010             Only return records where the 'date' field does not equal specified value.
2011              
2012             =item * B<date.max> => I<date>
2013              
2014             Only return records where the 'date' field is less than or equal to specified value.
2015              
2016             =item * B<date.min> => I<date>
2017              
2018             Only return records where the 'date' field is greater than or equal to specified value.
2019              
2020             =item * B<date.not_in> => I<array[date]>
2021              
2022             Only return records where the 'date' field is not in the specified values.
2023              
2024             =item * B<date.xmax> => I<date>
2025              
2026             Only return records where the 'date' field is less than specified value.
2027              
2028             =item * B<date.xmin> => I<date>
2029              
2030             Only return records where the 'date' field is greater than specified value.
2031              
2032             =item * B<day> => I<int>
2033              
2034             Only return records where the 'day' field equals specified value.
2035              
2036             =item * B<day.in> => I<array[int]>
2037              
2038             Only return records where the 'day' field is in the specified values.
2039              
2040             =item * B<day.is> => I<int>
2041              
2042             Only return records where the 'day' field equals specified value.
2043              
2044             =item * B<day.isnt> => I<int>
2045              
2046             Only return records where the 'day' field does not equal specified value.
2047              
2048             =item * B<day.max> => I<int>
2049              
2050             Only return records where the 'day' field is less than or equal to specified value.
2051              
2052             =item * B<day.min> => I<int>
2053              
2054             Only return records where the 'day' field is greater than or equal to specified value.
2055              
2056             =item * B<day.not_in> => I<array[int]>
2057              
2058             Only return records where the 'day' field is not in the specified values.
2059              
2060             =item * B<day.xmax> => I<int>
2061              
2062             Only return records where the 'day' field is less than specified value.
2063              
2064             =item * B<day.xmin> => I<int>
2065              
2066             Only return records where the 'day' field is greater than specified value.
2067              
2068             =item * B<decree_date> => I<str>
2069              
2070             Only return records where the 'decree_date' field equals specified value.
2071              
2072             =item * B<decree_date.contains> => I<str>
2073              
2074             Only return records where the 'decree_date' field contains specified text.
2075              
2076             =item * B<decree_date.in> => I<array[str]>
2077              
2078             Only return records where the 'decree_date' field is in the specified values.
2079              
2080             =item * B<decree_date.is> => I<str>
2081              
2082             Only return records where the 'decree_date' field equals specified value.
2083              
2084             =item * B<decree_date.isnt> => I<str>
2085              
2086             Only return records where the 'decree_date' field does not equal specified value.
2087              
2088             =item * B<decree_date.max> => I<str>
2089              
2090             Only return records where the 'decree_date' field is less than or equal to specified value.
2091              
2092             =item * B<decree_date.min> => I<str>
2093              
2094             Only return records where the 'decree_date' field is greater than or equal to specified value.
2095              
2096             =item * B<decree_date.not_contains> => I<str>
2097              
2098             Only return records where the 'decree_date' field does not contain specified text.
2099              
2100             =item * B<decree_date.not_in> => I<array[str]>
2101              
2102             Only return records where the 'decree_date' field is not in the specified values.
2103              
2104             =item * B<decree_date.xmax> => I<str>
2105              
2106             Only return records where the 'decree_date' field is less than specified value.
2107              
2108             =item * B<decree_date.xmin> => I<str>
2109              
2110             Only return records where the 'decree_date' field is greater than specified value.
2111              
2112             =item * B<decree_note> => I<str>
2113              
2114             Only return records where the 'decree_note' field equals specified value.
2115              
2116             =item * B<decree_note.contains> => I<str>
2117              
2118             Only return records where the 'decree_note' field contains specified text.
2119              
2120             =item * B<decree_note.in> => I<array[str]>
2121              
2122             Only return records where the 'decree_note' field is in the specified values.
2123              
2124             =item * B<decree_note.is> => I<str>
2125              
2126             Only return records where the 'decree_note' field equals specified value.
2127              
2128             =item * B<decree_note.isnt> => I<str>
2129              
2130             Only return records where the 'decree_note' field does not equal specified value.
2131              
2132             =item * B<decree_note.max> => I<str>
2133              
2134             Only return records where the 'decree_note' field is less than or equal to specified value.
2135              
2136             =item * B<decree_note.min> => I<str>
2137              
2138             Only return records where the 'decree_note' field is greater than or equal to specified value.
2139              
2140             =item * B<decree_note.not_contains> => I<str>
2141              
2142             Only return records where the 'decree_note' field does not contain specified text.
2143              
2144             =item * B<decree_note.not_in> => I<array[str]>
2145              
2146             Only return records where the 'decree_note' field is not in the specified values.
2147              
2148             =item * B<decree_note.xmax> => I<str>
2149              
2150             Only return records where the 'decree_note' field is less than specified value.
2151              
2152             =item * B<decree_note.xmin> => I<str>
2153              
2154             Only return records where the 'decree_note' field is greater than specified value.
2155              
2156             =item * B<detail> => I<bool> (default: 0)
2157              
2158             Return array of full records instead of just ID fields.
2159              
2160             By default, only the key (ID) field is returned per result entry.
2161              
2162             =item * B<dow> => I<int>
2163              
2164             Only return records where the 'dow' field equals specified value.
2165              
2166             =item * B<dow.in> => I<array[int]>
2167              
2168             Only return records where the 'dow' field is in the specified values.
2169              
2170             =item * B<dow.is> => I<int>
2171              
2172             Only return records where the 'dow' field equals specified value.
2173              
2174             =item * B<dow.isnt> => I<int>
2175              
2176             Only return records where the 'dow' field does not equal specified value.
2177              
2178             =item * B<dow.max> => I<int>
2179              
2180             Only return records where the 'dow' field is less than or equal to specified value.
2181              
2182             =item * B<dow.min> => I<int>
2183              
2184             Only return records where the 'dow' field is greater than or equal to specified value.
2185              
2186             =item * B<dow.not_in> => I<array[int]>
2187              
2188             Only return records where the 'dow' field is not in the specified values.
2189              
2190             =item * B<dow.xmax> => I<int>
2191              
2192             Only return records where the 'dow' field is less than specified value.
2193              
2194             =item * B<dow.xmin> => I<int>
2195              
2196             Only return records where the 'dow' field is greater than specified value.
2197              
2198             =item * B<eng_aliases> => I<array>
2199              
2200             Only return records where the 'eng_aliases' field equals specified value.
2201              
2202             =item * B<eng_aliases.has> => I<array[str]>
2203              
2204             Only return records where the 'eng_aliases' field is an arrayE<sol>list which contains specified value.
2205              
2206             =item * B<eng_aliases.is> => I<array>
2207              
2208             Only return records where the 'eng_aliases' field equals specified value.
2209              
2210             =item * B<eng_aliases.isnt> => I<array>
2211              
2212             Only return records where the 'eng_aliases' field does not equal specified value.
2213              
2214             =item * B<eng_aliases.lacks> => I<array[str]>
2215              
2216             Only return records where the 'eng_aliases' field is an arrayE<sol>list which does not contain specified value.
2217              
2218             =item * B<eng_name> => I<str>
2219              
2220             Only return records where the 'eng_name' field equals specified value.
2221              
2222             =item * B<eng_name.contains> => I<str>
2223              
2224             Only return records where the 'eng_name' field contains specified text.
2225              
2226             =item * B<eng_name.in> => I<array[str]>
2227              
2228             Only return records where the 'eng_name' field is in the specified values.
2229              
2230             =item * B<eng_name.is> => I<str>
2231              
2232             Only return records where the 'eng_name' field equals specified value.
2233              
2234             =item * B<eng_name.isnt> => I<str>
2235              
2236             Only return records where the 'eng_name' field does not equal specified value.
2237              
2238             =item * B<eng_name.max> => I<str>
2239              
2240             Only return records where the 'eng_name' field is less than or equal to specified value.
2241              
2242             =item * B<eng_name.min> => I<str>
2243              
2244             Only return records where the 'eng_name' field is greater than or equal to specified value.
2245              
2246             =item * B<eng_name.not_contains> => I<str>
2247              
2248             Only return records where the 'eng_name' field does not contain specified text.
2249              
2250             =item * B<eng_name.not_in> => I<array[str]>
2251              
2252             Only return records where the 'eng_name' field is not in the specified values.
2253              
2254             =item * B<eng_name.xmax> => I<str>
2255              
2256             Only return records where the 'eng_name' field is less than specified value.
2257              
2258             =item * B<eng_name.xmin> => I<str>
2259              
2260             Only return records where the 'eng_name' field is greater than specified value.
2261              
2262             =item * B<exclude_fields> => I<array[str]>
2263              
2264             Select fields to return.
2265              
2266             =item * B<fields> => I<array[str]>
2267              
2268             Select fields to return.
2269              
2270             =item * B<ind_aliases> => I<array>
2271              
2272             Only return records where the 'ind_aliases' field equals specified value.
2273              
2274             =item * B<ind_aliases.has> => I<array[str]>
2275              
2276             Only return records where the 'ind_aliases' field is an arrayE<sol>list which contains specified value.
2277              
2278             =item * B<ind_aliases.is> => I<array>
2279              
2280             Only return records where the 'ind_aliases' field equals specified value.
2281              
2282             =item * B<ind_aliases.isnt> => I<array>
2283              
2284             Only return records where the 'ind_aliases' field does not equal specified value.
2285              
2286             =item * B<ind_aliases.lacks> => I<array[str]>
2287              
2288             Only return records where the 'ind_aliases' field is an arrayE<sol>list which does not contain specified value.
2289              
2290             =item * B<ind_name> => I<str>
2291              
2292             Only return records where the 'ind_name' field equals specified value.
2293              
2294             =item * B<ind_name.contains> => I<str>
2295              
2296             Only return records where the 'ind_name' field contains specified text.
2297              
2298             =item * B<ind_name.in> => I<array[str]>
2299              
2300             Only return records where the 'ind_name' field is in the specified values.
2301              
2302             =item * B<ind_name.is> => I<str>
2303              
2304             Only return records where the 'ind_name' field equals specified value.
2305              
2306             =item * B<ind_name.isnt> => I<str>
2307              
2308             Only return records where the 'ind_name' field does not equal specified value.
2309              
2310             =item * B<ind_name.max> => I<str>
2311              
2312             Only return records where the 'ind_name' field is less than or equal to specified value.
2313              
2314             =item * B<ind_name.min> => I<str>
2315              
2316             Only return records where the 'ind_name' field is greater than or equal to specified value.
2317              
2318             =item * B<ind_name.not_contains> => I<str>
2319              
2320             Only return records where the 'ind_name' field does not contain specified text.
2321              
2322             =item * B<ind_name.not_in> => I<array[str]>
2323              
2324             Only return records where the 'ind_name' field is not in the specified values.
2325              
2326             =item * B<ind_name.xmax> => I<str>
2327              
2328             Only return records where the 'ind_name' field is less than specified value.
2329              
2330             =item * B<ind_name.xmin> => I<str>
2331              
2332             Only return records where the 'ind_name' field is greater than specified value.
2333              
2334             =item * B<is_holiday> => I<bool>
2335              
2336             Only return records where the 'is_holiday' field equals specified value.
2337              
2338             =item * B<is_holiday.is> => I<bool>
2339              
2340             Only return records where the 'is_holiday' field equals specified value.
2341              
2342             =item * B<is_holiday.isnt> => I<bool>
2343              
2344             Only return records where the 'is_holiday' field does not equal specified value.
2345              
2346             =item * B<is_joint_leave> => I<bool>
2347              
2348             Only return records where the 'is_joint_leave' field equals specified value.
2349              
2350             =item * B<is_joint_leave.is> => I<bool>
2351              
2352             Only return records where the 'is_joint_leave' field equals specified value.
2353              
2354             =item * B<is_joint_leave.isnt> => I<bool>
2355              
2356             Only return records where the 'is_joint_leave' field does not equal specified value.
2357              
2358             =item * B<month> => I<int>
2359              
2360             Only return records where the 'month' field equals specified value.
2361              
2362             =item * B<month.in> => I<array[int]>
2363              
2364             Only return records where the 'month' field is in the specified values.
2365              
2366             =item * B<month.is> => I<int>
2367              
2368             Only return records where the 'month' field equals specified value.
2369              
2370             =item * B<month.isnt> => I<int>
2371              
2372             Only return records where the 'month' field does not equal specified value.
2373              
2374             =item * B<month.max> => I<int>
2375              
2376             Only return records where the 'month' field is less than or equal to specified value.
2377              
2378             =item * B<month.min> => I<int>
2379              
2380             Only return records where the 'month' field is greater than or equal to specified value.
2381              
2382             =item * B<month.not_in> => I<array[int]>
2383              
2384             Only return records where the 'month' field is not in the specified values.
2385              
2386             =item * B<month.xmax> => I<int>
2387              
2388             Only return records where the 'month' field is less than specified value.
2389              
2390             =item * B<month.xmin> => I<int>
2391              
2392             Only return records where the 'month' field is greater than specified value.
2393              
2394             =item * B<note> => I<str>
2395              
2396             Only return records where the 'note' field equals specified value.
2397              
2398             =item * B<note.contains> => I<str>
2399              
2400             Only return records where the 'note' field contains specified text.
2401              
2402             =item * B<note.in> => I<array[str]>
2403              
2404             Only return records where the 'note' field is in the specified values.
2405              
2406             =item * B<note.is> => I<str>
2407              
2408             Only return records where the 'note' field equals specified value.
2409              
2410             =item * B<note.isnt> => I<str>
2411              
2412             Only return records where the 'note' field does not equal specified value.
2413              
2414             =item * B<note.max> => I<str>
2415              
2416             Only return records where the 'note' field is less than or equal to specified value.
2417              
2418             =item * B<note.min> => I<str>
2419              
2420             Only return records where the 'note' field is greater than or equal to specified value.
2421              
2422             =item * B<note.not_contains> => I<str>
2423              
2424             Only return records where the 'note' field does not contain specified text.
2425              
2426             =item * B<note.not_in> => I<array[str]>
2427              
2428             Only return records where the 'note' field is not in the specified values.
2429              
2430             =item * B<note.xmax> => I<str>
2431              
2432             Only return records where the 'note' field is less than specified value.
2433              
2434             =item * B<note.xmin> => I<str>
2435              
2436             Only return records where the 'note' field is greater than specified value.
2437              
2438             =item * B<queries> => I<array[str]>
2439              
2440             Search.
2441              
2442             This will search all searchable fields with one or more specified queries. Each
2443             query can be in the form of C<-FOO> (dash prefix notation) to require that the
2444             fields do not contain specified string, or C</FOO/> to use regular expression.
2445             All queries must match if the C<query_boolean> option is set to C<and>; only one
2446             query should match if the C<query_boolean> option is set to C<or>.
2447              
2448             =item * B<query_boolean> => I<str> (default: "and")
2449              
2450             Whether records must match all search queries ('and') or just one ('or').
2451              
2452             If set to C<and>, all queries must match; if set to C<or>, only one query should
2453             match. See the C<queries> option for more details on searching.
2454              
2455             =item * B<random> => I<bool> (default: 0)
2456              
2457             Return records in random order.
2458              
2459             =item * B<result_limit> => I<int>
2460              
2461             Only return a certain number of records.
2462              
2463             =item * B<result_start> => I<int> (default: 1)
2464              
2465             Only return starting from the n'th record.
2466              
2467             =item * B<sort> => I<array[str]>
2468              
2469             Order records according to certain field(s).
2470              
2471             A list of field names separated by comma. Each field can be prefixed with '-' to
2472             specify descending order instead of the default ascending.
2473              
2474             =item * B<tags> => I<array>
2475              
2476             Only return records where the 'tags' field equals specified value.
2477              
2478             =item * B<tags.has> => I<array[str]>
2479              
2480             Only return records where the 'tags' field is an arrayE<sol>list which contains specified value.
2481              
2482             =item * B<tags.is> => I<array>
2483              
2484             Only return records where the 'tags' field equals specified value.
2485              
2486             =item * B<tags.isnt> => I<array>
2487              
2488             Only return records where the 'tags' field does not equal specified value.
2489              
2490             =item * B<tags.lacks> => I<array[str]>
2491              
2492             Only return records where the 'tags' field is an arrayE<sol>list which does not contain specified value.
2493              
2494             =item * B<with_field_names> => I<bool>
2495              
2496             Return field names in each record (as hashE<sol>associative array).
2497              
2498             When enabled, function will return each record as hash/associative array
2499             (field name => value pairs). Otherwise, function will return each record
2500             as list/array (field value, field value, ...).
2501              
2502             =item * B<year> => I<int>
2503              
2504             Only return records where the 'year' field equals specified value.
2505              
2506             =item * B<year.in> => I<array[int]>
2507              
2508             Only return records where the 'year' field is in the specified values.
2509              
2510             =item * B<year.is> => I<int>
2511              
2512             Only return records where the 'year' field equals specified value.
2513              
2514             =item * B<year.isnt> => I<int>
2515              
2516             Only return records where the 'year' field does not equal specified value.
2517              
2518             =item * B<year.max> => I<int>
2519              
2520             Only return records where the 'year' field is less than or equal to specified value.
2521              
2522             =item * B<year.min> => I<int>
2523              
2524             Only return records where the 'year' field is greater than or equal to specified value.
2525              
2526             =item * B<year.not_in> => I<array[int]>
2527              
2528             Only return records where the 'year' field is not in the specified values.
2529              
2530             =item * B<year.xmax> => I<int>
2531              
2532             Only return records where the 'year' field is less than specified value.
2533              
2534             =item * B<year.xmin> => I<int>
2535              
2536             Only return records where the 'year' field is greater than specified value.
2537              
2538              
2539             =back
2540              
2541             Returns an enveloped result (an array).
2542              
2543             First element ($status_code) is an integer containing HTTP-like status code
2544             (200 means OK, 4xx caller error, 5xx function error). Second element
2545             ($reason) is a string containing error message, or something like "OK" if status is
2546             200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
2547             element (%result_meta) is called result metadata and is optional, a hash
2548             that contains extra information, much like how HTTP response headers provide additional metadata.
2549              
2550             Return value: (any)
2551              
2552              
2553              
2554             =head2 list_idn_workdays
2555              
2556             Usage:
2557              
2558             list_idn_workdays(%args) -> [$status_code, $reason, $payload, \%result_meta]
2559              
2560             List working days (non-holiday business days) for a certain period.
2561              
2562             Working day is defined as day that is not Saturday*/Sunday/holiday/joint leave
2563             days*. If work_saturdays is set to true, Saturdays are also counted as working
2564             days. If observe_joint_leaves is set to false, joint leave days are also counted
2565             as working days.
2566              
2567             Contains data from years 1990 to 2023
2568              
2569             This function is not exported by default, but exportable.
2570              
2571             Arguments ('*' denotes required arguments):
2572              
2573             =over 4
2574              
2575             =item * B<end_date> => I<str>
2576              
2577             End date.
2578              
2579             Defaults to end of current month. Either a string in the form of "YYYY-MM-DD",
2580             or a DateTime object, is accepted.
2581              
2582             =item * B<observe_joint_leaves> => I<bool> (default: 1)
2583              
2584             If set to 0, do not observe joint leave as holidays.
2585              
2586             =item * B<start_date> => I<str>
2587              
2588             Starting date.
2589              
2590             Defaults to start of current month. Either a string in the form of "YYYY-MM-DD",
2591             or a DateTime object, is accepted.
2592              
2593             =item * B<work_saturdays> => I<bool> (default: 0)
2594              
2595             If set to 1, Saturday is a working day.
2596              
2597              
2598             =back
2599              
2600             Returns an enveloped result (an array).
2601              
2602             First element ($status_code) is an integer containing HTTP-like status code
2603             (200 means OK, 4xx caller error, 5xx function error). Second element
2604             ($reason) is a string containing error message, or something like "OK" if status is
2605             200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
2606             element (%result_meta) is called result metadata and is optional, a hash
2607             that contains extra information, much like how HTTP response headers provide additional metadata.
2608              
2609             Return value: (any)
2610              
2611             =for Pod::Coverage ^(.+_id_.+)$
2612              
2613             =head1 FAQ
2614              
2615             =head2 What is "joint leave"?
2616              
2617             Workers are normally granted around 12 days of paid leave per year (excluding
2618             special leaves like maternity, etc). They are free to spend them on whichever
2619             days they want. The joint leave ("cuti bersama") is a government program to
2620             recommend that some of these leave days be spent together nationally on certain
2621             assigned days, especially adjacent to holidays like Eid Ul-Fitr ("Lebaran"). It
2622             is not mandated (companies can opt to follow it or not, depending on their
2623             specific situation), but many do follow it anyway, e.g. government civil
2624             workers, banks, etc. I am marking joint leave days with is_joint_leave=1 and
2625             is_holiday=0, while the holidays themselves with is_holiday=1, so you can
2626             differentiate/select both/either one.
2627              
2628             =head2 When was joint leave established?
2629              
2630             Joint leave was first decreed in 2001 [1] for the 2002 & 2003 calendar years.
2631             The 2001 calendar year does not yet have joint leave days [2]. See also [3].
2632             Websites that list joint leave days for 2001 or earlier years (example: [4],
2633             [5]) are incorrect; by 2001 or earlier, these joint leave days had not been
2634             officially decreed by the government.
2635              
2636             [1] https://jdih.kemnaker.go.id/data_wirata/2002-4-4.pdf
2637              
2638             [2] https://peraturan.bkpm.go.id/jdih/userfiles/batang/Kepmenag_162_2000.pdf
2639              
2640             [3] http://www.wikiapbn.org/cuti-bersama/
2641              
2642             [4] https://kalenderindonesia.com/libur/masehi/2001
2643              
2644             [5] https://kalenderindonesia.com/libur/masehi/1991
2645              
2646             =head2 What happens when multiple religious/holidays coincide on a single calendar day?
2647              
2648             For example, in 1997, both Hijra and Ascension Day fall on May 8th. When this
2649             happens, C<ind_name> and C<eng_name> will contain all the names of the holidays
2650             separated by comma, respectively:
2651              
2652             Tahun Baru Hijriah, Kenaikan Isa Al-Masih
2653             Hijra, Ascension Day
2654              
2655             All the properties that have the same value will be set in the merged holiday
2656             data:
2657              
2658             is_holiday => 1,
2659             is_joint_leave => 1,
2660              
2661             The C<multiple> property will also be set to true:
2662              
2663             multiple => 1,
2664              
2665             All the tags will be merged:
2666              
2667             tags => ['religious', 'religion=christianity', 'calendar=lunar']
2668              
2669             You can get each holiday's data in the C<holidays> key.
2670              
2671             =head2 Data for older holidays?
2672              
2673             Will be provided if there is demand and data source.
2674              
2675             =head2 Holidays after (current year)+1?
2676              
2677             Some religious holidays, especially Vesakha, are not determined yet. Joint leave
2678             days are also usually decreed by the government in as late as October/November
2679             in the preceding year.
2680              
2681             =head2 How to calculate the difference of two dates in number of working days?
2682              
2683             Use L</count_idn_workdays>.
2684              
2685             =head1 HOMEPAGE
2686              
2687             Please visit the project's homepage at L<https://metacpan.org/release/Calendar-Indonesia-Holiday>.
2688              
2689             =head1 SOURCE
2690              
2691             Source repository is at L<https://github.com/perlancar/perl-Calendar-Indonesia-Holiday>.
2692              
2693             =head1 AUTHOR
2694              
2695             perlancar <perlancar@cpan.org>
2696              
2697             =head1 CONTRIBUTOR
2698              
2699             =for stopwords Steven Haryanto
2700              
2701             Steven Haryanto <stevenharyanto@gmail.com>
2702              
2703             =head1 CONTRIBUTING
2704              
2705              
2706             To contribute, you can send patches by email/via RT, or send pull requests on
2707             GitHub.
2708              
2709             Most of the time, you don't need to build the distribution yourself. You can
2710             simply modify the code, then test via:
2711              
2712             % prove -l
2713              
2714             If you want to build the distribution (e.g. to try to install it locally on your
2715             system), you can install L<Dist::Zilla>,
2716             L<Dist::Zilla::PluginBundle::Author::PERLANCAR>,
2717             L<Pod::Weaver::PluginBundle::Author::PERLANCAR>, and sometimes one or two other
2718             Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps required beyond
2719             that are considered a bug and can be reported to me.
2720              
2721             =head1 COPYRIGHT AND LICENSE
2722              
2723             This software is copyright (c) 2023, 2022, 2021, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 2013, 2012, 2011 by perlancar <perlancar@cpan.org>.
2724              
2725             This is free software; you can redistribute it and/or modify it under
2726             the same terms as the Perl 5 programming language system itself.
2727              
2728             =head1 BUGS
2729              
2730             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Calendar-Indonesia-Holiday>
2731              
2732             When submitting a bug or request, please include a test-file or a
2733             patch to an existing test-file that illustrates the bug or desired
2734             feature.
2735              
2736             =cut