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