xref: /webtrees/app/Date/JewishDate.php (revision 34ed513f1c8687261b812389f04f857ef07b0a5a)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2021 webtrees development team
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees\Date;
21
22use Fisharebest\ExtCalendar\JewishCalendar;
23use Fisharebest\Webtrees\I18N;
24
25/**
26 * Definitions for the Jewish calendar
27 */
28class JewishDate extends AbstractCalendarDate
29{
30    // GEDCOM calendar escape
31    public const ESCAPE = '@#DHEBREW@';
32
33    // Convert GEDCOM month names to month numbers
34    protected const MONTH_TO_NUMBER = [
35        'TSH' => 1,
36        'CSH' => 2,
37        'KSL' => 3,
38        'TVT' => 4,
39        'SHV' => 5,
40        'ADR' => 6,
41        'ADS' => 7,
42        'NSN' => 8,
43        'IYR' => 9,
44        'SVN' => 10,
45        'TMZ' => 11,
46        'AAV' => 12,
47        'ELL' => 13,
48    ];
49
50    protected const NUMBER_TO_MONTH = [
51        1  => 'TSH',
52        2  => 'CSH',
53        3  => 'KSL',
54        4  => 'TVT',
55        5  => 'SHV',
56        6  => 'ADR',
57        7  => 'ADS',
58        8  => 'NSN',
59        9  => 'IYR',
60        10 => 'SVN',
61        11 => 'TMZ',
62        12 => 'AAV',
63        13 => 'ELL',
64    ];
65
66    /**
67     * Create a date from either:
68     * a Julian day number
69     * day/month/year strings from a GEDCOM date
70     * another CalendarDate object
71     *
72     * @param array<string>|int|AbstractCalendarDate $date
73     */
74    public function __construct($date)
75    {
76        $this->calendar = new JewishCalendar();
77        parent::__construct($date);
78    }
79
80    /**
81     * Generate the %j format for a date.
82     *
83     * @return string
84     */
85    protected function formatDay(): string
86    {
87        if (I18N::locale()->script()->code() === 'Hebr') {
88            return (new JewishCalendar())->numberToHebrewNumerals($this->day, true);
89        }
90
91        return parent::formatDay();
92    }
93
94    /**
95     * Generate the %y format for a date.
96     *
97     * NOTE Short year is NOT a 2-digit year. It is for calendars such as hebrew
98     * which have a 3-digit form of 4-digit years.
99     *
100     * @return string
101     */
102    protected function formatShortYear(): string
103    {
104        if (I18N::locale()->script()->code() === 'Hebr') {
105            return (new JewishCalendar())->numberToHebrewNumerals($this->year, false);
106        }
107
108        return parent::formatLongYear();
109    }
110
111    /**
112     * Generate the %Y format for a date.
113     *
114     * @return string
115     */
116    protected function formatLongYear(): string
117    {
118        if (I18N::locale()->script()->code() === 'Hebr') {
119            return (new JewishCalendar())->numberToHebrewNumerals($this->year, true);
120        }
121
122        return parent::formatLongYear();
123    }
124
125    /**
126     * Full month name in nominative case.
127     *
128     * @param int  $month
129     * @param bool $leap_year Some calendars use leap months
130     *
131     * @return string
132     */
133    protected function monthNameNominativeCase(int $month, bool $leap_year): string
134    {
135        static $translated_month_names;
136
137        if ($translated_month_names === null) {
138            $translated_month_names = [
139                0  => '',
140                /* I18N: a month in the Jewish calendar */
141                1  => I18N::translateContext('NOMINATIVE', 'Tishrei'),
142                /* I18N: a month in the Jewish calendar */
143                2  => I18N::translateContext('NOMINATIVE', 'Heshvan'),
144                /* I18N: a month in the Jewish calendar */
145                3  => I18N::translateContext('NOMINATIVE', 'Kislev'),
146                /* I18N: a month in the Jewish calendar */
147                4  => I18N::translateContext('NOMINATIVE', 'Tevet'),
148                /* I18N: a month in the Jewish calendar */
149                5  => I18N::translateContext('NOMINATIVE', 'Shevat'),
150                /* I18N: a month in the Jewish calendar */
151                6  => I18N::translateContext('NOMINATIVE', 'Adar I'),
152                /* I18N: a month in the Jewish calendar */
153                7  => I18N::translateContext('NOMINATIVE', 'Adar'),
154                /* I18N: a month in the Jewish calendar */
155                8  => I18N::translateContext('NOMINATIVE', 'Nissan'),
156                /* I18N: a month in the Jewish calendar */
157                9  => I18N::translateContext('NOMINATIVE', 'Iyar'),
158                /* I18N: a month in the Jewish calendar */
159                10 => I18N::translateContext('NOMINATIVE', 'Sivan'),
160                /* I18N: a month in the Jewish calendar */
161                11 => I18N::translateContext('NOMINATIVE', 'Tamuz'),
162                /* I18N: a month in the Jewish calendar */
163                12 => I18N::translateContext('NOMINATIVE', 'Av'),
164                /* I18N: a month in the Jewish calendar */
165                13 => I18N::translateContext('NOMINATIVE', 'Elul'),
166            ];
167        }
168
169        if ($month === 7 && $leap_year) {
170            /* I18N: a month in the Jewish calendar */
171            return I18N::translateContext('NOMINATIVE', 'Adar II');
172        }
173
174        return $translated_month_names[$month];
175    }
176
177    /**
178     * Full month name in genitive case.
179     *
180     * @param int  $month
181     * @param bool $leap_year Some calendars use leap months
182     *
183     * @return string
184     */
185    protected function monthNameGenitiveCase(int $month, bool $leap_year): string
186    {
187        static $translated_month_names;
188
189        if ($translated_month_names === null) {
190            $translated_month_names = [
191                0  => '',
192                /* I18N: a month in the Jewish calendar */
193                1  => I18N::translateContext('GENITIVE', 'Tishrei'),
194                /* I18N: a month in the Jewish calendar */
195                2  => I18N::translateContext('GENITIVE', 'Heshvan'),
196                /* I18N: a month in the Jewish calendar */
197                3  => I18N::translateContext('GENITIVE', 'Kislev'),
198                /* I18N: a month in the Jewish calendar */
199                4  => I18N::translateContext('GENITIVE', 'Tevet'),
200                /* I18N: a month in the Jewish calendar */
201                5  => I18N::translateContext('GENITIVE', 'Shevat'),
202                /* I18N: a month in the Jewish calendar */
203                6  => I18N::translateContext('GENITIVE', 'Adar I'),
204                /* I18N: a month in the Jewish calendar */
205                7  => I18N::translateContext('GENITIVE', 'Adar'),
206                /* I18N: a month in the Jewish calendar */
207                8  => I18N::translateContext('GENITIVE', 'Nissan'),
208                /* I18N: a month in the Jewish calendar */
209                9  => I18N::translateContext('GENITIVE', 'Iyar'),
210                /* I18N: a month in the Jewish calendar */
211                10 => I18N::translateContext('GENITIVE', 'Sivan'),
212                /* I18N: a month in the Jewish calendar */
213                11 => I18N::translateContext('GENITIVE', 'Tamuz'),
214                /* I18N: a month in the Jewish calendar */
215                12 => I18N::translateContext('GENITIVE', 'Av'),
216                /* I18N: a month in the Jewish calendar */
217                13 => I18N::translateContext('GENITIVE', 'Elul'),
218            ];
219        }
220
221        if ($month === 7 && $leap_year) {
222            /* I18N: a month in the Jewish calendar */
223            return I18N::translateContext('GENITIVE', 'Adar II');
224        }
225
226        return $translated_month_names[$month];
227    }
228
229    /**
230     * Full month name in locative case.
231     *
232     * @param int  $month
233     * @param bool $leap_year Some calendars use leap months
234     *
235     * @return string
236     */
237    protected function monthNameLocativeCase(int $month, bool $leap_year): string
238    {
239        static $translated_month_names;
240
241        if ($translated_month_names === null) {
242            $translated_month_names = [
243                0  => '',
244                /* I18N: a month in the Jewish calendar */
245                1  => I18N::translateContext('LOCATIVE', 'Tishrei'),
246                /* I18N: a month in the Jewish calendar */
247                2  => I18N::translateContext('LOCATIVE', 'Heshvan'),
248                /* I18N: a month in the Jewish calendar */
249                3  => I18N::translateContext('LOCATIVE', 'Kislev'),
250                /* I18N: a month in the Jewish calendar */
251                4  => I18N::translateContext('LOCATIVE', 'Tevet'),
252                /* I18N: a month in the Jewish calendar */
253                5  => I18N::translateContext('LOCATIVE', 'Shevat'),
254                /* I18N: a month in the Jewish calendar */
255                6  => I18N::translateContext('LOCATIVE', 'Adar I'),
256                /* I18N: a month in the Jewish calendar */
257                7  => I18N::translateContext('LOCATIVE', 'Adar'),
258                /* I18N: a month in the Jewish calendar */
259                8  => I18N::translateContext('LOCATIVE', 'Nissan'),
260                /* I18N: a month in the Jewish calendar */
261                9  => I18N::translateContext('LOCATIVE', 'Iyar'),
262                /* I18N: a month in the Jewish calendar */
263                10 => I18N::translateContext('LOCATIVE', 'Sivan'),
264                /* I18N: a month in the Jewish calendar */
265                11 => I18N::translateContext('LOCATIVE', 'Tamuz'),
266                /* I18N: a month in the Jewish calendar */
267                12 => I18N::translateContext('LOCATIVE', 'Av'),
268                /* I18N: a month in the Jewish calendar */
269                13 => I18N::translateContext('LOCATIVE', 'Elul'),
270            ];
271        }
272
273        if ($month === 7 && $leap_year) {
274            /* I18N: a month in the Jewish calendar */
275            return I18N::translateContext('LOCATIVE', 'Adar II');
276        }
277
278        return $translated_month_names[$month];
279    }
280
281    /**
282     * Full month name in instrumental case.
283     *
284     * @param int  $month
285     * @param bool $leap_year Some calendars use leap months
286     *
287     * @return string
288     */
289    protected function monthNameInstrumentalCase(int $month, bool $leap_year): string
290    {
291        static $translated_month_names;
292
293        if ($translated_month_names === null) {
294            $translated_month_names = [
295                0  => '',
296                /* I18N: a month in the Jewish calendar */
297                1  => I18N::translateContext('INSTRUMENTAL', 'Tishrei'),
298                /* I18N: a month in the Jewish calendar */
299                2  => I18N::translateContext('INSTRUMENTAL', 'Heshvan'),
300                /* I18N: a month in the Jewish calendar */
301                3  => I18N::translateContext('INSTRUMENTAL', 'Kislev'),
302                /* I18N: a month in the Jewish calendar */
303                4  => I18N::translateContext('INSTRUMENTAL', 'Tevet'),
304                /* I18N: a month in the Jewish calendar */
305                5  => I18N::translateContext('INSTRUMENTAL', 'Shevat'),
306                /* I18N: a month in the Jewish calendar */
307                6  => I18N::translateContext('INSTRUMENTAL', 'Adar I'),
308                /* I18N: a month in the Jewish calendar */
309                7  => I18N::translateContext('INSTRUMENTAL', 'Adar'),
310                /* I18N: a month in the Jewish calendar */
311                8  => I18N::translateContext('INSTRUMENTAL', 'Nissan'),
312                /* I18N: a month in the Jewish calendar */
313                9  => I18N::translateContext('INSTRUMENTAL', 'Iyar'),
314                /* I18N: a month in the Jewish calendar */
315                10 => I18N::translateContext('INSTRUMENTAL', 'Sivan'),
316                /* I18N: a month in the Jewish calendar */
317                11 => I18N::translateContext('INSTRUMENTAL', 'Tamuz'),
318                /* I18N: a month in the Jewish calendar */
319                12 => I18N::translateContext('INSTRUMENTAL', 'Av'),
320                /* I18N: a month in the Jewish calendar */
321                13 => I18N::translateContext('INSTRUMENTAL', 'Elul'),
322            ];
323        }
324
325        if ($month === 7 && $leap_year) {
326            /* I18N: a month in the Jewish calendar */
327            return I18N::translateContext('INSTRUMENTAL', 'Adar II');
328        }
329
330        return $translated_month_names[$month];
331    }
332
333    /**
334     * Abbreviated month name
335     *
336     * @param int  $month
337     * @param bool $leap_year Some calendars use leap months
338     *
339     * @return string
340     */
341    protected function monthNameAbbreviated(int $month, bool $leap_year): string
342    {
343        return $this->monthNameNominativeCase($month, $leap_year);
344    }
345
346    /**
347     * Which months follows this one? Calendars with leap-months should provide their own implementation.
348     *
349     * @return array<int>
350     */
351    protected function nextMonth(): array
352    {
353        if ($this->month === 6 && !$this->isLeapYear()) {
354            return [
355                $this->year,
356                8,
357            ];
358        }
359
360        return [
361            $this->year + ($this->month === 13 ? 1 : 0),
362            $this->month % 13 + 1,
363        ];
364    }
365}
366