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