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