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