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