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