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