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