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