xref: /webtrees/app/Date/JewishDate.php (revision 62a43f67ae5ca59481845b3fff5258b8751ccba4)
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_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<string>|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        if (I18N::locale()->script()->code() === 'Hebr') {
73            return (new JewishCalendar())->numberToHebrewNumerals($this->day, true);
74        }
75
76        return parent::formatDay();
77    }
78
79    /**
80     * Generate the %y format for a date.
81     *
82     * NOTE Short year is NOT a 2-digit year. It is for calendars such as hebrew
83     * which have a 3-digit form of 4-digit years.
84     *
85     * @return string
86     */
87    protected function formatShortYear(): string
88    {
89        if (I18N::locale()->script()->code() === 'Hebr') {
90            return (new JewishCalendar())->numberToHebrewNumerals($this->year, false);
91        }
92
93        return parent::formatLongYear();
94    }
95
96    /**
97     * Generate the %Y format for a date.
98     *
99     * @return string
100     */
101    protected function formatLongYear(): string
102    {
103        if (I18N::locale()->script()->code() === 'Hebr') {
104            return (new JewishCalendar())->numberToHebrewNumerals($this->year, true);
105        }
106
107        return parent::formatLongYear();
108    }
109
110    /**
111     * Full month name in nominative case.
112     *
113     * @param int  $month
114     * @param bool $leap_year Some calendars use leap months
115     *
116     * @return string
117     */
118    protected function monthNameNominativeCase(int $month, bool $leap_year): string
119    {
120        static $translated_month_names;
121
122        if ($translated_month_names === null) {
123            $translated_month_names = [
124                0  => '',
125                /* I18N: a month in the Jewish calendar */
126                1  => I18N::translateContext('NOMINATIVE', 'Tishrei'),
127                /* I18N: a month in the Jewish calendar */
128                2  => I18N::translateContext('NOMINATIVE', 'Heshvan'),
129                /* I18N: a month in the Jewish calendar */
130                3  => I18N::translateContext('NOMINATIVE', 'Kislev'),
131                /* I18N: a month in the Jewish calendar */
132                4  => I18N::translateContext('NOMINATIVE', 'Tevet'),
133                /* I18N: a month in the Jewish calendar */
134                5  => I18N::translateContext('NOMINATIVE', 'Shevat'),
135                /* I18N: a month in the Jewish calendar */
136                6  => I18N::translateContext('NOMINATIVE', 'Adar I'),
137                /* I18N: a month in the Jewish calendar */
138                7  => I18N::translateContext('NOMINATIVE', 'Adar'),
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            /* I18N: a month in the Jewish calendar */
156            return I18N::translateContext('NOMINATIVE', 'Adar II');
157        }
158
159        return $translated_month_names[$month];
160    }
161
162    /**
163     * Full month name in genitive case.
164     *
165     * @param int  $month
166     * @param bool $leap_year Some calendars use leap months
167     *
168     * @return string
169     */
170    protected function monthNameGenitiveCase(int $month, bool $leap_year): string
171    {
172        static $translated_month_names;
173
174        if ($translated_month_names === null) {
175            $translated_month_names = [
176                0  => '',
177                /* I18N: a month in the Jewish calendar */
178                1  => I18N::translateContext('GENITIVE', 'Tishrei'),
179                /* I18N: a month in the Jewish calendar */
180                2  => I18N::translateContext('GENITIVE', 'Heshvan'),
181                /* I18N: a month in the Jewish calendar */
182                3  => I18N::translateContext('GENITIVE', 'Kislev'),
183                /* I18N: a month in the Jewish calendar */
184                4  => I18N::translateContext('GENITIVE', 'Tevet'),
185                /* I18N: a month in the Jewish calendar */
186                5  => I18N::translateContext('GENITIVE', 'Shevat'),
187                /* I18N: a month in the Jewish calendar */
188                6  => I18N::translateContext('GENITIVE', 'Adar I'),
189                /* I18N: a month in the Jewish calendar */
190                7  => I18N::translateContext('GENITIVE', 'Adar'),
191                /* I18N: a month in the Jewish calendar */
192                8  => I18N::translateContext('GENITIVE', 'Nissan'),
193                /* I18N: a month in the Jewish calendar */
194                9  => I18N::translateContext('GENITIVE', 'Iyar'),
195                /* I18N: a month in the Jewish calendar */
196                10 => I18N::translateContext('GENITIVE', 'Sivan'),
197                /* I18N: a month in the Jewish calendar */
198                11 => I18N::translateContext('GENITIVE', 'Tamuz'),
199                /* I18N: a month in the Jewish calendar */
200                12 => I18N::translateContext('GENITIVE', 'Av'),
201                /* I18N: a month in the Jewish calendar */
202                13 => I18N::translateContext('GENITIVE', 'Elul'),
203            ];
204        }
205
206        if ($month === 7 && $leap_year) {
207            /* I18N: a month in the Jewish calendar */
208            return I18N::translateContext('GENITIVE', 'Adar II');
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                8  => I18N::translateContext('LOCATIVE', 'Nissan'),
245                /* I18N: a month in the Jewish calendar */
246                9  => I18N::translateContext('LOCATIVE', 'Iyar'),
247                /* I18N: a month in the Jewish calendar */
248                10 => I18N::translateContext('LOCATIVE', 'Sivan'),
249                /* I18N: a month in the Jewish calendar */
250                11 => I18N::translateContext('LOCATIVE', 'Tamuz'),
251                /* I18N: a month in the Jewish calendar */
252                12 => I18N::translateContext('LOCATIVE', 'Av'),
253                /* I18N: a month in the Jewish calendar */
254                13 => I18N::translateContext('LOCATIVE', 'Elul'),
255            ];
256        }
257
258        if ($month === 7 && $leap_year) {
259            /* I18N: a month in the Jewish calendar */
260            return I18N::translateContext('LOCATIVE', 'Adar II');
261        }
262
263        return $translated_month_names[$month];
264    }
265
266    /**
267     * Full month name in instrumental case.
268     *
269     * @param int  $month
270     * @param bool $leap_year Some calendars use leap months
271     *
272     * @return string
273     */
274    protected function monthNameInstrumentalCase(int $month, bool $leap_year): string
275    {
276        static $translated_month_names;
277
278        if ($translated_month_names === null) {
279            $translated_month_names = [
280                0  => '',
281                /* I18N: a month in the Jewish calendar */
282                1  => I18N::translateContext('INSTRUMENTAL', 'Tishrei'),
283                /* I18N: a month in the Jewish calendar */
284                2  => I18N::translateContext('INSTRUMENTAL', 'Heshvan'),
285                /* I18N: a month in the Jewish calendar */
286                3  => I18N::translateContext('INSTRUMENTAL', 'Kislev'),
287                /* I18N: a month in the Jewish calendar */
288                4  => I18N::translateContext('INSTRUMENTAL', 'Tevet'),
289                /* I18N: a month in the Jewish calendar */
290                5  => I18N::translateContext('INSTRUMENTAL', 'Shevat'),
291                /* I18N: a month in the Jewish calendar */
292                6  => I18N::translateContext('INSTRUMENTAL', 'Adar I'),
293                /* I18N: a month in the Jewish calendar */
294                7  => I18N::translateContext('INSTRUMENTAL', 'Adar'),
295                /* I18N: a month in the Jewish calendar */
296                8  => I18N::translateContext('INSTRUMENTAL', 'Nissan'),
297                /* I18N: a month in the Jewish calendar */
298                9  => I18N::translateContext('INSTRUMENTAL', 'Iyar'),
299                /* I18N: a month in the Jewish calendar */
300                10 => I18N::translateContext('INSTRUMENTAL', 'Sivan'),
301                /* I18N: a month in the Jewish calendar */
302                11 => I18N::translateContext('INSTRUMENTAL', 'Tamuz'),
303                /* I18N: a month in the Jewish calendar */
304                12 => I18N::translateContext('INSTRUMENTAL', 'Av'),
305                /* I18N: a month in the Jewish calendar */
306                13 => I18N::translateContext('INSTRUMENTAL', 'Elul'),
307            ];
308        }
309
310        if ($month === 7 && $leap_year) {
311            /* I18N: a month in the Jewish calendar */
312            return I18N::translateContext('INSTRUMENTAL', 'Adar II');
313        }
314
315        return $translated_month_names[$month];
316    }
317
318    /**
319     * Abbreviated month name
320     *
321     * @param int  $month
322     * @param bool $leap_year Some calendars use leap months
323     *
324     * @return string
325     */
326    protected function monthNameAbbreviated(int $month, bool $leap_year): string
327    {
328        return $this->monthNameNominativeCase($month, $leap_year);
329    }
330
331    /**
332     * Which months follows this one? Calendars with leap-months should provide their own implementation.
333     *
334     * @return array<int>
335     */
336    protected function nextMonth(): array
337    {
338        if ($this->month === 6 && !$this->isLeapYear()) {
339            return [
340                $this->year,
341                8,
342            ];
343        }
344
345        return [
346            $this->year + ($this->month === 13 ? 1 : 0),
347            $this->month % 13 + 1,
348        ];
349    }
350}
351