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   105554 use 5.010001;
  1         13  
4 1     1   5 use strict;
  1         2  
  1         34  
5 1     1   8 use warnings;
  1         2  
  1         49  
6 1     1   538 use experimental 'smartmatch';
  1         3631  
  1         5  
7             #use Log::ger;
8              
9 1     1   1062 use DateTime;
  1         568971  
  1         54  
10 1     1   530 use Function::Fallback::CoreOrPP qw(clone);
  1         779  
  1         88  
11 1     1   760 use Perinci::Sub::Gen::AccessTable qw(gen_read_table_func);
  1         41533  
  1         82  
12 1     1   10 use Perinci::Sub::Util qw(err gen_modified_sub);
  1         2  
  1         10666  
13              
14             require Exporter;
15              
16             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
17             our $DATE = '2023-06-20'; # DATE
18             our $DIST = 'Calendar-Indonesia-Holiday'; # DIST
19             our $VERSION = '0.350'; # 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   590 my ($r, $opts) = @_;
98 369 100       834 if ($opts->{original_date}) {
99 14         35 $r->{ind_name} .= " (diperingati $opts->{original_date})";
100 14         39 $r->{eng_name} .= " (commemorated on $opts->{original_date})";
101             }
102             }
103              
104             sub _h_chnewyear {
105 22     22   43 my ($r, $opts) = @_;
106             $r->{ind_name} = "Tahun Baru Imlek".
107 22 50       83 ($opts->{hyear} ? " $opts->{hyear}":"");
108             $r->{eng_name} = "Chinese New Year".
109 22 50       64 ($opts->{hyear} ? " $opts->{hyear}":"");
110 22         54 _add_original_date($r, $opts);
111 22         42 $r->{ind_aliases} = [];
112 22         51 $r->{eng_aliases} = [];
113 22         35 $r->{is_holiday} = 1;
114 22         56 $r->{year_start} = 2003; # decreed in 2002 by megawati soekarnoputri
115 22         46 $r->{tags} = [qw/international calendar=lunar/];
116 22         77 ($r);
117             }
118              
119             sub _h_mawlid {
120 34     34   68 my ($r, $opts) = @_;
121 34         64 $r->{ind_name} = "Maulid Nabi Muhammad";
122 34         71 $r->{eng_name} = "Mawlid";
123 34         84 _add_original_date($r, $opts);
124 34         111 $r->{ind_aliases} = [qw/Maulud/];
125 34         80 $r->{eng_aliases} = ["Mawlid An-Nabi"];
126 34         64 $r->{is_holiday} = 1;
127 34         116 $r->{tags} = [qw/religious religion=islam calendar=lunar/];
128 34         129 ($r);
129             }
130              
131             sub _h_nyepi {
132 34     34   66 my ($r, $opts) = @_;
133             $r->{ind_name} = "Nyepi".
134 34 50       119 ($opts->{hyear} ? " $opts->{hyear}":"");
135             $r->{eng_name} = "Nyepi".
136 34 50       100 ($opts->{hyear} ? " $opts->{hyear}":"");
137 34         80 _add_original_date($r, $opts);
138 34         75 $r->{ind_aliases} = ["Tahun Baru Saka"];
139 34         103 $r->{eng_aliases} = ["Bali New Year", "Bali Day Of Silence"];
140 34         58 $r->{is_holiday} = 1;
141 34         85 $r->{tags} = [qw/religious religion=hinduism calendar=saka/];
142 34         138 ($r);
143             }
144              
145             sub _h_goodfri {
146 34     34   81 my ($r, $opts) = @_;
147 34         61 $r->{ind_name} = "Jum'at Agung";
148 34         60 $r->{eng_name} = "Good Friday";
149 34         85 _add_original_date($r, $opts);
150 34         95 $r->{ind_aliases} = ["Wafat Isa Al-Masih"];
151 34         67 $r->{eng_aliases} = [];
152 34         50 $r->{is_holiday} = 1;
153 34         98 $r->{tags} = [qw/religious religion=christianity/];
154 34         105 ($r);
155             }
156              
157             sub _h_vesakha {
158 34     34   94 my ($r, $opts) = @_;
159             $r->{ind_name} = "Waisyak".
160 34 50       129 ($opts->{hyear} ? " $opts->{hyear}":"");
161             $r->{eng_name} = "Vesakha".
162 34 50       93 ($opts->{hyear} ? " $opts->{hyear}":"");
163 34         76 _add_original_date($r, $opts);
164 34         53 $r->{ind_aliases} = [];
165 34         108 $r->{eng_aliases} = ["Vesak"];
166 34         68 $r->{is_holiday} = 1;
167 34         155 $r->{tags} = [qw/religious religion=buddhism/];
168 34         129 ($r);
169             }
170              
171             sub _h_ascension {
172 34     34   69 my ($r, $opts) = @_;
173 34         74 $r->{ind_name} = "Kenaikan Isa Al-Masih";
174 34         62 $r->{eng_name} = "Ascension Day";
175 34         87 _add_original_date($r, $opts);
176 34         55 $r->{ind_aliases} = [];
177 34         75 $r->{eng_aliases} = [];
178 34         61 $r->{is_holiday} = 1;
179 34         119 $r->{tags} = [qw/religious religion=christianity/];
180 34         109 ($r);
181             }
182              
183             sub _h_isramiraj {
184 35     35   72 my ($r, $opts) = @_;
185 35         77 $r->{ind_name} = "Isra Miraj";
186 35         63 $r->{eng_name} = "Isra And Miraj";
187 35         89 _add_original_date($r, $opts);
188 35         91 $r->{ind_aliases} = [];
189 35         87 $r->{eng_aliases} = [];
190 35         58 $r->{is_holiday} = 1;
191 35         125 $r->{tags} = [qw/religious religion=islam calendar=lunar/];
192 35         128 ($r);
193             }
194              
195             sub _h_eidulf {
196 71     71   146 my ($r, $opts) = @_;
197 71   50     140 $opts //= {};
198             my $ind_name0 = "Idul Fitri".
199 71 50       220 ($opts->{hyear} ? " $opts->{hyear}H":"");
200             my $eng_name0 = "Eid Ul-Fitr".
201 71 50       161 ($opts->{hyear} ? " $opts->{hyear}H":"");
202 71 50       257 $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         172 _add_original_date($r, $opts);
205 71         177 $r->{ind_aliases} = ["Lebaran"];
206 71         149 $r->{eng_aliases} = [];
207 71         127 $r->{is_holiday} = 1;
208 71         213 $r->{tags} = [qw/religious religion=islam calendar=lunar/];
209 71         269 ($r);
210             }
211              
212             sub _h_eidula {
213 35     35   70 my ($r, $opts) = @_;
214 35         62 $r->{ind_name} = "Idul Adha";
215 35         63 $r->{eng_name} = "Eid Al-Adha";
216 35         98 _add_original_date($r, $opts);
217 35         82 $r->{ind_aliases} = ["Idul Kurban"];
218 35         61 $r->{eng_aliases} = [];
219 35         53 $r->{is_holiday} = 1;
220 35         154 $r->{tags} = [qw/religious religion=islam calendar=lunar/];
221 35         96 ($r);
222             }
223              
224             sub _h_hijra {
225 35     35   73 my ($r, $opts) = @_;
226 35   50     76 $opts //= {};
227             $r->{ind_name} = "Tahun Baru Hijriyah".
228 35 50       127 ($opts->{hyear} ? " $opts->{hyear}H":"");
229             $r->{eng_name} = "Hijra".
230 35 50       96 ($opts->{hyear} ? " $opts->{hyear}H":"");
231 35         86 _add_original_date($r, $opts);
232 35         80 $r->{ind_aliases} = ["1 Muharam"];
233 35         71 $r->{eng_aliases} = [];
234 35         55 $r->{is_holiday} = 1;
235 35         96 $r->{tags} = [qw/calendar=lunar/];
236 35         132 ($r);
237             }
238              
239             sub _h_lelection {
240 7     7   14 my ($r, $opts) = @_;
241 7         13 $r->{ind_name} = "Pemilu Legislatif (Pileg)";
242 7         12 $r->{eng_name} = "Legislative Election";
243 7         13 $r->{is_holiday} = 1;
244 7         14 $r->{tags} = [qw/political/];
245              
246 7         15 for (qw(decree_date decree_note)) {
247 14 100       32 $r->{$_} = $opts->{$_} if defined $opts->{$_};
248             }
249 7         20 ($r);
250             }
251              
252             sub _h_pelection {
253 2     2   6 my ($r, $opts) = @_;
254 2         4 $r->{ind_name} = "Pemilu Presiden (Pilpres)";
255 2         4 $r->{eng_name} = "Presidential Election";
256 2         3 $r->{is_holiday} = 1;
257 2         4 $r->{tags} = [qw/political/];
258              
259 2         4 for (qw(decree_date decree_note)) {
260 4 50       11 $r->{$_} = $opts->{$_} if defined $opts->{$_};
261             }
262 2         6 ($r);
263             }
264              
265             sub _h_jrelection {
266 3     3   8 my ($r, $opts) = @_;
267 3         6 $r->{ind_name} = "Pilkada Serentak";
268 3         5 $r->{eng_name} = "Joint Regional Election";
269 3         6 $r->{is_holiday} = 1;
270 3         20 $r->{tags} = [qw/political/];
271              
272 3         9 for (qw(decree_date decree_note)) {
273 6 100       18 $r->{$_} = $opts->{$_} if defined $opts->{$_};
274             }
275 3         11 ($r);
276             }
277              
278             sub _jointlv {
279 97     97   196 my ($r, $opts) = @_;
280 97   50     183 $opts //= {};
281 97         154 my $h = $opts->{holiday};
282             $r->{ind_name} = "Cuti Bersama".
283 97 100 33     437 ($h ? " (".($h->{ind_name0} // $h->{ind_name}).")": "");
284             $r->{eng_name} = "Joint Leave".
285 97 100 33     385 ($h ? " (".($h->{eng_name0} // $h->{eng_name}).")": "");
286 97         185 $r->{ind_aliases} = [];
287 97         183 $r->{eng_aliases} = [];
288 97         137 $r->{is_joint_leave} = 1;
289 97         242 $r->{tags} = [];
290 97         349 ($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 477 50   477   1621 $_[0] =~ m!(\d+)[-/](\d+)! or die "Bug: bad dm syntax $_[0]";
311 477         3025 return (day => $1+0, month => $2+0);
312             }
313              
314             sub _uniquify_holidays {
315 34     34   93 my @holidays = @_;
316              
317 34         65 my %seen; # key=mm-dd (e.g. 12-25), val=[hol1, hol2, ...]
318 34         67 for my $h (@holidays) {
319 596         1452 my $k = sprintf "%02d-%02d", $h->{month}, $h->{day};
320 596   100     2814 $seen{$k} //= [];
321 596         889 push @{ $seen{$k} }, $h;
  596         1305  
322             }
323              
324 34         183 for my $k (keys %seen) {
325 594 100       779 if (@{ $seen{$k} } == 1) {
  594         985  
326 592         996 $seen{$k} = $seen{$k}[0];
327             } else {
328             my $h_mult = {
329             multiple => 1,
330 4         16 ind_name => join(", ", map {$_->{ind_name}} @{ $seen{$k} }),
  2         7  
331 4         17 eng_name => join(", ", map {$_->{eng_name}} @{ $seen{$k} }),
  2         6  
332 2         5 holidays => $seen{$k},
333             };
334             # join all the tags
335 2         5 my @tags;
336 2         6 for my $h (@{ $seen{$k} }) {
  2         6  
337 4 50       11 next unless $h->{tags};
338 4         7 for my $t (@{ $h->{tags} }) {
  4         9  
339 8 100       18 push @tags, $t unless grep { $_ eq $t } @tags;
  11         28  
340             }
341             }
342 2         5 $h_mult->{tags} = \@tags;
343             # join all the properties
344             PROP:
345 2         4 for my $prop (keys %{ $seen{$k}[0] }) {
  2         21  
346 24 100       58 next if exists $h_mult->{$prop};
347 18         29 my %vals;
348 18         25 for my $h (@{ $seen{$k} }) {
  18         33  
349 36 50       68 next PROP unless defined $h->{$prop};
350 36         87 $vals{ $h->{$prop} }++;
351             }
352 18 100       41 next if keys(%vals) > 1;
353 14         37 $h_mult->{$prop} = $seen{$k}[0]{$prop};
354             }
355 2         10 $seen{$k} = $h_mult;
356             }
357             }
358              
359 34         277 map { $seen{$_} } sort keys %seen;
  594         1235  
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             # superseded jun 16, 2023 (SKB No 624/2023, 2/2023, 2/2023)
1229             # ref:
1230             # -
1231             {
1232             # 2023 holidays
1233             my ($chnewyear2023, $nyepi2023, $eidulf2023, $eidula2023, $vesakha2023, $christmas);
1234             $year_holidays{2023} = [
1235             # - new year
1236             ($chnewyear2023 = _h_chnewyear ({_expand_dm("22-01")}, {hyear=>2574})),
1237             _h_isramiraj ({_expand_dm("18-02")}, {hyear=>1444}),
1238             ($nyepi2023 = _h_nyepi ({_expand_dm("22-03")}, {hyear=>1945})),
1239             _h_goodfri ({_expand_dm("07-04")}),
1240             ($eidulf2023 = _h_eidulf ({_expand_dm("22-04")}, {hyear=>1444, day=>1})),
1241             _h_eidulf ({_expand_dm("23-04")}, {hyear=>1444, day=>2}),
1242             # - labor day
1243             _h_ascension ({_expand_dm("18-05")}),
1244             # - pancasila day
1245             _h_vesakha ({_expand_dm("04-06")}, {hyear=>2567}),
1246             ($eidula2023 = _h_eidula ({_expand_dm("29-06")}, {hyear=>1444})),
1247             _h_hijra ({_expand_dm("19-07")}, {hyear=>1445}),
1248             # - independence day
1249             _h_mawlid({_expand_dm("28-09")}, {hyear=>1445}),
1250             # - christmas
1251             ];
1252              
1253             push @{ $year_holidays{2023} }, (
1254             _jointlv ({_expand_dm("23-01")}, {holiday=>$chnewyear2023}),
1255             _jointlv ({_expand_dm("23-03")}, {holiday=>$nyepi2023}),
1256             _jointlv ({_expand_dm("19-04")}, {holiday=>$eidulf2023}),
1257             _jointlv ({_expand_dm("20-04")}, {holiday=>$eidulf2023}),
1258             _jointlv ({_expand_dm("21-04")}, {holiday=>$eidulf2023}),
1259             _jointlv ({_expand_dm("24-04")}, {holiday=>$eidulf2023}),
1260             _jointlv ({_expand_dm("25-04")}, {holiday=>$eidulf2023}),
1261             _jointlv ({_expand_dm("02-06")}, {holiday=>$vesakha2023}),
1262             _jointlv ({_expand_dm("28-06")}, {holiday=>$eidula2023}),
1263             _jointlv ({_expand_dm("30-06")}, {holiday=>$eidula2023}),
1264             _jointlv ({_expand_dm("26-12")}, {holiday=>$christmas}),
1265             );
1266             }
1267              
1268             {
1269             # 2024 holidays
1270             1;
1271             }
1272              
1273             {
1274             # 2025 holidays
1275             #_h_jrelection({_expand_dm("09-12")}, {decree_date => "2025-11-xx"}), # keppres xx/2025, surat edaran menaker xxx.../2020 (2020-12-xx)
1276             1;
1277             }
1278              
1279             {
1280             # 2026 holidays
1281             1;
1282             }
1283              
1284              
1285             my @years = sort keys %year_holidays;
1286             our $min_year = $years[0];
1287             our $max_year = $years[-1];
1288             our $max_joint_leave_year;
1289             for my $y (reverse @years) {
1290             if (grep {$_->{is_joint_leave}} @{$year_holidays{$y}}) {
1291             $max_joint_leave_year = $y;
1292             last;
1293             }
1294             }
1295              
1296             my @holidays;
1297             for my $year ($min_year .. $max_year) {
1298             my @hf;
1299             for my $h0 (@fixed_holidays) {
1300             next if $h0->{year_start} && $year < $h0->{year_start};
1301             next if $h0->{year_en} && $year > $h0->{year_end};
1302             my $h = clone $h0;
1303             push @{$h->{tags}}, "fixed-date";
1304             $h->{is_holiday} = 1;
1305             $h->{is_joint_leave} = 0;
1306             push @hf, $h;
1307             }
1308              
1309             my @hy;
1310             for my $h0 (@{$year_holidays{$year}}) {
1311             my $h = clone $h0;
1312             $h->{is_holiday} //= 0;
1313             $h->{is_joint_leave} //= 0;
1314             delete $h->{ind_name0};
1315             delete $h->{eng_name0};
1316             push @hy, $h;
1317             }
1318              
1319             for my $h (@hf, @hy) {
1320             $h->{year} = $year;
1321             my $dt = DateTime->new(year=>$year, month=>$h->{month}, day=>$h->{day});
1322             $h->{date} = $dt->ymd;
1323             $h->{dow} = $dt->day_of_week;
1324             }
1325              
1326             push @holidays, _uniquify_holidays(@hf, @hy);
1327             }
1328              
1329             my $res = gen_read_table_func(
1330             name => 'list_idn_holidays',
1331             table_data => \@holidays,
1332             table_spec => {
1333             fields => {
1334             date => {
1335             schema => 'date*',
1336             pos => 0,
1337             },
1338             day => {
1339             schema => 'int*',
1340             pos => 1,
1341             },
1342             month => {
1343             schema => 'int*',
1344             pos => 2,
1345             },
1346             year => {
1347             schema => 'int*',
1348             pos => 3,
1349             },
1350             dow => {
1351             schema => 'int*',
1352             summary => 'Day of week (1-7, Monday is 1)',
1353             pos => 4,
1354             },
1355             eng_name => {
1356             schema => 'str*',
1357             summary => 'English name',
1358             pos => 5,
1359             },
1360             ind_name => {
1361             schema => 'str*',
1362             summary => 'Indonesian name',
1363             pos => 6,
1364             },
1365             eng_aliases => {
1366             schema => ['array*'=>{of=>'str*'}],
1367             summary => 'English other names, if any',
1368             pos => 7,
1369             },
1370             ind_aliases => {
1371             schema => ['array*'=>{of=>'str*'}],
1372             summary => 'Indonesian other names, if any',
1373             pos => 8,
1374             },
1375             is_holiday => {
1376             schema => 'bool*',
1377             pos => 9,
1378             },
1379             is_joint_leave => {
1380             schema => 'bool*',
1381             summary => 'Whether this date is a joint leave day '.
1382             '("cuti bersama")',
1383             pos => 10,
1384             },
1385             decree_date => {
1386             schema => 'str',
1387             pos => 11,
1388             },
1389             decree_note => {
1390             schema => 'str',
1391             pos => 12,
1392             },
1393             note => {
1394             schema => 'str',
1395             pos => 13,
1396             },
1397             tags => {
1398             schema => 'array*',
1399             pos => 14,
1400             },
1401             },
1402             pk => 'date',
1403             },
1404             langs => ['en_US', 'id_ID'],
1405             );
1406              
1407             die "BUG: Can't generate func: $res->[0] - $res->[1]"
1408             unless $res->[0] == 200;
1409              
1410             delete $SPEC{list_idn_holidays}{args}{queries}{pos};
1411             delete $SPEC{list_idn_holidays}{args}{queries}{slurpy};
1412             $SPEC{list_idn_holidays}{args}{year}{pos} = 0;
1413             $SPEC{list_idn_holidays}{args}{month}{pos} = 1;
1414              
1415             my $TEXT_AVAILABLE_YEARS = "Contains data from years $min_year to $max_year";
1416              
1417             my $TEXT_WORKDAY_DEFINITION = <<'_';
1418             Working day is defined as day that is not Saturday*/Sunday/holiday/joint leave
1419             days*. If work_saturdays is set to true, Saturdays are also counted as working
1420             days. If observe_joint_leaves is set to false, joint leave days are also counted
1421             as working days.
1422             _
1423              
1424             my $meta = $res->[2]{meta};
1425             $meta->{summary} = "List Indonesian holidays in calendar";
1426             $meta->{description} = <<"_";
1427              
1428             List holidays and joint leave days ("cuti bersama").
1429              
1430             $TEXT_AVAILABLE_YEARS
1431              
1432             _
1433              
1434             sub _check_date_arg {
1435 6     6   22 my ($date) = @_;
1436 6 50 33     58 if (ref($date) && $date->isa('DateTime')) {
    50          
1437 0         0 return $date;
1438             } elsif ($date =~ /\A(\d{4})-(\d{2})-(\d{2})\z/) {
1439 6         25 return DateTime->new(year=>$1, month=>$2, day=>$3);
1440             } else {
1441 0         0 return;
1442             }
1443             }
1444              
1445             $SPEC{list_idn_workdays} = {
1446             v => 1.1,
1447             summary => 'List working days (non-holiday business days) for a certain period',
1448             description => <<"_",
1449              
1450             $TEXT_WORKDAY_DEFINITION
1451              
1452             $TEXT_AVAILABLE_YEARS
1453              
1454             _
1455             args => {
1456             start_date => {
1457             summary => 'Starting date',
1458             schema => 'str*',
1459             pos => 0,
1460             description => <<'_',
1461              
1462             Defaults to start of current month. Either a string in the form of "YYYY-MM-DD",
1463             or a DateTime object, is accepted.
1464              
1465             _
1466             },
1467             end_date => {
1468             summary => 'End date',
1469             schema => 'str*',
1470             pos => 1,
1471             description => <<'_',
1472              
1473             Defaults to end of current month. Either a string in the form of "YYYY-MM-DD",
1474             or a DateTime object, is accepted.
1475              
1476             _
1477             },
1478             work_saturdays => {
1479             schema => ['bool' => {default=>0}],
1480             summary => 'If set to 1, Saturday is a working day',
1481             },
1482             observe_joint_leaves => {
1483             summary => 'If set to 0, do not observe joint leave as holidays',
1484             schema => ['bool' => {default => 1}],
1485             cmdline_aliases => {j=>{}},
1486             },
1487             },
1488             };
1489             sub list_idn_workdays {
1490 3     3 1 17 my %args = @_;
1491              
1492             # XXX args
1493 3         26 my $now = DateTime->now;
1494 3         1305 my $som = DateTime->new(year => $now->year, month => $now->month, day => 1);
1495 3         1043 my $eom = $som->clone->add(months=>1)->subtract(days=>1);
1496 3 50 33     6986 my $start_date = _check_date_arg($args{start_date} // $som) or
1497             return [400, "Invalid start_date, must be string 'YYYY-MM-DD' ".
1498             "or DateTime object"];
1499 3 50 33     958 my $end_date = _check_date_arg($args{end_date} // $eom) or
1500             return [400, "Invalid end_date, must be string 'YYYY-MM-DD' ".
1501             "or DateTime object"];
1502 3         923 for ($start_date, $end_date) {
1503 6 50       38 return [400, "Sorry, no data for year earlier than $min_year available"]
1504             if $_->year < $min_year;
1505 6 50       43 return [400, "Sorry, no data for year newer than $max_year available"]
1506             if $_->year > $max_year;
1507             }
1508 3   100     35 my $work_saturdays = $args{work_saturdays} // 0;
1509 3   100     17 my $observe_joint_leaves = $args{observe_joint_leaves} // 1;
1510              
1511 3         8 my @args;
1512 3         10 push @args, "year.min"=>$start_date->year;
1513 3         21 push @args, "year.max"=>$end_date->year;
1514 3 100       29 push @args, (is_holiday=>1) if !$observe_joint_leaves;
1515 3         19 my $res = list_idn_holidays(@args);
1516 3 50       70443 return err(500, "Can't list holidays", $res)
1517             unless $res->[0] == 200;
1518             #use Data::Dump; dd $res;
1519              
1520 3         10 my @wd;
1521 3         19 my $dt = $start_date->clone->subtract(days=>1);
1522 3         3999 while (1) {
1523 99         408 $dt->add(days=>1);
1524 99 100       96136 next if $dt->day_of_week == 7;
1525 84 100 100     462 next if $dt->day_of_week == 6 && !$work_saturdays;
1526 74 100       413 last if DateTime->compare($dt, $end_date) > 0;
1527 71         5208 my $ymd = $dt->ymd;
1528 71 100       990 next if $ymd ~~ @{$res->[2]};
  71         409  
1529 69         202 push @wd, $ymd;
1530             }
1531              
1532 3         297 [200, "OK", \@wd];
1533             }
1534              
1535             gen_modified_sub(
1536             output_name => 'count_idn_workdays',
1537             summary => "Count working days (non-holiday business days) for a certain period",
1538              
1539             base_name => 'list_idn_workdays',
1540             output_code => sub {
1541 3     3   61227 my $res = list_idn_workdays(@_);
1542 3 50       14 return $res unless $res->[0] == 200;
1543 3         8 $res->[2] = @{$res->[2]};
  3         10  
1544 3         24 $res;
1545             },
1546             );
1547              
1548             $SPEC{is_idn_holiday} = {
1549             v => 1.1,
1550             summary => 'Check whether a date is an Indonesian holiday',
1551             description => <<'_',
1552              
1553             Will return boolean if a given date is a holiday. A joint leave day will not
1554             count as holiday unless you specify `include_joint_leave` option.
1555              
1556             Date can be given using separate `day` (of month), `month`, and `year`, or as a
1557             single YYYY-MM-DD date.
1558              
1559             Will return undef (exit code 2 on CLI) if year is not within range of the
1560             holiday data.
1561              
1562             Note that you can also use `list_idn_holidays` to check whether a `date` (or a
1563             combination of `day`, `month`, and `year`) is a holiday , but `is_idn_holiday`
1564             is slightly more efficient, its `include_joint_leave` option is more convenient,
1565             and it offers a few more options.
1566              
1567             _
1568             args => {
1569             %argspecs_date_or_day_month_year,
1570              
1571             include_joint_leave => {schema=>'bool*', cmdline_aliases=>{j=>{}}},
1572             reverse => {schema=>'bool*', cmdline_aliases=>{r=>{}}},
1573             quiet => {schema=>'bool*', cmdline_aliases=>{q=>{}}},
1574             detail => {schema=>'bool*', cmdline_aliases=>{l=>{}}},
1575             },
1576             args_rels => {
1577             %argsrels_date_or_day_month_year,
1578             },
1579             };
1580             sub is_idn_holiday {
1581 0     0 1   my %args = @_;
1582              
1583 0           my $res = _get_date_day_month_year(\%args);
1584 0 0         return $res unless $res->[0] == 200;
1585 0           my ($date, $y, $m, $d) = @{ $res->[2] };
  0            
1586              
1587 0           for my $e (@fixed_holidays) {
1588 0 0 0       next if defined $e->{year_start} && $y < $e->{year_start};
1589 0 0 0       next if defined $e->{year_end} && $y > $e->{year_end};
1590 0 0 0       next unless $e->{day} == $d && $e->{month} == $m;
1591             return [200, "OK", ($args{reverse} ? 0 : ($args{detail} ? $e : 1)), {
1592             'cmdline.exit_code' => ($args{reverse} ? 1 : 0),
1593             ('cmdline.result' => ($args{quiet} ? '' : "Date $date IS a holiday")) x !$args{detail},
1594 0 0         }];
    0          
    0          
    0          
1595             }
1596              
1597 0 0 0       unless ($y >= $min_year && $y <= $max_year) {
1598             return [200, "OK", undef, {
1599             'cmdline.exit_code' => 2,
1600 0 0         'cmdline.result' => ($args{quiet} ? '' : "Date year ($y) is not within range of holiday data ($min_year-$max_year)"),
1601             }];
1602             }
1603              
1604 0           for my $e (@{ $year_holidays{$y} }) {
  0            
1605 0 0 0       next unless $e->{day} == $d && $e->{month} == $m;
1606 0 0 0       next if $e->{is_joint_leave} && !$args{include_joint_leave};
1607             return [200, "OK", ($args{reverse} ? 0 : ($args{detail} ? $e : 1)), {
1608             'cmdline.exit_code' => ($args{reverse} ? 1 : 0),
1609             ('cmdline.result' => ($args{quiet} ? '' : "Date $date IS a holiday")) x !$args{detail},
1610 0 0         }];
    0          
    0          
    0          
1611             }
1612              
1613             return [200, "OK", ($args{reverse} ? 1:0), {
1614             'cmdline.exit_code' => ($args{reverse} ? 0 : 1),
1615 0 0         'cmdline.result' => ($args{quiet} ? '' : "Date $date is NOT a holiday"),
    0          
    0          
1616             }];
1617             }
1618              
1619             gen_modified_sub(
1620             output_name => 'is_idn_workday',
1621             summary => "Check whether a date is a working day (non-holiday business day)",
1622             base_name => 'count_idn_workdays',
1623             modify_meta => sub {
1624             my $meta = shift;
1625             delete $meta->{args}{start_date};
1626             delete $meta->{args}{end_date};
1627             $meta->{args}{$_} = $argspecs_date_or_day_month_year{$_}
1628             for keys %argspecs_date_or_day_month_year;
1629             $meta->{args_rels} = {
1630             %argsrels_date_or_day_month_year,
1631             };
1632             },
1633             output_code => sub {
1634 0     0     my %args = @_;
1635              
1636 0           my $res = _get_date_day_month_year(\%args);
1637 0 0         return $res unless $res->[0] == 200;
1638 0           my ($date, $y, $m, $d) = @{ $res->[2] };
  0            
1639              
1640 0           delete $args{date};
1641 0           delete $args{day};
1642 0           delete $args{month};
1643 0           delete $args{year};
1644              
1645 0           $res = count_idn_workdays(%args, start_date=>$date, end_date=>$date);
1646 0 0         return $res unless $res->[0] == 200;
1647 0           $res;
1648             },
1649             );
1650              
1651             1;
1652             # ABSTRACT: List Indonesian public holidays
1653              
1654             __END__
1655              
1656             =pod
1657              
1658             =encoding UTF-8
1659              
1660             =head1 NAME
1661              
1662             Calendar::Indonesia::Holiday - List Indonesian public holidays
1663              
1664             =head1 VERSION
1665              
1666             This document describes version 0.350 of Calendar::Indonesia::Holiday (from Perl distribution Calendar-Indonesia-Holiday), released on 2023-06-20.
1667              
1668             =head1 SYNOPSIS
1669              
1670             use Calendar::Indonesia::Holiday qw(
1671             list_idn_holidays
1672             list_idn_workdays
1673              
1674             count_idn_workdays
1675              
1676             is_idn_holiday
1677             is_idn_workday
1678             );
1679              
1680             This lists Indonesian holidays for the year 2011, without the joint leave days
1681             ("cuti bersama"), showing only the dates:
1682              
1683             my $res = list_idn_holidays(year => 2011, is_joint_leave=>0);
1684              
1685             Sample result:
1686              
1687             [200, "OK", [
1688             '2011-01-01',
1689             '2011-02-03',
1690             '2011-02-16',
1691             '2011-03-05',
1692             '2011-04-22',
1693             '2011-05-17',
1694             '2011-06-02',
1695             '2011-06-29',
1696             '2011-08-17',
1697             '2011-08-31',
1698             '2011-09-01',
1699             '2011-11-07',
1700             '2011-11-27',
1701             '2011-12-25',
1702             ]];
1703              
1704             This lists religious Indonesian holidays, showing full details:
1705              
1706             my $res = list_idn_holidays(year => 2011,
1707             "tags.has" => ['religious'], detail=>1);
1708              
1709             Sample result:
1710              
1711             [200, "OK", [
1712             {date => '2011-02-16',
1713             day => 16,
1714             month => 2,
1715             year => 2011,
1716             ind_name => 'Maulid Nabi Muhammad',
1717             eng_name => 'Mawlid',
1718             eng_aliases => ['Mawlid An-Nabi'],
1719             ind_aliases => ['Maulud'],
1720             is_holiday => 1,
1721             tags => [qw/religious religion=islam calendar=lunar/],
1722             },
1723             ...
1724             ]];
1725              
1726             This checks whether 2011-02-16 is a holiday:
1727              
1728             my $res = is_idn_holiday(date => '2011-02-16');
1729             print "2011-02-16 is a holiday\n" if $res->[2];
1730              
1731             This checks whether 2021-03-11 is a working day:
1732              
1733             my $res = is_idn_workday(date => '2021-03-11');
1734             print "2011-02-16 is a holiday\n" if $res->[2];
1735              
1736             This lists working days for a certain period:
1737              
1738             my $res = list_idn_workdays(start_date=>'2021-01-01', end_date=>'2021-06-30');
1739              
1740             Idem, but returns a number instead. If unspecified, C<start_date> defaults to
1741             start of current month and C<end_date> defaults to end of current month. So this
1742             returns the number of working days in the current month:
1743              
1744             my $res = count_idn_workdays();
1745              
1746             =head1 DESCRIPTION
1747              
1748             This module provides functions to list Indonesian holidays. There is a
1749             command-line script interface for this module: L<list-idn-holidays> and a few
1750             others distributed in L<App::IndonesianHolidayUtils> distribution.
1751              
1752             Calendar years supported: 1990-2023.
1753              
1754             Note: Note that sometimes the holiday (as set by law) falls at a different date
1755             than the actual religious commemoration date. When you use the C<detail> option,
1756             the C<original_date> key will show you the actual religious date.
1757              
1758             Note: it is also possible that multiple (religious, cultural) holidays fall on
1759             the same national holiday. An example is May 8, 1997 which is commemorated as
1760             Hijra 1418H as well as Ascension Day. When this happens, the C<holidays> key
1761             will contain the details of each religious/cultural holiday.
1762              
1763             Caveat: aside from national holidays, some provinces sometimes declare their own
1764             (e.g. governor election day for East Java province, etc). This is currently not
1765             yet included in this module.
1766              
1767             =head1 DEVELOPER NOTES
1768              
1769             To mark that a holiday has been moved from its original date, use the
1770             C<original_date> option. For example, Mawlid in 2021 has been moved from its
1771             original date 2021-11-19 (this is the day it is actually observed/commemorated)
1772             to 2021-11-20 (this is the day the holiday is in effect where offices and public
1773             places are closed). By adding this option, the summary will reflect this
1774             information:
1775              
1776             date: 2021-12-20
1777             eng_name: Mawlid (commemorated on 2021-12-19)
1778             ind_name: Maulid Nabi Muhammad (diperingati 2021-12-19)
1779              
1780             =head1 FUNCTIONS
1781              
1782              
1783             =head2 count_idn_workdays
1784              
1785             Usage:
1786              
1787             count_idn_workdays(%args) -> [$status_code, $reason, $payload, \%result_meta]
1788              
1789             Count working days (non-holiday business days) for a certain period.
1790              
1791             Working day is defined as day that is not Saturday*/Sunday/holiday/joint leave
1792             days*. If work_saturdays is set to true, Saturdays are also counted as working
1793             days. If observe_joint_leaves is set to false, joint leave days are also counted
1794             as working days.
1795              
1796             Contains data from years 1990 to 2023
1797              
1798             This function is not exported by default, but exportable.
1799              
1800             Arguments ('*' denotes required arguments):
1801              
1802             =over 4
1803              
1804             =item * B<end_date> => I<str>
1805              
1806             End date.
1807              
1808             Defaults to end of current month. Either a string in the form of "YYYY-MM-DD",
1809             or a DateTime object, is accepted.
1810              
1811             =item * B<observe_joint_leaves> => I<bool> (default: 1)
1812              
1813             If set to 0, do not observe joint leave as holidays.
1814              
1815             =item * B<start_date> => I<str>
1816              
1817             Starting date.
1818              
1819             Defaults to start of current month. Either a string in the form of "YYYY-MM-DD",
1820             or a DateTime object, is accepted.
1821              
1822             =item * B<work_saturdays> => I<bool> (default: 0)
1823              
1824             If set to 1, Saturday is a working day.
1825              
1826              
1827             =back
1828              
1829             Returns an enveloped result (an array).
1830              
1831             First element ($status_code) is an integer containing HTTP-like status code
1832             (200 means OK, 4xx caller error, 5xx function error). Second element
1833             ($reason) is a string containing error message, or something like "OK" if status is
1834             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
1835             element (%result_meta) is called result metadata and is optional, a hash
1836             that contains extra information, much like how HTTP response headers provide additional metadata.
1837              
1838             Return value: (any)
1839              
1840              
1841              
1842             =head2 is_idn_holiday
1843              
1844             Usage:
1845              
1846             is_idn_holiday(%args) -> [$status_code, $reason, $payload, \%result_meta]
1847              
1848             Check whether a date is an Indonesian holiday.
1849              
1850             Will return boolean if a given date is a holiday. A joint leave day will not
1851             count as holiday unless you specify C<include_joint_leave> option.
1852              
1853             Date can be given using separate C<day> (of month), C<month>, and C<year>, or as a
1854             single YYYY-MM-DD date.
1855              
1856             Will return undef (exit code 2 on CLI) if year is not within range of the
1857             holiday data.
1858              
1859             Note that you can also use C<list_idn_holidays> to check whether a C<date> (or a
1860             combination of C<day>, C<month>, and C<year>) is a holiday , but C<is_idn_holiday>
1861             is slightly more efficient, its C<include_joint_leave> option is more convenient,
1862             and it offers a few more options.
1863              
1864             This function is not exported by default, but exportable.
1865              
1866             Arguments ('*' denotes required arguments):
1867              
1868             =over 4
1869              
1870             =item * B<date> => I<str>
1871              
1872             (No description)
1873              
1874             =item * B<day> => I<int>
1875              
1876             (No description)
1877              
1878             =item * B<detail> => I<bool>
1879              
1880             (No description)
1881              
1882             =item * B<include_joint_leave> => I<bool>
1883              
1884             (No description)
1885              
1886             =item * B<month> => I<int>
1887              
1888             (No description)
1889              
1890             =item * B<quiet> => I<bool>
1891              
1892             (No description)
1893              
1894             =item * B<reverse> => I<bool>
1895              
1896             (No description)
1897              
1898             =item * B<year> => I<int>
1899              
1900             (No description)
1901              
1902              
1903             =back
1904              
1905             Returns an enveloped result (an array).
1906              
1907             First element ($status_code) is an integer containing HTTP-like status code
1908             (200 means OK, 4xx caller error, 5xx function error). Second element
1909             ($reason) is a string containing error message, or something like "OK" if status is
1910             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
1911             element (%result_meta) is called result metadata and is optional, a hash
1912             that contains extra information, much like how HTTP response headers provide additional metadata.
1913              
1914             Return value: (any)
1915              
1916              
1917              
1918             =head2 is_idn_workday
1919              
1920             Usage:
1921              
1922             is_idn_workday(%args) -> [$status_code, $reason, $payload, \%result_meta]
1923              
1924             Check whether a date is a working day (non-holiday business day).
1925              
1926             Working day is defined as day that is not Saturday*/Sunday/holiday/joint leave
1927             days*. If work_saturdays is set to true, Saturdays are also counted as working
1928             days. If observe_joint_leaves is set to false, joint leave days are also counted
1929             as working days.
1930              
1931             Contains data from years 1990 to 2023
1932              
1933             This function is not exported by default, but exportable.
1934              
1935             Arguments ('*' denotes required arguments):
1936              
1937             =over 4
1938              
1939             =item * B<date> => I<str>
1940              
1941             (No description)
1942              
1943             =item * B<day> => I<int>
1944              
1945             (No description)
1946              
1947             =item * B<month> => I<int>
1948              
1949             (No description)
1950              
1951             =item * B<observe_joint_leaves> => I<bool> (default: 1)
1952              
1953             If set to 0, do not observe joint leave as holidays.
1954              
1955             =item * B<work_saturdays> => I<bool> (default: 0)
1956              
1957             If set to 1, Saturday is a working day.
1958              
1959             =item * B<year> => I<int>
1960              
1961             (No description)
1962              
1963              
1964             =back
1965              
1966             Returns an enveloped result (an array).
1967              
1968             First element ($status_code) is an integer containing HTTP-like status code
1969             (200 means OK, 4xx caller error, 5xx function error). Second element
1970             ($reason) is a string containing error message, or something like "OK" if status is
1971             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
1972             element (%result_meta) is called result metadata and is optional, a hash
1973             that contains extra information, much like how HTTP response headers provide additional metadata.
1974              
1975             Return value: (any)
1976              
1977              
1978              
1979             =head2 list_idn_holidays
1980              
1981             Usage:
1982              
1983             list_idn_holidays(%args) -> [$status_code, $reason, $payload, \%result_meta]
1984              
1985             List Indonesian holidays in calendar.
1986              
1987             List holidays and joint leave days ("cuti bersama").
1988              
1989             Contains data from years 1990 to 2023
1990              
1991             This function is not exported by default, but exportable.
1992              
1993             Arguments ('*' denotes required arguments):
1994              
1995             =over 4
1996              
1997             =item * B<date> => I<date>
1998              
1999             Only return records where the 'date' field equals specified value.
2000              
2001             =item * B<date.in> => I<array[date]>
2002              
2003             Only return records where the 'date' field is in the specified values.
2004              
2005             =item * B<date.is> => I<date>
2006              
2007             Only return records where the 'date' field equals specified value.
2008              
2009             =item * B<date.isnt> => I<date>
2010              
2011             Only return records where the 'date' field does not equal specified value.
2012              
2013             =item * B<date.max> => I<date>
2014              
2015             Only return records where the 'date' field is less than or equal to specified value.
2016              
2017             =item * B<date.min> => I<date>
2018              
2019             Only return records where the 'date' field is greater than or equal to specified value.
2020              
2021             =item * B<date.not_in> => I<array[date]>
2022              
2023             Only return records where the 'date' field is not in the specified values.
2024              
2025             =item * B<date.xmax> => I<date>
2026              
2027             Only return records where the 'date' field is less than specified value.
2028              
2029             =item * B<date.xmin> => I<date>
2030              
2031             Only return records where the 'date' field is greater than specified value.
2032              
2033             =item * B<day> => I<int>
2034              
2035             Only return records where the 'day' field equals specified value.
2036              
2037             =item * B<day.in> => I<array[int]>
2038              
2039             Only return records where the 'day' field is in the specified values.
2040              
2041             =item * B<day.is> => I<int>
2042              
2043             Only return records where the 'day' field equals specified value.
2044              
2045             =item * B<day.isnt> => I<int>
2046              
2047             Only return records where the 'day' field does not equal specified value.
2048              
2049             =item * B<day.max> => I<int>
2050              
2051             Only return records where the 'day' field is less than or equal to specified value.
2052              
2053             =item * B<day.min> => I<int>
2054              
2055             Only return records where the 'day' field is greater than or equal to specified value.
2056              
2057             =item * B<day.not_in> => I<array[int]>
2058              
2059             Only return records where the 'day' field is not in the specified values.
2060              
2061             =item * B<day.xmax> => I<int>
2062              
2063             Only return records where the 'day' field is less than specified value.
2064              
2065             =item * B<day.xmin> => I<int>
2066              
2067             Only return records where the 'day' field is greater than specified value.
2068              
2069             =item * B<decree_date> => I<str>
2070              
2071             Only return records where the 'decree_date' field equals specified value.
2072              
2073             =item * B<decree_date.contains> => I<str>
2074              
2075             Only return records where the 'decree_date' field contains specified text.
2076              
2077             =item * B<decree_date.in> => I<array[str]>
2078              
2079             Only return records where the 'decree_date' field is in the specified values.
2080              
2081             =item * B<decree_date.is> => I<str>
2082              
2083             Only return records where the 'decree_date' field equals specified value.
2084              
2085             =item * B<decree_date.isnt> => I<str>
2086              
2087             Only return records where the 'decree_date' field does not equal specified value.
2088              
2089             =item * B<decree_date.max> => I<str>
2090              
2091             Only return records where the 'decree_date' field is less than or equal to specified value.
2092              
2093             =item * B<decree_date.min> => I<str>
2094              
2095             Only return records where the 'decree_date' field is greater than or equal to specified value.
2096              
2097             =item * B<decree_date.not_contains> => I<str>
2098              
2099             Only return records where the 'decree_date' field does not contain specified text.
2100              
2101             =item * B<decree_date.not_in> => I<array[str]>
2102              
2103             Only return records where the 'decree_date' field is not in the specified values.
2104              
2105             =item * B<decree_date.xmax> => I<str>
2106              
2107             Only return records where the 'decree_date' field is less than specified value.
2108              
2109             =item * B<decree_date.xmin> => I<str>
2110              
2111             Only return records where the 'decree_date' field is greater than specified value.
2112              
2113             =item * B<decree_note> => I<str>
2114              
2115             Only return records where the 'decree_note' field equals specified value.
2116              
2117             =item * B<decree_note.contains> => I<str>
2118              
2119             Only return records where the 'decree_note' field contains specified text.
2120              
2121             =item * B<decree_note.in> => I<array[str]>
2122              
2123             Only return records where the 'decree_note' field is in the specified values.
2124              
2125             =item * B<decree_note.is> => I<str>
2126              
2127             Only return records where the 'decree_note' field equals specified value.
2128              
2129             =item * B<decree_note.isnt> => I<str>
2130              
2131             Only return records where the 'decree_note' field does not equal specified value.
2132              
2133             =item * B<decree_note.max> => I<str>
2134              
2135             Only return records where the 'decree_note' field is less than or equal to specified value.
2136              
2137             =item * B<decree_note.min> => I<str>
2138              
2139             Only return records where the 'decree_note' field is greater than or equal to specified value.
2140              
2141             =item * B<decree_note.not_contains> => I<str>
2142              
2143             Only return records where the 'decree_note' field does not contain specified text.
2144              
2145             =item * B<decree_note.not_in> => I<array[str]>
2146              
2147             Only return records where the 'decree_note' field is not in the specified values.
2148              
2149             =item * B<decree_note.xmax> => I<str>
2150              
2151             Only return records where the 'decree_note' field is less than specified value.
2152              
2153             =item * B<decree_note.xmin> => I<str>
2154              
2155             Only return records where the 'decree_note' field is greater than specified value.
2156              
2157             =item * B<detail> => I<bool> (default: 0)
2158              
2159             Return array of full records instead of just ID fields.
2160              
2161             By default, only the key (ID) field is returned per result entry.
2162              
2163             =item * B<dow> => I<int>
2164              
2165             Only return records where the 'dow' field equals specified value.
2166              
2167             =item * B<dow.in> => I<array[int]>
2168              
2169             Only return records where the 'dow' field is in the specified values.
2170              
2171             =item * B<dow.is> => I<int>
2172              
2173             Only return records where the 'dow' field equals specified value.
2174              
2175             =item * B<dow.isnt> => I<int>
2176              
2177             Only return records where the 'dow' field does not equal specified value.
2178              
2179             =item * B<dow.max> => I<int>
2180              
2181             Only return records where the 'dow' field is less than or equal to specified value.
2182              
2183             =item * B<dow.min> => I<int>
2184              
2185             Only return records where the 'dow' field is greater than or equal to specified value.
2186              
2187             =item * B<dow.not_in> => I<array[int]>
2188              
2189             Only return records where the 'dow' field is not in the specified values.
2190              
2191             =item * B<dow.xmax> => I<int>
2192              
2193             Only return records where the 'dow' field is less than specified value.
2194              
2195             =item * B<dow.xmin> => I<int>
2196              
2197             Only return records where the 'dow' field is greater than specified value.
2198              
2199             =item * B<eng_aliases> => I<array>
2200              
2201             Only return records where the 'eng_aliases' field equals specified value.
2202              
2203             =item * B<eng_aliases.has> => I<array[str]>
2204              
2205             Only return records where the 'eng_aliases' field is an arrayE<sol>list which contains specified value.
2206              
2207             =item * B<eng_aliases.is> => I<array>
2208              
2209             Only return records where the 'eng_aliases' field equals specified value.
2210              
2211             =item * B<eng_aliases.isnt> => I<array>
2212              
2213             Only return records where the 'eng_aliases' field does not equal specified value.
2214              
2215             =item * B<eng_aliases.lacks> => I<array[str]>
2216              
2217             Only return records where the 'eng_aliases' field is an arrayE<sol>list which does not contain specified value.
2218              
2219             =item * B<eng_name> => I<str>
2220              
2221             Only return records where the 'eng_name' field equals specified value.
2222              
2223             =item * B<eng_name.contains> => I<str>
2224              
2225             Only return records where the 'eng_name' field contains specified text.
2226              
2227             =item * B<eng_name.in> => I<array[str]>
2228              
2229             Only return records where the 'eng_name' field is in the specified values.
2230              
2231             =item * B<eng_name.is> => I<str>
2232              
2233             Only return records where the 'eng_name' field equals specified value.
2234              
2235             =item * B<eng_name.isnt> => I<str>
2236              
2237             Only return records where the 'eng_name' field does not equal specified value.
2238              
2239             =item * B<eng_name.max> => I<str>
2240              
2241             Only return records where the 'eng_name' field is less than or equal to specified value.
2242              
2243             =item * B<eng_name.min> => I<str>
2244              
2245             Only return records where the 'eng_name' field is greater than or equal to specified value.
2246              
2247             =item * B<eng_name.not_contains> => I<str>
2248              
2249             Only return records where the 'eng_name' field does not contain specified text.
2250              
2251             =item * B<eng_name.not_in> => I<array[str]>
2252              
2253             Only return records where the 'eng_name' field is not in the specified values.
2254              
2255             =item * B<eng_name.xmax> => I<str>
2256              
2257             Only return records where the 'eng_name' field is less than specified value.
2258              
2259             =item * B<eng_name.xmin> => I<str>
2260              
2261             Only return records where the 'eng_name' field is greater than specified value.
2262              
2263             =item * B<exclude_fields> => I<array[str]>
2264              
2265             Select fields to return.
2266              
2267             =item * B<fields> => I<array[str]>
2268              
2269             Select fields to return.
2270              
2271             =item * B<ind_aliases> => I<array>
2272              
2273             Only return records where the 'ind_aliases' field equals specified value.
2274              
2275             =item * B<ind_aliases.has> => I<array[str]>
2276              
2277             Only return records where the 'ind_aliases' field is an arrayE<sol>list which contains specified value.
2278              
2279             =item * B<ind_aliases.is> => I<array>
2280              
2281             Only return records where the 'ind_aliases' field equals specified value.
2282              
2283             =item * B<ind_aliases.isnt> => I<array>
2284              
2285             Only return records where the 'ind_aliases' field does not equal specified value.
2286              
2287             =item * B<ind_aliases.lacks> => I<array[str]>
2288              
2289             Only return records where the 'ind_aliases' field is an arrayE<sol>list which does not contain specified value.
2290              
2291             =item * B<ind_name> => I<str>
2292              
2293             Only return records where the 'ind_name' field equals specified value.
2294              
2295             =item * B<ind_name.contains> => I<str>
2296              
2297             Only return records where the 'ind_name' field contains specified text.
2298              
2299             =item * B<ind_name.in> => I<array[str]>
2300              
2301             Only return records where the 'ind_name' field is in the specified values.
2302              
2303             =item * B<ind_name.is> => I<str>
2304              
2305             Only return records where the 'ind_name' field equals specified value.
2306              
2307             =item * B<ind_name.isnt> => I<str>
2308              
2309             Only return records where the 'ind_name' field does not equal specified value.
2310              
2311             =item * B<ind_name.max> => I<str>
2312              
2313             Only return records where the 'ind_name' field is less than or equal to specified value.
2314              
2315             =item * B<ind_name.min> => I<str>
2316              
2317             Only return records where the 'ind_name' field is greater than or equal to specified value.
2318              
2319             =item * B<ind_name.not_contains> => I<str>
2320              
2321             Only return records where the 'ind_name' field does not contain specified text.
2322              
2323             =item * B<ind_name.not_in> => I<array[str]>
2324              
2325             Only return records where the 'ind_name' field is not in the specified values.
2326              
2327             =item * B<ind_name.xmax> => I<str>
2328              
2329             Only return records where the 'ind_name' field is less than specified value.
2330              
2331             =item * B<ind_name.xmin> => I<str>
2332              
2333             Only return records where the 'ind_name' field is greater than specified value.
2334              
2335             =item * B<is_holiday> => I<bool>
2336              
2337             Only return records where the 'is_holiday' field equals specified value.
2338              
2339             =item * B<is_holiday.is> => I<bool>
2340              
2341             Only return records where the 'is_holiday' field equals specified value.
2342              
2343             =item * B<is_holiday.isnt> => I<bool>
2344              
2345             Only return records where the 'is_holiday' field does not equal specified value.
2346              
2347             =item * B<is_joint_leave> => I<bool>
2348              
2349             Only return records where the 'is_joint_leave' field equals specified value.
2350              
2351             =item * B<is_joint_leave.is> => I<bool>
2352              
2353             Only return records where the 'is_joint_leave' field equals specified value.
2354              
2355             =item * B<is_joint_leave.isnt> => I<bool>
2356              
2357             Only return records where the 'is_joint_leave' field does not equal specified value.
2358              
2359             =item * B<month> => I<int>
2360              
2361             Only return records where the 'month' field equals specified value.
2362              
2363             =item * B<month.in> => I<array[int]>
2364              
2365             Only return records where the 'month' field is in the specified values.
2366              
2367             =item * B<month.is> => I<int>
2368              
2369             Only return records where the 'month' field equals specified value.
2370              
2371             =item * B<month.isnt> => I<int>
2372              
2373             Only return records where the 'month' field does not equal specified value.
2374              
2375             =item * B<month.max> => I<int>
2376              
2377             Only return records where the 'month' field is less than or equal to specified value.
2378              
2379             =item * B<month.min> => I<int>
2380              
2381             Only return records where the 'month' field is greater than or equal to specified value.
2382              
2383             =item * B<month.not_in> => I<array[int]>
2384              
2385             Only return records where the 'month' field is not in the specified values.
2386              
2387             =item * B<month.xmax> => I<int>
2388              
2389             Only return records where the 'month' field is less than specified value.
2390              
2391             =item * B<month.xmin> => I<int>
2392              
2393             Only return records where the 'month' field is greater than specified value.
2394              
2395             =item * B<note> => I<str>
2396              
2397             Only return records where the 'note' field equals specified value.
2398              
2399             =item * B<note.contains> => I<str>
2400              
2401             Only return records where the 'note' field contains specified text.
2402              
2403             =item * B<note.in> => I<array[str]>
2404              
2405             Only return records where the 'note' field is in the specified values.
2406              
2407             =item * B<note.is> => I<str>
2408              
2409             Only return records where the 'note' field equals specified value.
2410              
2411             =item * B<note.isnt> => I<str>
2412              
2413             Only return records where the 'note' field does not equal specified value.
2414              
2415             =item * B<note.max> => I<str>
2416              
2417             Only return records where the 'note' field is less than or equal to specified value.
2418              
2419             =item * B<note.min> => I<str>
2420              
2421             Only return records where the 'note' field is greater than or equal to specified value.
2422              
2423             =item * B<note.not_contains> => I<str>
2424              
2425             Only return records where the 'note' field does not contain specified text.
2426              
2427             =item * B<note.not_in> => I<array[str]>
2428              
2429             Only return records where the 'note' field is not in the specified values.
2430              
2431             =item * B<note.xmax> => I<str>
2432              
2433             Only return records where the 'note' field is less than specified value.
2434              
2435             =item * B<note.xmin> => I<str>
2436              
2437             Only return records where the 'note' field is greater than specified value.
2438              
2439             =item * B<queries> => I<array[str]>
2440              
2441             Search.
2442              
2443             This will search all searchable fields with one or more specified queries. Each
2444             query can be in the form of C<-FOO> (dash prefix notation) to require that the
2445             fields do not contain specified string, or C</FOO/> to use regular expression.
2446             All queries must match if the C<query_boolean> option is set to C<and>; only one
2447             query should match if the C<query_boolean> option is set to C<or>.
2448              
2449             =item * B<query_boolean> => I<str> (default: "and")
2450              
2451             Whether records must match all search queries ('and') or just one ('or').
2452              
2453             If set to C<and>, all queries must match; if set to C<or>, only one query should
2454             match. See the C<queries> option for more details on searching.
2455              
2456             =item * B<random> => I<bool> (default: 0)
2457              
2458             Return records in random order.
2459              
2460             =item * B<result_limit> => I<int>
2461              
2462             Only return a certain number of records.
2463              
2464             =item * B<result_start> => I<int> (default: 1)
2465              
2466             Only return starting from the n'th record.
2467              
2468             =item * B<sort> => I<array[str]>
2469              
2470             Order records according to certain field(s).
2471              
2472             A list of field names separated by comma. Each field can be prefixed with '-' to
2473             specify descending order instead of the default ascending.
2474              
2475             =item * B<tags> => I<array>
2476              
2477             Only return records where the 'tags' field equals specified value.
2478              
2479             =item * B<tags.has> => I<array[str]>
2480              
2481             Only return records where the 'tags' field is an arrayE<sol>list which contains specified value.
2482              
2483             =item * B<tags.is> => I<array>
2484              
2485             Only return records where the 'tags' field equals specified value.
2486              
2487             =item * B<tags.isnt> => I<array>
2488              
2489             Only return records where the 'tags' field does not equal specified value.
2490              
2491             =item * B<tags.lacks> => I<array[str]>
2492              
2493             Only return records where the 'tags' field is an arrayE<sol>list which does not contain specified value.
2494              
2495             =item * B<with_field_names> => I<bool>
2496              
2497             Return field names in each record (as hashE<sol>associative array).
2498              
2499             When enabled, function will return each record as hash/associative array
2500             (field name => value pairs). Otherwise, function will return each record
2501             as list/array (field value, field value, ...).
2502              
2503             =item * B<year> => I<int>
2504              
2505             Only return records where the 'year' field equals specified value.
2506              
2507             =item * B<year.in> => I<array[int]>
2508              
2509             Only return records where the 'year' field is in the specified values.
2510              
2511             =item * B<year.is> => I<int>
2512              
2513             Only return records where the 'year' field equals specified value.
2514              
2515             =item * B<year.isnt> => I<int>
2516              
2517             Only return records where the 'year' field does not equal specified value.
2518              
2519             =item * B<year.max> => I<int>
2520              
2521             Only return records where the 'year' field is less than or equal to specified value.
2522              
2523             =item * B<year.min> => I<int>
2524              
2525             Only return records where the 'year' field is greater than or equal to specified value.
2526              
2527             =item * B<year.not_in> => I<array[int]>
2528              
2529             Only return records where the 'year' field is not in the specified values.
2530              
2531             =item * B<year.xmax> => I<int>
2532              
2533             Only return records where the 'year' field is less than specified value.
2534              
2535             =item * B<year.xmin> => I<int>
2536              
2537             Only return records where the 'year' field is greater than specified value.
2538              
2539              
2540             =back
2541              
2542             Returns an enveloped result (an array).
2543              
2544             First element ($status_code) is an integer containing HTTP-like status code
2545             (200 means OK, 4xx caller error, 5xx function error). Second element
2546             ($reason) is a string containing error message, or something like "OK" if status is
2547             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
2548             element (%result_meta) is called result metadata and is optional, a hash
2549             that contains extra information, much like how HTTP response headers provide additional metadata.
2550              
2551             Return value: (any)
2552              
2553              
2554              
2555             =head2 list_idn_workdays
2556              
2557             Usage:
2558              
2559             list_idn_workdays(%args) -> [$status_code, $reason, $payload, \%result_meta]
2560              
2561             List working days (non-holiday business days) for a certain period.
2562              
2563             Working day is defined as day that is not Saturday*/Sunday/holiday/joint leave
2564             days*. If work_saturdays is set to true, Saturdays are also counted as working
2565             days. If observe_joint_leaves is set to false, joint leave days are also counted
2566             as working days.
2567              
2568             Contains data from years 1990 to 2023
2569              
2570             This function is not exported by default, but exportable.
2571              
2572             Arguments ('*' denotes required arguments):
2573              
2574             =over 4
2575              
2576             =item * B<end_date> => I<str>
2577              
2578             End date.
2579              
2580             Defaults to end of current month. Either a string in the form of "YYYY-MM-DD",
2581             or a DateTime object, is accepted.
2582              
2583             =item * B<observe_joint_leaves> => I<bool> (default: 1)
2584              
2585             If set to 0, do not observe joint leave as holidays.
2586              
2587             =item * B<start_date> => I<str>
2588              
2589             Starting date.
2590              
2591             Defaults to start of current month. Either a string in the form of "YYYY-MM-DD",
2592             or a DateTime object, is accepted.
2593              
2594             =item * B<work_saturdays> => I<bool> (default: 0)
2595              
2596             If set to 1, Saturday is a working day.
2597              
2598              
2599             =back
2600              
2601             Returns an enveloped result (an array).
2602              
2603             First element ($status_code) is an integer containing HTTP-like status code
2604             (200 means OK, 4xx caller error, 5xx function error). Second element
2605             ($reason) is a string containing error message, or something like "OK" if status is
2606             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
2607             element (%result_meta) is called result metadata and is optional, a hash
2608             that contains extra information, much like how HTTP response headers provide additional metadata.
2609              
2610             Return value: (any)
2611              
2612             =for Pod::Coverage ^(.+_id_.+)$
2613              
2614             =head1 FAQ
2615              
2616             =head2 What is "joint leave"?
2617              
2618             Workers are normally granted around 12 days of paid leave per year (excluding
2619             special leaves like maternity, etc). They are free to spend them on whichever
2620             days they want. The joint leave ("cuti bersama") is a government program to
2621             recommend that some of these leave days be spent together nationally on certain
2622             assigned days, especially adjacent to holidays like Eid Ul-Fitr ("Lebaran"). It
2623             is not mandated (companies can opt to follow it or not, depending on their
2624             specific situation), but many do follow it anyway, e.g. government civil
2625             workers, banks, etc. I am marking joint leave days with is_joint_leave=1 and
2626             is_holiday=0, while the holidays themselves with is_holiday=1, so you can
2627             differentiate/select both/either one.
2628              
2629             =head2 When was joint leave established?
2630              
2631             Joint leave was first decreed in 2001 [1] for the 2002 & 2003 calendar years.
2632             The 2001 calendar year does not yet have joint leave days [2]. See also [3].
2633             Websites that list joint leave days for 2001 or earlier years (example: [4],
2634             [5]) are incorrect; by 2001 or earlier, these joint leave days had not been
2635             officially decreed by the government.
2636              
2637             [1] https://jdih.kemnaker.go.id/data_wirata/2002-4-4.pdf
2638              
2639             [2] https://peraturan.bkpm.go.id/jdih/userfiles/batang/Kepmenag_162_2000.pdf
2640              
2641             [3] http://www.wikiapbn.org/cuti-bersama/
2642              
2643             [4] https://kalenderindonesia.com/libur/masehi/2001
2644              
2645             [5] https://kalenderindonesia.com/libur/masehi/1991
2646              
2647             =head2 What happens when multiple religious/holidays coincide on a single calendar day?
2648              
2649             For example, in 1997, both Hijra and Ascension Day fall on May 8th. When this
2650             happens, C<ind_name> and C<eng_name> will contain all the names of the holidays
2651             separated by comma, respectively:
2652              
2653             Tahun Baru Hijriah, Kenaikan Isa Al-Masih
2654             Hijra, Ascension Day
2655              
2656             All the properties that have the same value will be set in the merged holiday
2657             data:
2658              
2659             is_holiday => 1,
2660             is_joint_leave => 1,
2661              
2662             The C<multiple> property will also be set to true:
2663              
2664             multiple => 1,
2665              
2666             All the tags will be merged:
2667              
2668             tags => ['religious', 'religion=christianity', 'calendar=lunar']
2669              
2670             You can get each holiday's data in the C<holidays> key.
2671              
2672             =head2 Data for older holidays?
2673              
2674             Will be provided if there is demand and data source.
2675              
2676             =head2 Holidays after (current year)+1?
2677              
2678             Some religious holidays, especially Vesakha, are not determined yet. Joint leave
2679             days are also usually decreed by the government in as late as October/November
2680             in the preceding year.
2681              
2682             =head2 How to calculate the difference of two dates in number of working days?
2683              
2684             Use L</count_idn_workdays>.
2685              
2686             =head1 HOMEPAGE
2687              
2688             Please visit the project's homepage at L<https://metacpan.org/release/Calendar-Indonesia-Holiday>.
2689              
2690             =head1 SOURCE
2691              
2692             Source repository is at L<https://github.com/perlancar/perl-Calendar-Indonesia-Holiday>.
2693              
2694             =head1 AUTHOR
2695              
2696             perlancar <perlancar@cpan.org>
2697              
2698             =head1 CONTRIBUTOR
2699              
2700             =for stopwords Steven Haryanto
2701              
2702             Steven Haryanto <stevenharyanto@gmail.com>
2703              
2704             =head1 CONTRIBUTING
2705              
2706              
2707             To contribute, you can send patches by email/via RT, or send pull requests on
2708             GitHub.
2709              
2710             Most of the time, you don't need to build the distribution yourself. You can
2711             simply modify the code, then test via:
2712              
2713             % prove -l
2714              
2715             If you want to build the distribution (e.g. to try to install it locally on your
2716             system), you can install L<Dist::Zilla>,
2717             L<Dist::Zilla::PluginBundle::Author::PERLANCAR>,
2718             L<Pod::Weaver::PluginBundle::Author::PERLANCAR>, and sometimes one or two other
2719             Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps required beyond
2720             that are considered a bug and can be reported to me.
2721              
2722             =head1 COPYRIGHT AND LICENSE
2723              
2724             This software is copyright (c) 2023, 2022, 2021, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 2013, 2012, 2011 by perlancar <perlancar@cpan.org>.
2725              
2726             This is free software; you can redistribute it and/or modify it under
2727             the same terms as the Perl 5 programming language system itself.
2728              
2729             =head1 BUGS
2730              
2731             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Calendar-Indonesia-Holiday>
2732              
2733             When submitting a bug or request, please include a test-file or a
2734             patch to an existing test-file that illustrates the bug or desired
2735             feature.
2736              
2737             =cut