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