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