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\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 -7 => I18N::translateContext('NOMINATIVE', 'Adar II'), 141 /* I18N: a month in the Jewish calendar */ 142 8 => I18N::translateContext('NOMINATIVE', 'Nissan'), 143 /* I18N: a month in the Jewish calendar */ 144 9 => I18N::translateContext('NOMINATIVE', 'Iyar'), 145 /* I18N: a month in the Jewish calendar */ 146 10 => I18N::translateContext('NOMINATIVE', 'Sivan'), 147 /* I18N: a month in the Jewish calendar */ 148 11 => I18N::translateContext('NOMINATIVE', 'Tamuz'), 149 /* I18N: a month in the Jewish calendar */ 150 12 => I18N::translateContext('NOMINATIVE', 'Av'), 151 /* I18N: a month in the Jewish calendar */ 152 13 => I18N::translateContext('NOMINATIVE', 'Elul'), 153 ]; 154 } 155 156 if ($month === 7 && $leap_year) { 157 return $translated_month_names[-7]; 158 } 159 160 return $translated_month_names[$month]; 161 } 162 163 /** 164 * Full month name in genitive case. 165 * 166 * @param int $month 167 * @param bool $leap_year Some calendars use leap months 168 * 169 * @return string 170 */ 171 protected function monthNameGenitiveCase(int $month, bool $leap_year): string 172 { 173 static $translated_month_names; 174 175 if ($translated_month_names === null) { 176 $translated_month_names = [ 177 0 => '', 178 /* I18N: a month in the Jewish calendar */ 179 1 => I18N::translateContext('GENITIVE', 'Tishrei'), 180 /* I18N: a month in the Jewish calendar */ 181 2 => I18N::translateContext('GENITIVE', 'Heshvan'), 182 /* I18N: a month in the Jewish calendar */ 183 3 => I18N::translateContext('GENITIVE', 'Kislev'), 184 /* I18N: a month in the Jewish calendar */ 185 4 => I18N::translateContext('GENITIVE', 'Tevet'), 186 /* I18N: a month in the Jewish calendar */ 187 5 => I18N::translateContext('GENITIVE', 'Shevat'), 188 /* I18N: a month in the Jewish calendar */ 189 6 => I18N::translateContext('GENITIVE', 'Adar I'), 190 /* I18N: a month in the Jewish calendar */ 191 7 => I18N::translateContext('GENITIVE', 'Adar'), 192 /* I18N: a month in the Jewish calendar */ 193 -7 => I18N::translateContext('GENITIVE', 'Adar II'), 194 /* I18N: a month in the Jewish calendar */ 195 8 => I18N::translateContext('GENITIVE', 'Nissan'), 196 /* I18N: a month in the Jewish calendar */ 197 9 => I18N::translateContext('GENITIVE', 'Iyar'), 198 /* I18N: a month in the Jewish calendar */ 199 10 => I18N::translateContext('GENITIVE', 'Sivan'), 200 /* I18N: a month in the Jewish calendar */ 201 11 => I18N::translateContext('GENITIVE', 'Tamuz'), 202 /* I18N: a month in the Jewish calendar */ 203 12 => I18N::translateContext('GENITIVE', 'Av'), 204 /* I18N: a month in the Jewish calendar */ 205 13 => I18N::translateContext('GENITIVE', 'Elul'), 206 ]; 207 } 208 209 if ($month === 7 && $leap_year) { 210 return $translated_month_names[-7]; 211 } 212 213 return $translated_month_names[$month]; 214 } 215 216 /** 217 * Full month name in locative case. 218 * 219 * @param int $month 220 * @param bool $leap_year Some calendars use leap months 221 * 222 * @return string 223 */ 224 protected function monthNameLocativeCase(int $month, bool $leap_year): string 225 { 226 static $translated_month_names; 227 228 if ($translated_month_names === null) { 229 $translated_month_names = [ 230 0 => '', 231 /* I18N: a month in the Jewish calendar */ 232 1 => I18N::translateContext('LOCATIVE', 'Tishrei'), 233 /* I18N: a month in the Jewish calendar */ 234 2 => I18N::translateContext('LOCATIVE', 'Heshvan'), 235 /* I18N: a month in the Jewish calendar */ 236 3 => I18N::translateContext('LOCATIVE', 'Kislev'), 237 /* I18N: a month in the Jewish calendar */ 238 4 => I18N::translateContext('LOCATIVE', 'Tevet'), 239 /* I18N: a month in the Jewish calendar */ 240 5 => I18N::translateContext('LOCATIVE', 'Shevat'), 241 /* I18N: a month in the Jewish calendar */ 242 6 => I18N::translateContext('LOCATIVE', 'Adar I'), 243 /* I18N: a month in the Jewish calendar */ 244 7 => I18N::translateContext('LOCATIVE', 'Adar'), 245 /* I18N: a month in the Jewish calendar */ 246 -7 => I18N::translateContext('LOCATIVE', 'Adar II'), 247 /* I18N: a month in the Jewish calendar */ 248 8 => I18N::translateContext('LOCATIVE', 'Nissan'), 249 /* I18N: a month in the Jewish calendar */ 250 9 => I18N::translateContext('LOCATIVE', 'Iyar'), 251 /* I18N: a month in the Jewish calendar */ 252 10 => I18N::translateContext('LOCATIVE', 'Sivan'), 253 /* I18N: a month in the Jewish calendar */ 254 11 => I18N::translateContext('LOCATIVE', 'Tamuz'), 255 /* I18N: a month in the Jewish calendar */ 256 12 => I18N::translateContext('LOCATIVE', 'Av'), 257 /* I18N: a month in the Jewish calendar */ 258 13 => I18N::translateContext('LOCATIVE', 'Elul'), 259 ]; 260 } 261 262 if ($month === 7 && $leap_year) { 263 return $translated_month_names[-7]; 264 } 265 266 return $translated_month_names[$month]; 267 } 268 269 /** 270 * Full month name in instrumental case. 271 * 272 * @param int $month 273 * @param bool $leap_year Some calendars use leap months 274 * 275 * @return string 276 */ 277 protected function monthNameInstrumentalCase(int $month, bool $leap_year): string 278 { 279 static $translated_month_names; 280 281 if ($translated_month_names === null) { 282 $translated_month_names = [ 283 0 => '', 284 /* I18N: a month in the Jewish calendar */ 285 1 => I18N::translateContext('INSTRUMENTAL', 'Tishrei'), 286 /* I18N: a month in the Jewish calendar */ 287 2 => I18N::translateContext('INSTRUMENTAL', 'Heshvan'), 288 /* I18N: a month in the Jewish calendar */ 289 3 => I18N::translateContext('INSTRUMENTAL', 'Kislev'), 290 /* I18N: a month in the Jewish calendar */ 291 4 => I18N::translateContext('INSTRUMENTAL', 'Tevet'), 292 /* I18N: a month in the Jewish calendar */ 293 5 => I18N::translateContext('INSTRUMENTAL', 'Shevat'), 294 /* I18N: a month in the Jewish calendar */ 295 6 => I18N::translateContext('INSTRUMENTAL', 'Adar I'), 296 /* I18N: a month in the Jewish calendar */ 297 7 => I18N::translateContext('INSTRUMENTAL', 'Adar'), 298 /* I18N: a month in the Jewish calendar */ 299 -7 => I18N::translateContext('INSTRUMENTAL', 'Adar II'), 300 /* I18N: a month in the Jewish calendar */ 301 8 => I18N::translateContext('INSTRUMENTAL', 'Nissan'), 302 /* I18N: a month in the Jewish calendar */ 303 9 => I18N::translateContext('INSTRUMENTAL', 'Iyar'), 304 /* I18N: a month in the Jewish calendar */ 305 10 => I18N::translateContext('INSTRUMENTAL', 'Sivan'), 306 /* I18N: a month in the Jewish calendar */ 307 11 => I18N::translateContext('INSTRUMENTAL', 'Tamuz'), 308 /* I18N: a month in the Jewish calendar */ 309 12 => I18N::translateContext('INSTRUMENTAL', 'Av'), 310 /* I18N: a month in the Jewish calendar */ 311 13 => I18N::translateContext('INSTRUMENTAL', 'Elul'), 312 ]; 313 } 314 315 if ($month === 7 && $leap_year) { 316 return $translated_month_names[-7]; 317 } 318 319 return $translated_month_names[$month]; 320 } 321 322 /** 323 * Abbreviated month name 324 * 325 * @param int $month 326 * @param bool $leap_year Some calendars use leap months 327 * 328 * @return string 329 */ 330 protected function monthNameAbbreviated(int $month, bool $leap_year): string 331 { 332 return $this->monthNameNominativeCase($month, $leap_year); 333 } 334 335 /** 336 * Which months follows this one? Calendars with leap-months should provide their own implementation. 337 * 338 * @return int[] 339 */ 340 protected function nextMonth(): array 341 { 342 if ($this->month === 6 && !$this->isLeapYear()) { 343 return [ 344 $this->year, 345 8, 346 ]; 347 } 348 349 return [ 350 $this->year + ($this->month === 13 ? 1 : 0), 351 ($this->month % 13) + 1, 352 ]; 353 } 354} 355