xref: /webtrees/app/Date/JewishDate.php (revision f4c767fd89cdb62ee54edec032285924cd767af7)
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\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                -7 => I18N::translateContext('NOMINATIVE', 'Adar II'),
141                /* I18N: a month in the Jewish calendar */
142                8  => I18N::translateContext('NOMINATIVE', 'Nissan'),
143                /* I18N: a month in the Jewish calendar */
144                9  => I18N::translateContext('NOMINATIVE', 'Iyar'),
145                /* I18N: a month in the Jewish calendar */
146                10 => I18N::translateContext('NOMINATIVE', 'Sivan'),
147                /* I18N: a month in the Jewish calendar */
148                11 => I18N::translateContext('NOMINATIVE', 'Tamuz'),
149                /* I18N: a month in the Jewish calendar */
150                12 => I18N::translateContext('NOMINATIVE', 'Av'),
151                /* I18N: a month in the Jewish calendar */
152                13 => I18N::translateContext('NOMINATIVE', 'Elul'),
153            ];
154        }
155
156        if ($month === 7 && $leap_year) {
157            return $translated_month_names[-7];
158        }
159
160        return $translated_month_names[$month];
161    }
162
163    /**
164     * Full month name in genitive case.
165     *
166     * @param int  $month
167     * @param bool $leap_year Some calendars use leap months
168     *
169     * @return string
170     */
171    protected function monthNameGenitiveCase(int $month, bool $leap_year): string
172    {
173        static $translated_month_names;
174
175        if ($translated_month_names === null) {
176            $translated_month_names = [
177                0  => '',
178                /* I18N: a month in the Jewish calendar */
179                1  => I18N::translateContext('GENITIVE', 'Tishrei'),
180                /* I18N: a month in the Jewish calendar */
181                2  => I18N::translateContext('GENITIVE', 'Heshvan'),
182                /* I18N: a month in the Jewish calendar */
183                3  => I18N::translateContext('GENITIVE', 'Kislev'),
184                /* I18N: a month in the Jewish calendar */
185                4  => I18N::translateContext('GENITIVE', 'Tevet'),
186                /* I18N: a month in the Jewish calendar */
187                5  => I18N::translateContext('GENITIVE', 'Shevat'),
188                /* I18N: a month in the Jewish calendar */
189                6  => I18N::translateContext('GENITIVE', 'Adar I'),
190                /* I18N: a month in the Jewish calendar */
191                7  => I18N::translateContext('GENITIVE', 'Adar'),
192                /* I18N: a month in the Jewish calendar */
193                -7 => I18N::translateContext('GENITIVE', 'Adar II'),
194                /* I18N: a month in the Jewish calendar */
195                8  => I18N::translateContext('GENITIVE', 'Nissan'),
196                /* I18N: a month in the Jewish calendar */
197                9  => I18N::translateContext('GENITIVE', 'Iyar'),
198                /* I18N: a month in the Jewish calendar */
199                10 => I18N::translateContext('GENITIVE', 'Sivan'),
200                /* I18N: a month in the Jewish calendar */
201                11 => I18N::translateContext('GENITIVE', 'Tamuz'),
202                /* I18N: a month in the Jewish calendar */
203                12 => I18N::translateContext('GENITIVE', 'Av'),
204                /* I18N: a month in the Jewish calendar */
205                13 => I18N::translateContext('GENITIVE', 'Elul'),
206            ];
207        }
208
209        if ($month === 7 && $leap_year) {
210            return $translated_month_names[-7];
211        }
212
213        return $translated_month_names[$month];
214    }
215
216    /**
217     * Full month name in locative case.
218     *
219     * @param int  $month
220     * @param bool $leap_year Some calendars use leap months
221     *
222     * @return string
223     */
224    protected function monthNameLocativeCase(int $month, bool $leap_year): string
225    {
226        static $translated_month_names;
227
228        if ($translated_month_names === null) {
229            $translated_month_names = [
230                0  => '',
231                /* I18N: a month in the Jewish calendar */
232                1  => I18N::translateContext('LOCATIVE', 'Tishrei'),
233                /* I18N: a month in the Jewish calendar */
234                2  => I18N::translateContext('LOCATIVE', 'Heshvan'),
235                /* I18N: a month in the Jewish calendar */
236                3  => I18N::translateContext('LOCATIVE', 'Kislev'),
237                /* I18N: a month in the Jewish calendar */
238                4  => I18N::translateContext('LOCATIVE', 'Tevet'),
239                /* I18N: a month in the Jewish calendar */
240                5  => I18N::translateContext('LOCATIVE', 'Shevat'),
241                /* I18N: a month in the Jewish calendar */
242                6  => I18N::translateContext('LOCATIVE', 'Adar I'),
243                /* I18N: a month in the Jewish calendar */
244                7  => I18N::translateContext('LOCATIVE', 'Adar'),
245                /* I18N: a month in the Jewish calendar */
246                -7 => I18N::translateContext('LOCATIVE', 'Adar II'),
247                /* I18N: a month in the Jewish calendar */
248                8  => I18N::translateContext('LOCATIVE', 'Nissan'),
249                /* I18N: a month in the Jewish calendar */
250                9  => I18N::translateContext('LOCATIVE', 'Iyar'),
251                /* I18N: a month in the Jewish calendar */
252                10 => I18N::translateContext('LOCATIVE', 'Sivan'),
253                /* I18N: a month in the Jewish calendar */
254                11 => I18N::translateContext('LOCATIVE', 'Tamuz'),
255                /* I18N: a month in the Jewish calendar */
256                12 => I18N::translateContext('LOCATIVE', 'Av'),
257                /* I18N: a month in the Jewish calendar */
258                13 => I18N::translateContext('LOCATIVE', 'Elul'),
259            ];
260        }
261
262        if ($month === 7 && $leap_year) {
263            return $translated_month_names[-7];
264        }
265
266        return $translated_month_names[$month];
267    }
268
269    /**
270     * Full month name in instrumental case.
271     *
272     * @param int  $month
273     * @param bool $leap_year Some calendars use leap months
274     *
275     * @return string
276     */
277    protected function monthNameInstrumentalCase(int $month, bool $leap_year): string
278    {
279        static $translated_month_names;
280
281        if ($translated_month_names === null) {
282            $translated_month_names = [
283                0  => '',
284                /* I18N: a month in the Jewish calendar */
285                1  => I18N::translateContext('INSTRUMENTAL', 'Tishrei'),
286                /* I18N: a month in the Jewish calendar */
287                2  => I18N::translateContext('INSTRUMENTAL', 'Heshvan'),
288                /* I18N: a month in the Jewish calendar */
289                3  => I18N::translateContext('INSTRUMENTAL', 'Kislev'),
290                /* I18N: a month in the Jewish calendar */
291                4  => I18N::translateContext('INSTRUMENTAL', 'Tevet'),
292                /* I18N: a month in the Jewish calendar */
293                5  => I18N::translateContext('INSTRUMENTAL', 'Shevat'),
294                /* I18N: a month in the Jewish calendar */
295                6  => I18N::translateContext('INSTRUMENTAL', 'Adar I'),
296                /* I18N: a month in the Jewish calendar */
297                7  => I18N::translateContext('INSTRUMENTAL', 'Adar'),
298                /* I18N: a month in the Jewish calendar */
299                -7 => I18N::translateContext('INSTRUMENTAL', 'Adar II'),
300                /* I18N: a month in the Jewish calendar */
301                8  => I18N::translateContext('INSTRUMENTAL', 'Nissan'),
302                /* I18N: a month in the Jewish calendar */
303                9  => I18N::translateContext('INSTRUMENTAL', 'Iyar'),
304                /* I18N: a month in the Jewish calendar */
305                10 => I18N::translateContext('INSTRUMENTAL', 'Sivan'),
306                /* I18N: a month in the Jewish calendar */
307                11 => I18N::translateContext('INSTRUMENTAL', 'Tamuz'),
308                /* I18N: a month in the Jewish calendar */
309                12 => I18N::translateContext('INSTRUMENTAL', 'Av'),
310                /* I18N: a month in the Jewish calendar */
311                13 => I18N::translateContext('INSTRUMENTAL', 'Elul'),
312            ];
313        }
314
315        if ($month === 7 && $leap_year) {
316            return $translated_month_names[-7];
317        }
318
319        return $translated_month_names[$month];
320    }
321
322    /**
323     * Abbreviated month name
324     *
325     * @param int  $month
326     * @param bool $leap_year Some calendars use leap months
327     *
328     * @return string
329     */
330    protected function monthNameAbbreviated(int $month, bool $leap_year): string
331    {
332        return $this->monthNameNominativeCase($month, $leap_year);
333    }
334
335    /**
336     * Which months follows this one? Calendars with leap-months should provide their own implementation.
337     *
338     * @return int[]
339     */
340    protected function nextMonth(): array
341    {
342        if ($this->month === 6 && !$this->isLeapYear()) {
343            return [
344                $this->year,
345                8,
346            ];
347        }
348
349        return [
350            $this->year + ($this->month === 13 ? 1 : 0),
351            ($this->month % 13) + 1,
352        ];
353    }
354}
355