Japan::initialize()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 29
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 22
c 4
b 0
f 0
dl 0
loc 29
rs 9.568
cc 2
nc 2
nop 0
1
<?php declare(strict_types=1);
2
3
/**
4
 * This file is part of the Yasumi package.
5
 *
6
 * Copyright (c) 2015 - 2020 AzuyaLabs
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * @author Sacha Telgenhof <[email protected]>
12
 */
13
14
namespace Yasumi\Provider;
15
16
use DateInterval;
17
use DateTime;
18
use DateTimeInterface;
19
use Yasumi\Exception\InvalidDateException;
20
use Yasumi\Exception\UnknownLocaleException;
21
use Yasumi\Holiday;
22
use Yasumi\SubstituteHoliday;
23
24
/**
25
 * Provider for all holidays in the Japan.
26
 */
27
class Japan extends AbstractProvider
28
{
29
    use CommonHolidays;
30
31
    /**
32
     * Code to identify this Holiday Provider. Typically this is the ISO3166 code corresponding to the respective
33
     * country or sub-region.
34
     */
35
    public const ID = 'JP';
36
37
    /**
38
     * The gradient parameter of the approximate expression to calculate equinox day.
39
     */
40
    private const EQUINOX_GRADIENT = 0.242194;
41
42
    /**
43
     * The initial parameter of the approximate expression to calculate vernal equinox day from 1900 to 1979.
44
     */
45
    private const VERNAL_EQUINOX_PARAM_1979 = 20.8357;
46
47
    /**
48
     * The initial parameter of the approximate expression to calculate vernal equinox day from 1980 to 2099.
49
     */
50
    private const VERNAL_EQUINOX_PARAM_2099 = 20.8431;
51
52
    /**
53
     * The initial parameter of the approximate expression to calculate vernal equinox day from 2100 to 2150.
54
     */
55
    private const VERNAL_EQUINOX_PARAM_2150 = 21.8510;
56
57
    /**
58
     * The initial parameter of the approximate expression to calculate autumnal equinox day from 1900 to 1979.
59
     */
60
    private const AUTUMNAL_EQUINOX_PARAM_1979 = 23.2588;
61
62
    /**
63
     * The initial parameter of the approximate expression to calculate autumnal equinox day from 1980 to 2099.
64
     */
65
    private const AUTUMNAL_EQUINOX_PARAM_2099 = 23.2488;
66
67
    /**
68
     * The initial parameter of the approximate expression to calculate autumnal equinox day from 2100 to 2150.
69
     */
70
    private const AUTUMNAL_EQUINOX_PARAM_2150 = 24.2488;
71
72
    /**
73
     * Initialize holidays for Japan.
74
     *
75
     * @throws InvalidDateException
76
     * @throws \InvalidArgumentException
77
     * @throws UnknownLocaleException
78
     * @throws \Exception
79
     */
80
    public function initialize(): void
81
    {
82
        $this->timezone = 'Asia/Tokyo';
83
84
        // Add common holidays
85
        if ($this->year >= 1948) {
86
            $this->addHoliday($this->newYearsDay($this->year, $this->timezone, $this->locale));
87
        }
88
89
        // Calculate other holidays
90
        $this->calculateNationalFoundationDay();
91
        $this->calculateShowaDay();
92
        $this->calculateConstitutionMemorialDay();
93
        $this->calculateChildrensDay();
94
        $this->calculateCultureDay();
95
        $this->calculateLaborThanksgivingDay();
96
        $this->calculateEmperorsBirthday();
97
        $this->calculateVernalEquinoxDay();
98
        $this->calculateComingOfAgeDay();
99
        $this->calculateGreeneryDay();
100
        $this->calculateMarineDay();
101
        $this->calculateMountainDay();
102
        $this->calculateRespectForTheAgeDay();
103
        $this->calculateSportsDay();
104
        $this->calculateAutumnalEquinoxDay();
105
        $this->calculateSubstituteHolidays();
106
        $this->calculateCoronationDay();
107
        $this->calculateEnthronementProclamationCeremony();
108
        $this->calculateBridgeHolidays();
109
    }
110
111
    /**
112
     * National Foundation Day. National Foundation Day is held on February 11th and established since 1966.
113
     *
114
     * @throws \Exception
115
     */
116
    private function calculateNationalFoundationDay(): void
117
    {
118
        if ($this->year >= 1966) {
119
            $this->addHoliday(new Holiday(
120
                'nationalFoundationDay',
121
                [
122
                    'en' => 'National Foundation Day',
123
                    'ja' => '建国記念の日',
124
                ],
125
                new DateTime("$this->year-2-11", DateTimeZoneFactory::getDateTimeZone($this->timezone)),
126
                $this->locale
127
            ));
128
        }
129
    }
130
131
    /**
132
     * Showa Day. Showa Day is held on April 29th and established since 2007.
133
     *
134
     * @throws \Exception
135
     */
136
    private function calculateShowaDay(): void
137
    {
138
        if ($this->year >= 2007) {
139
            $this->addHoliday(new Holiday(
140
                'showaDay',
141
                [
142
                    'en' => 'Showa Day',
143
                    'ja' => '昭和の日',
144
                ],
145
                new DateTime("$this->year-4-29", DateTimeZoneFactory::getDateTimeZone($this->timezone)),
146
                $this->locale
147
            ));
148
        }
149
    }
150
151
    /**
152
     * Constitution Memorial Day. Constitution Memorial Day is held on May 3rd and established since 1948.
153
     *
154
     * @throws \Exception
155
     */
156
    private function calculateConstitutionMemorialDay(): void
157
    {
158
        if ($this->year >= 1948) {
159
            $this->addHoliday(new Holiday(
160
                'constitutionMemorialDay',
161
                [
162
                    'en' => 'Constitution Memorial Day',
163
                    'ja' => '憲法記念日',
164
                ],
165
                new DateTime("$this->year-5-3", DateTimeZoneFactory::getDateTimeZone($this->timezone)),
166
                $this->locale
167
            ));
168
        }
169
    }
170
171
    /**
172
     * Children's Day. Children's Day is held on May 5th and established since 1948.
173
     *
174
     * @throws \Exception
175
     */
176
    private function calculateChildrensDay(): void
177
    {
178
        if ($this->year >= 1948) {
179
            $this->addHoliday(new Holiday(
180
                'childrensDay',
181
                [
182
                    'en' => 'Children’s Day',
183
                    'ja' => 'こどもの日',
184
                ],
185
                new DateTime("$this->year-5-5", DateTimeZoneFactory::getDateTimeZone($this->timezone)),
186
                $this->locale
187
            ));
188
        }
189
    }
190
191
    /**
192
     * Culture Day. Culture Day is held on November 11th and established since 1948.
193
     *
194
     * @throws \Exception
195
     */
196
    private function calculateCultureDay(): void
197
    {
198
        if ($this->year >= 1948) {
199
            $this->addHoliday(new Holiday(
200
                'cultureDay',
201
                ['en' => 'Culture Day', 'ja' => '文化の日'],
202
                new DateTime("$this->year-11-3", DateTimeZoneFactory::getDateTimeZone($this->timezone)),
203
                $this->locale
204
            ));
205
        }
206
    }
207
208
    /**
209
     * Labor Thanksgiving Day. Labor Thanksgiving Day is held on November 23rd and established since 1948.
210
     *
211
     * @throws \Exception
212
     */
213
    private function calculateLaborThanksgivingDay(): void
214
    {
215
        if ($this->year >= 1948) {
216
            $this->addHoliday(new Holiday(
217
                'laborThanksgivingDay',
218
                ['en' => 'Labor Thanksgiving Day', 'ja' => '勤労感謝の日'],
219
                new DateTime("$this->year-11-23", DateTimeZoneFactory::getDateTimeZone($this->timezone)),
220
                $this->locale
221
            ));
222
        }
223
    }
224
225
    /**
226
     * Emperors Birthday.
227
     * The Emperors Birthday is on April 29rd and celebrated as such since 1949 to 1988.
228
     * December 23rd and celebrated as such since 1989 to 2018.
229
     * February 23rd and celebrated as such since 2020.(Coronation Day of the new Emperor, May 1, 2019)
230
     *
231
     * @throws \Exception
232
     */
233
    private function calculateEmperorsBirthday(): void
234
    {
235
        $emperorsBirthday = false;
236
        if ($this->year >= 2020) {
237
            $emperorsBirthday = "$this->year-2-23";
238
        } elseif ($this->year >= 1989 && $this->year < 2019) {
239
            $emperorsBirthday = "$this->year-12-23";
240
        } elseif ($this->year >= 1949 && $this->year < 1988) {
241
            $emperorsBirthday = "$this->year-4-29";
242
        }
243
244
        if (\is_string($emperorsBirthday)) {
245
            $this->addHoliday(new Holiday(
246
                'emperorsBirthday',
247
                ['en' => 'Emperors Birthday', 'ja' => '天皇誕生日'],
248
                new DateTime($emperorsBirthday, DateTimeZoneFactory::getDateTimeZone($this->timezone)),
249
                $this->locale
250
            ));
251
        }
252
    }
253
254
    /**
255
     * Calculate Vernal Equinox Day.
256
     *
257
     * This national holiday was established in 1948 as a day for the admiration
258
     * of nature and the love of living things. Prior to 1948, the vernal equinox was an imperial ancestor worship
259
     * festival called Shunki kōrei-sai (春季皇霊祭).
260
     *
261
     * @link http://www.h3.dion.ne.jp/~sakatsu/holiday_topic.htm (in Japanese)
262
     *
263
     * @throws InvalidDateException
264
     * @throws \InvalidArgumentException
265
     * @throws UnknownLocaleException
266
     * @throws \Exception
267
     */
268
    private function calculateVernalEquinoxDay(): void
269
    {
270
        $day = null;
271
        if ($this->year >= 1948 && $this->year <= 1979) {
272
            $day = \floor(self::VERNAL_EQUINOX_PARAM_1979 + self::EQUINOX_GRADIENT * ($this->year - 1980) - \floor(($this->year - 1983) / 4));
273
        } elseif ($this->year <= 2099) {
274
            $day = \floor(self::VERNAL_EQUINOX_PARAM_2099 + self::EQUINOX_GRADIENT * ($this->year - 1980) - \floor(($this->year - 1980) / 4));
275
        } elseif ($this->year <= 2150) {
276
            $day = \floor(self::VERNAL_EQUINOX_PARAM_2150 + self::EQUINOX_GRADIENT * ($this->year - 1980) - \floor(($this->year - 1980) / 4));
277
        }
278
279
        if ($this->year < 1948 || $this->year > 2150) {
280
            $day = null;
281
        }
282
283
        if (\is_numeric($day)) {
284
            $this->addHoliday(new Holiday(
285
                'vernalEquinoxDay',
286
                ['en' => 'Vernal Equinox Day', 'ja' => '春分の日'],
287
                new DateTime("$this->year-3-$day", DateTimeZoneFactory::getDateTimeZone($this->timezone)),
288
                $this->locale
289
            ));
290
        }
291
    }
292
293
    /**
294
     * Calculate Coming of Age Day.
295
     *
296
     * Coming of Age Day was established after 1948 on January 15th. After 2000 it was changed to be the second monday
297
     * of January.
298
     *
299
     * @throws InvalidDateException
300
     * @throws \InvalidArgumentException
301
     * @throws UnknownLocaleException
302
     * @throws \Exception
303
     * @throws \Exception
304
     */
305
    private function calculateComingOfAgeDay(): void
306
    {
307
        $date = null;
308
        if ($this->year >= 2000) {
309
            $date = new DateTime("second monday of january $this->year", DateTimeZoneFactory::getDateTimeZone($this->timezone));
310
        } elseif ($this->year >= 1948) {
311
            $date = new DateTime("$this->year-1-15", DateTimeZoneFactory::getDateTimeZone($this->timezone));
312
        }
313
314
        if ($date instanceof DateTimeInterface) {
315
            $this->addHoliday(new Holiday(
316
                'comingOfAgeDay',
317
                ['en' => 'Coming of Age Day', 'ja' => '成人の日'],
318
                $date,
319
                $this->locale
320
            ));
321
        }
322
    }
323
324
    /**
325
     * Calculates Greenery Day.
326
     *
327
     * Greenery Day was established from 1989 on April 29th. After 2007 it was changed to be May 4th.
328
     *
329
     * @throws InvalidDateException
330
     * @throws \InvalidArgumentException
331
     * @throws UnknownLocaleException
332
     * @throws \Exception
333
     * @throws \Exception
334
     */
335
    private function calculateGreeneryDay(): void
336
    {
337
        $date = null;
338
        if ($this->year >= 2007) {
339
            $date = new DateTime("$this->year-5-4", DateTimeZoneFactory::getDateTimeZone($this->timezone));
340
        } elseif ($this->year >= 1989) {
341
            $date = new DateTime("$this->year-4-29", DateTimeZoneFactory::getDateTimeZone($this->timezone));
342
        }
343
344
        if ($date instanceof DateTimeInterface) {
345
            $this->addHoliday(new Holiday(
346
                'greeneryDay',
347
                ['en' => 'Greenery Day', 'ja' => 'みどりの日'],
348
                $date,
349
                $this->locale
350
            ));
351
        }
352
    }
353
354
    /**
355
     * Calculates Marine Day.
356
     *
357
     * Marine Day was established since 1996 on July 20th. After 2003 it was changed to be the third monday of July.In
358
     * 2020 is July 23th.
359
     *
360
     * @throws InvalidDateException
361
     * @throws \InvalidArgumentException
362
     * @throws UnknownLocaleException
363
     * @throws \Exception
364
     * @throws \Exception
365
     * @throws \Exception
366
     */
367
    private function calculateMarineDay(): void
368
    {
369
        $date = null;
370
        if (2020 === $this->year) {
371
            $date = new DateTime("$this->year-7-23", DateTimeZoneFactory::getDateTimeZone($this->timezone));
372
        } elseif ($this->year >= 2003) {
373
            $date = new DateTime("third monday of july $this->year", DateTimeZoneFactory::getDateTimeZone($this->timezone));
374
        } elseif ($this->year >= 1996) {
375
            $date = new DateTime("$this->year-7-20", DateTimeZoneFactory::getDateTimeZone($this->timezone));
376
        }
377
378
        if ($date instanceof DateTimeInterface) {
379
            $this->addHoliday(new Holiday(
380
                'marineDay',
381
                ['en' => 'Marine Day', 'ja' => '海の日'],
382
                $date,
383
                $this->locale
384
            ));
385
        }
386
    }
387
388
    /**
389
     * Calculates MountainDay
390
     *
391
     * Mountain Day. Mountain Day is held on August 11th and established since 2016.In 2020 is August 10th.
392
     *
393
     * @throws \InvalidArgumentException
394
     * @throws UnknownLocaleException
395
     * @throws \Exception
396
     * @throws \Exception
397
     */
398
    private function calculateMountainDay(): void
399
    {
400
        $date = null;
401
        if (2020 === $this->year) {
402
            $date = new DateTime("$this->year-8-10", DateTimeZoneFactory::getDateTimeZone($this->timezone));
403
        } elseif ($this->year >= 2016) {
404
            $date = new DateTime("$this->year-8-11", DateTimeZoneFactory::getDateTimeZone($this->timezone));
405
        }
406
407
        if ($date instanceof DateTimeInterface) {
408
            $this->addHoliday(new Holiday(
409
                'mountainDay',
410
                ['en' => 'Mountain Day', 'ja' => '山の日'],
411
                $date,
412
                $this->locale
413
            ));
414
        }
415
    }
416
417
    /**
418
     * Calculates Respect for the Age Day.
419
     *
420
     * Respect for the Age Day was established since 1996 on September 15th. After 2003 it was changed to be the third
421
     * monday of September.
422
     *
423
     * @throws InvalidDateException
424
     * @throws \InvalidArgumentException
425
     * @throws UnknownLocaleException
426
     * @throws \Exception
427
     * @throws \Exception
428
     */
429
    private function calculateRespectForTheAgeDay(): void
430
    {
431
        $date = null;
432
        if ($this->year >= 2003) {
433
            $date = new DateTime("third monday of september $this->year", DateTimeZoneFactory::getDateTimeZone($this->timezone));
434
        } elseif ($this->year >= 1996) {
435
            $date = new DateTime("$this->year-9-15", DateTimeZoneFactory::getDateTimeZone($this->timezone));
436
        }
437
438
        if ($date instanceof DateTimeInterface) {
439
            $this->addHoliday(new Holiday(
440
                'respectfortheAgedDay',
441
                ['en' => 'Respect for the Aged Day', 'ja' => '敬老の日'],
442
                $date,
443
                $this->locale
444
            ));
445
        }
446
    }
447
448
    /**
449
     * Calculates Health And Sports Day.
450
     *
451
     * Health And Sports Day was established since 1966 on October 10th. After 2000 it was changed to be the second
452
     * monday of October.In 2020 is July 24th.
453
     *
454
     * @throws InvalidDateException
455
     * @throws \InvalidArgumentException
456
     * @throws UnknownLocaleException
457
     * @throws \Exception
458
     * @throws \Exception
459
     * @throws \Exception
460
     */
461
    private function calculateSportsDay(): void
462
    {
463
        $date = null;
464
        if (2020 === $this->year) {
465
            $date = new DateTime("$this->year-7-24", DateTimeZoneFactory::getDateTimeZone($this->timezone));
466
        } elseif ($this->year >= 2000) {
467
            $date = new DateTime("second monday of october $this->year", DateTimeZoneFactory::getDateTimeZone($this->timezone));
468
        } elseif ($this->year >= 1996) {
469
            $date = new DateTime("$this->year-10-10", DateTimeZoneFactory::getDateTimeZone($this->timezone));
470
        }
471
472
        $holidayName = ['en' => 'Health And Sports Day', 'ja' => '体育の日'];
473
        if ($this->year >= 2020) {
474
            $holidayName = ['en' => 'Sports Day', 'ja' => 'スポーツの日'];
475
        }
476
477
        if ($date instanceof DateTimeInterface) {
478
            $this->addHoliday(new Holiday(
479
                'sportsDay',
480
                $holidayName,
481
                $date,
482
                $this->locale
483
            ));
484
        }
485
    }
486
487
    /**
488
     * Calculate Autumnal Equinox Day.
489
     *
490
     * This national holiday was established in 1948 as a day on which to honor
491
     * one's ancestors and remember the dead. Prior to 1948, the autumnal equinox was an imperial ancestor worship
492
     * festival called Shūki kōrei-sai (秋季皇霊祭).
493
     *
494
     * @link http://www.h3.dion.ne.jp/~sakatsu/holiday_topic.htm (in Japanese)
495
     *
496
     * @throws InvalidDateException
497
     * @throws \InvalidArgumentException
498
     * @throws UnknownLocaleException
499
     * @throws \Exception
500
     */
501
    private function calculateAutumnalEquinoxDay(): void
502
    {
503
        $day = null;
504
        if ($this->year >= 1948 && $this->year <= 1979) {
505
            $day = \floor(self::AUTUMNAL_EQUINOX_PARAM_1979 + self::EQUINOX_GRADIENT * ($this->year - 1980) - \floor(($this->year - 1983) / 4));
506
        } elseif ($this->year <= 2099) {
507
            $day = \floor(self::AUTUMNAL_EQUINOX_PARAM_2099 + self::EQUINOX_GRADIENT * ($this->year - 1980) - \floor(($this->year - 1980) / 4));
508
        } elseif ($this->year <= 2150) {
509
            $day = \floor(self::AUTUMNAL_EQUINOX_PARAM_2150 + self::EQUINOX_GRADIENT * ($this->year - 1980) - \floor(($this->year - 1980) / 4));
510
        }
511
512
        if ($this->year < 1948 || $this->year > 2150) {
513
            $day = null;
514
        }
515
516
        if (\is_numeric($day)) {
517
            $this->addHoliday(new Holiday(
518
                'autumnalEquinoxDay',
519
                ['en' => 'Autumnal Equinox Day', 'ja' => '秋分の日'],
520
                new DateTime("$this->year-9-$day", DateTimeZoneFactory::getDateTimeZone($this->timezone)),
521
                $this->locale
522
            ));
523
        }
524
    }
525
526
    /**
527
     * Calculate the substitute holidays.
528
     *
529
     * Generally if a national holiday falls on a Sunday, the holiday is observed the next working day (not being
530
     * another holiday).
531
     *
532
     * @throws InvalidDateException
533
     * @throws \InvalidArgumentException
534
     * @throws UnknownLocaleException
535
     * @throws \Exception
536
     */
537
    private function calculateSubstituteHolidays(): void
538
    {
539
        // Get initial list of holiday dates
540
        $dates = $this->getHolidayDates();
541
542
        // Loop through all holidays
543
        foreach ($this->getHolidays() as $holiday) {
544
            $date = clone $holiday;
545
546
            // If holidays falls on a Sunday
547
            if (0 === (int) $holiday->format('w')) {
548
                if ($this->year >= 2007) {
549
                    // Find next week day (not being another holiday)
550
                    while (\in_array($date, $dates, false)) {
551
                        $date->add(new DateInterval('P1D'));
552
                        continue;
553
                    }
554
                } elseif ($holiday >= '1973-04-12') {
555
                    $date->add(new DateInterval('P1D'));
556
                    if (\in_array($date, $dates, false)) {
557
                        continue; // @codeCoverageIgnore
558
                    }
559
                } else {
560
                    continue;
561
                }
562
563
                // Add a new holiday that is substituting the original holiday
564
                $substitute = new SubstituteHoliday(
565
                    $holiday,
566
                    [],
567
                    $date,
568
                    $this->locale
569
                );
570
571
                $this->addHoliday($substitute);
572
            }
573
        }
574
    }
575
576
    /**
577
     * Coronation Day. Coronation Day is The new Emperor Coronation.
578
     * This holiday is only 2019.
579
     *
580
     * @throws \Exception
581
     */
582
    private function calculateCoronationDay(): void
583
    {
584
        if (2019 === $this->year) {
585
            $this->addHoliday(new Holiday(
586
                'coronationDay',
587
                ['en' => 'Coronation Day', 'ja' => '即位の日'],
588
                new DateTime("$this->year-5-1", DateTimeZoneFactory::getDateTimeZone($this->timezone)),
589
                $this->locale
590
            ));
591
        }
592
    }
593
594
    /**
595
     * Enthronement Proclamation Ceremony. Enthronement Proclamation Ceremony is The New Emperor enthronement ceremony.
596
     * This holiday only 2019.
597
     *
598
     * @throws \Exception
599
     */
600
    private function calculateEnthronementProclamationCeremony(): void
601
    {
602
        if (2019 === $this->year) {
603
            $this->addHoliday(new Holiday(
604
                'enthronementProclamationCeremony',
605
                ['en' => 'Enthronement Proclamation Ceremony', 'ja' => '即位礼正殿の儀'],
606
                new DateTime("$this->year-10-22", DateTimeZoneFactory::getDateTimeZone($this->timezone)),
607
                $this->locale
608
            ));
609
        }
610
    }
611
612
    /**
613
     * Calculate public bridge holidays.
614
     *
615
     * Any day that falls between two other national holidays also becomes a holiday, known as a bridge holiday.
616
     *
617
     * @throws InvalidDateException
618
     * @throws \InvalidArgumentException
619
     * @throws UnknownLocaleException
620
     * @throws \Exception
621
     */
622
    private function calculateBridgeHolidays(): void
623
    {
624
        // Get initial list of holidays and iterator
625
        $datesIterator = $this->getIterator();
626
627
        $counter = 1;
628
        // Loop through all defined holidays
629
        while ($datesIterator->valid()) {
630
            $previous = $datesIterator->current();
631
            $datesIterator->next();
632
633
            // Skip if next holiday is not set
634
            if (null === $datesIterator->current()) {
635
                continue;
636
            }
637
638
            // Determine if gap between holidays is one day and create bridge holiday
639
            if (2 === (int) $previous->diff($datesIterator->current())->format('%a')) {
640
                $bridgeDate = clone $previous;
641
                $bridgeDate->add(new DateInterval('P1D'));
642
643
                $this->addHoliday(new Holiday('bridgeDay' . $counter, [
644
                    'en' => 'Bridge Public holiday',
645
                    'ja' => '国民の休日',
646
                ], $bridgeDate, $this->locale));
647
                $counter++;
648
            }
649
        }
650
    }
651
}
652