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