File Coverage

blib/lib/Calendar/Indonesia/Holiday.pm
Criterion Covered Total %
statement 233 281 82.9
branch 52 128 40.6
condition 17 54 31.4
subroutine 28 33 84.8
pod 2 2 100.0
total 332 498 66.6


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