diff options
| author | Greg Roach <fisharebest@webtrees.net> | 2018-10-09 09:34:23 +0100 |
|---|---|---|
| committer | Greg Roach <fisharebest@webtrees.net> | 2018-10-09 10:28:12 +0100 |
| commit | 4a83f5d742e43c37a641392fe50a3db201bffb30 (patch) | |
| tree | eafa95c5c20ef96de50ee707cfb483e6d0c28280 /app/Date | |
| parent | 60c2f26d25c230dabfa31502bfa6d713019e9ce5 (diff) | |
| download | webtrees-4a83f5d742e43c37a641392fe50a3db201bffb30.tar.gz webtrees-4a83f5d742e43c37a641392fe50a3db201bffb30.tar.bz2 webtrees-4a83f5d742e43c37a641392fe50a3db201bffb30.zip | |
Calendar dates are abstract
Diffstat (limited to 'app/Date')
| -rw-r--r-- | app/Date/AbstractCalendarDate.php (renamed from app/Date/CalendarDate.php) | 416 | ||||
| -rw-r--r-- | app/Date/AbstractGregorianJulianDate.php | 219 | ||||
| -rw-r--r-- | app/Date/FrenchDate.php | 13 | ||||
| -rw-r--r-- | app/Date/GregorianDate.php | 9 | ||||
| -rw-r--r-- | app/Date/HijriDate.php | 9 | ||||
| -rw-r--r-- | app/Date/JalaliDate.php | 9 | ||||
| -rw-r--r-- | app/Date/JewishDate.php | 21 | ||||
| -rw-r--r-- | app/Date/JulianDate.php | 26 | ||||
| -rw-r--r-- | app/Date/RomanDate.php | 9 |
9 files changed, 413 insertions, 318 deletions
diff --git a/app/Date/CalendarDate.php b/app/Date/AbstractCalendarDate.php index 5014023d96..a72db1b906 100644 --- a/app/Date/CalendarDate.php +++ b/app/Date/AbstractCalendarDate.php @@ -34,36 +34,25 @@ use Fisharebest\Webtrees\I18N; * midnight, solar midnight, sunset, sunrise, etc.), we convert on the basis of * midday. */ -class CalendarDate +class AbstractCalendarDate { - // Convert GEDCOM month names to month numbers - const MONTH_ABBREVIATIONS = [ - '' => 0, - 'JAN' => 1, - 'FEB' => 2, - 'MAR' => 3, - 'APR' => 4, - 'MAY' => 5, - 'JUN' => 6, - 'JUL' => 7, - 'AUG' => 8, - 'SEP' => 9, - 'OCT' => 10, - 'NOV' => 11, - 'DEC' => 12, - ]; + // GEDCOM calendar escape + const ESCAPE = '@#DUNKNOWN@'; + + // Convert GEDCOM month names to month numbers. + const MONTH_ABBREVIATIONS = []; /** @var CalendarInterface The calendar system used to represent this date */ protected $calendar; /** @var int Year number */ - public $y; + public $year; /** @var int Month number */ - public $m; + public $month; /** @var int Day number */ - public $d; + public $day; /** @var int Earliest Julian day number (start of month/year for imprecise dates) */ private $minimum_julian_day; @@ -77,7 +66,7 @@ class CalendarDate * day/month/year strings from a GEDCOM date * another CalendarDate object * - * @param array|int|CalendarDate $date + * @param array|int|AbstractCalendarDate $date */ protected function __construct($date) { @@ -85,25 +74,25 @@ class CalendarDate if (is_int($date)) { $this->minimum_julian_day = $date; $this->maximum_julian_day = $date; - list($this->y, $this->m, $this->d) = $this->calendar->jdToYmd($date); + list($this->year, $this->month, $this->day) = $this->calendar->jdToYmd($date); return; } // Construct from an array (of three gedcom-style strings: "1900", "FEB", "4") if (is_array($date)) { - $this->d = (int) $date[2]; + $this->day = (int) $date[2]; if (array_key_exists($date[1], static::MONTH_ABBREVIATIONS)) { - $this->m = static::MONTH_ABBREVIATIONS[$date[1]]; + $this->month = static::MONTH_ABBREVIATIONS[$date[1]]; } else { - $this->m = 0; - $this->d = 0; + $this->month = 0; + $this->day = 0; } - $this->y = $this->extractYear($date[0]); + $this->year = $this->extractYear($date[0]); // Our simple lookup table above does not take into account Adar and leap-years. - if ($this->m === 6 && $this->calendar instanceof JewishCalendar && !$this->calendar->isLeapYear($this->y)) { - $this->m = 7; + if ($this->month === 6 && $this->calendar instanceof JewishCalendar && !$this->calendar->isLeapYear($this->year)) { + $this->month = 7; } $this->setJdFromYmd(); @@ -117,269 +106,128 @@ class CalendarDate // Construct from an equivalent xxxxDate object if (get_class($this) == get_class($date)) { - $this->y = $date->y; - $this->m = $date->m; - $this->d = $date->d; + $this->year = $date->year; + $this->month = $date->month; + $this->day = $date->day; return; } // Not all dates can be converted if (!$this->inValidRange()) { - $this->y = 0; - $this->m = 0; - $this->d = 0; + $this->year = 0; + $this->month = 0; + $this->day = 0; return; } // ...else construct an inequivalent xxxxDate object - if ($date->y == 0) { + if ($date->year == 0) { // Incomplete date - convert on basis of anniversary in current year $today = $date->calendar->jdToYmd(unixtojd()); - $jd = $date->calendar->ymdToJd($today[0], $date->m, $date->d == 0 ? $today[2] : $date->d); + $jd = $date->calendar->ymdToJd($today[0], $date->month, $date->day == 0 ? $today[2] : $date->day); } else { // Complete date $jd = intdiv($date->maximum_julian_day + $date->minimum_julian_day, 2); } - list($this->y, $this->m, $this->d) = $this->calendar->jdToYmd($jd); + list($this->year, $this->month, $this->day) = $this->calendar->jdToYmd($jd); // New date has same precision as original date - if ($date->y == 0) { - $this->y = 0; + if ($date->year == 0) { + $this->year = 0; } - if ($date->m == 0) { - $this->m = 0; + if ($date->month == 0) { + $this->month = 0; } - if ($date->d == 0) { - $this->d = 0; + if ($date->day == 0) { + $this->day = 0; } $this->setJdFromYmd(); } /** - * @return int + * @return CalendarInterface */ - public function maximumJulianDay(): int + public function calendar(): CalendarInterface { - return $this->maximum_julian_day; + return $this->calendar(); } /** * @return int */ - public function minimumJulianDay(): int + public function maximumJulianDay(): int { - return $this->minimum_julian_day; + return $this->maximum_julian_day; } /** - * Is the current year a leap year? - * - * @return bool + * @return int */ - public function isLeapYear(): bool + public function year(): int { - return $this->calendar->isLeapYear($this->y); + return $this->year; } /** - * Set the object’s Julian day number from a potentially incomplete year/month/day - * - * @return void + * @return int */ - public function setJdFromYmd() + public function month(): int { - if ($this->y == 0) { - $this->minimum_julian_day = 0; - $this->maximum_julian_day = 0; - } elseif ($this->m == 0) { - $this->minimum_julian_day = $this->calendar->ymdToJd($this->y, 1, 1); - $this->maximum_julian_day = $this->calendar->ymdToJd($this->nextYear($this->y), 1, 1) - 1; - } elseif ($this->d == 0) { - list($ny, $nm) = $this->nextMonth(); - $this->minimum_julian_day = $this->calendar->ymdToJd($this->y, $this->m, 1); - $this->maximum_julian_day = $this->calendar->ymdToJd($ny, $nm, 1) - 1; - } else { - $this->minimum_julian_day = $this->calendar->ymdToJd($this->y, $this->m, $this->d); - $this->maximum_julian_day = $this->minimum_julian_day; - } + return $this->month; } /** - * Full month name in nominative case. - * - * We put these in the base class, to save duplicating it in the Julian and Gregorian calendars. - * - * @param int $month_number - * @param bool $leap_year Some calendars use leap months - * - * @return string + * @return int */ - protected function monthNameNominativeCase(int $month_number, bool $leap_year): string + public function day(): int { - static $translated_month_names; - - if ($translated_month_names === null) { - $translated_month_names = [ - 0 => '', - 1 => I18N::translateContext('NOMINATIVE', 'January'), - 2 => I18N::translateContext('NOMINATIVE', 'February'), - 3 => I18N::translateContext('NOMINATIVE', 'March'), - 4 => I18N::translateContext('NOMINATIVE', 'April'), - 5 => I18N::translateContext('NOMINATIVE', 'May'), - 6 => I18N::translateContext('NOMINATIVE', 'June'), - 7 => I18N::translateContext('NOMINATIVE', 'July'), - 8 => I18N::translateContext('NOMINATIVE', 'August'), - 9 => I18N::translateContext('NOMINATIVE', 'September'), - 10 => I18N::translateContext('NOMINATIVE', 'October'), - 11 => I18N::translateContext('NOMINATIVE', 'November'), - 12 => I18N::translateContext('NOMINATIVE', 'December'), - ]; - } - - return $translated_month_names[$month_number]; + return $this->day; } /** - * Full month name in genitive case. - * - * We put these in the base class, to save duplicating it in the Julian and Gregorian calendars. - * - * @param int $month_number - * @param bool $leap_year Some calendars use leap months - * - * @return string - */ - protected function monthNameGenitiveCase(int $month_number, bool $leap_year): string - { - static $translated_month_names; - - if ($translated_month_names === null) { - $translated_month_names = [ - 0 => '', - 1 => I18N::translateContext('GENITIVE', 'January'), - 2 => I18N::translateContext('GENITIVE', 'February'), - 3 => I18N::translateContext('GENITIVE', 'March'), - 4 => I18N::translateContext('GENITIVE', 'April'), - 5 => I18N::translateContext('GENITIVE', 'May'), - 6 => I18N::translateContext('GENITIVE', 'June'), - 7 => I18N::translateContext('GENITIVE', 'July'), - 8 => I18N::translateContext('GENITIVE', 'August'), - 9 => I18N::translateContext('GENITIVE', 'September'), - 10 => I18N::translateContext('GENITIVE', 'October'), - 11 => I18N::translateContext('GENITIVE', 'November'), - 12 => I18N::translateContext('GENITIVE', 'December'), - ]; - } - - return $translated_month_names[$month_number]; - } - - /** - * Full month name in locative case. - * - * We put these in the base class, to save duplicating it in the Julian and Gregorian calendars. - * - * @param int $month_number - * @param bool $leap_year Some calendars use leap months - * - * @return string + * @return int */ - protected function monthNameLocativeCase(int $month_number, bool $leap_year): string + public function minimumJulianDay(): int { - static $translated_month_names; - - if ($translated_month_names === null) { - $translated_month_names = [ - 0 => '', - 1 => I18N::translateContext('LOCATIVE', 'January'), - 2 => I18N::translateContext('LOCATIVE', 'February'), - 3 => I18N::translateContext('LOCATIVE', 'March'), - 4 => I18N::translateContext('LOCATIVE', 'April'), - 5 => I18N::translateContext('LOCATIVE', 'May'), - 6 => I18N::translateContext('LOCATIVE', 'June'), - 7 => I18N::translateContext('LOCATIVE', 'July'), - 8 => I18N::translateContext('LOCATIVE', 'August'), - 9 => I18N::translateContext('LOCATIVE', 'September'), - 10 => I18N::translateContext('LOCATIVE', 'October'), - 11 => I18N::translateContext('LOCATIVE', 'November'), - 12 => I18N::translateContext('LOCATIVE', 'December'), - ]; - } - - return $translated_month_names[$month_number]; + return $this->minimum_julian_day; } /** - * Full month name in instrumental case. - * - * We put these in the base class, to save duplicating it in the Julian and Gregorian calendars. - * - * @param int $month_number - * @param bool $leap_year Some calendars use leap months + * Is the current year a leap year? * - * @return string + * @return bool */ - protected function monthNameInstrumentalCase(int $month_number, bool $leap_year): string + public function isLeapYear(): bool { - static $translated_month_names; - - if ($translated_month_names === null) { - $translated_month_names = [ - 0 => '', - 1 => I18N::translateContext('INSTRUMENTAL', 'January'), - 2 => I18N::translateContext('INSTRUMENTAL', 'February'), - 3 => I18N::translateContext('INSTRUMENTAL', 'March'), - 4 => I18N::translateContext('INSTRUMENTAL', 'April'), - 5 => I18N::translateContext('INSTRUMENTAL', 'May'), - 6 => I18N::translateContext('INSTRUMENTAL', 'June'), - 7 => I18N::translateContext('INSTRUMENTAL', 'July'), - 8 => I18N::translateContext('INSTRUMENTAL', 'August'), - 9 => I18N::translateContext('INSTRUMENTAL', 'September'), - 10 => I18N::translateContext('INSTRUMENTAL', 'October'), - 11 => I18N::translateContext('INSTRUMENTAL', 'November'), - 12 => I18N::translateContext('INSTRUMENTAL', 'December'), - ]; - } - - return $translated_month_names[$month_number]; + return $this->calendar->isLeapYear($this->year); } /** - * Abbreviated month name - * - * @param int $month_number - * @param bool $leap_year Some calendars use leap months + * Set the object’s Julian day number from a potentially incomplete year/month/day * - * @return string + * @return void */ - protected function monthNameAbbreviated(int $month_number, bool $leap_year): string + public function setJdFromYmd() { - static $translated_month_names; - - if ($translated_month_names === null) { - $translated_month_names = [ - 0 => '', - 1 => I18N::translateContext('Abbreviation for January', 'Jan'), - 2 => I18N::translateContext('Abbreviation for February', 'Feb'), - 3 => I18N::translateContext('Abbreviation for March', 'Mar'), - 4 => I18N::translateContext('Abbreviation for April', 'Apr'), - 5 => I18N::translateContext('Abbreviation for May', 'May'), - 6 => I18N::translateContext('Abbreviation for June', 'Jun'), - 7 => I18N::translateContext('Abbreviation for July', 'Jul'), - 8 => I18N::translateContext('Abbreviation for August', 'Aug'), - 9 => I18N::translateContext('Abbreviation for September', 'Sep'), - 10 => I18N::translateContext('Abbreviation for October', 'Oct'), - 11 => I18N::translateContext('Abbreviation for November', 'Nov'), - 12 => I18N::translateContext('Abbreviation for December', 'Dec'), - ]; + if ($this->year == 0) { + $this->minimum_julian_day = 0; + $this->maximum_julian_day = 0; + } elseif ($this->month == 0) { + $this->minimum_julian_day = $this->calendar->ymdToJd($this->year, 1, 1); + $this->maximum_julian_day = $this->calendar->ymdToJd($this->nextYear($this->year), 1, 1) - 1; + } elseif ($this->day == 0) { + list($ny, $nm) = $this->nextMonth(); + $this->minimum_julian_day = $this->calendar->ymdToJd($this->year, $this->month, 1); + $this->maximum_julian_day = $this->calendar->ymdToJd($ny, $nm, 1) - 1; + } else { + $this->minimum_julian_day = $this->calendar->ymdToJd($this->year, $this->month, $this->day); + $this->maximum_julian_day = $this->minimum_julian_day; } - - return $translated_month_names[$month_number]; } /** - * Full day of th eweek + * Full day of the week * * @param int $day_number * @@ -464,12 +312,12 @@ class CalendarDate /** * Compare two dates, for sorting * - * @param CalendarDate $d1 - * @param CalendarDate $d2 + * @param AbstractCalendarDate $d1 + * @param AbstractCalendarDate $d2 * * @return int */ - public static function compare(CalendarDate $d1, CalendarDate $d2): int + public static function compare(AbstractCalendarDate $d1, AbstractCalendarDate $d2): int { if ($d1->maximum_julian_day < $d2->minimum_julian_day) { return -1; @@ -484,19 +332,18 @@ class CalendarDate /** * Calculate the years/months/days between this date and another date. - * * Results assume you add the days first, then the months. * 4 February -> 3 July is 27 days (3 March) and 4 months. * It is not 4 months (4 June) and 29 days. * - * @param CalendarDate $date + * @param AbstractCalendarDate $date * * @return int[] Age in years/months/days */ - public function ageDifference(CalendarDate $date): array + public function ageDifference(AbstractCalendarDate $date): array { // Incomplete dates - if ($this->y === 0 || $date->y === 0) { + if ($this->year === 0 || $date->year === 0) { return [-1, -1, -1]; } @@ -536,7 +383,7 @@ class CalendarDate */ public function getAge(int $jd): int { - if ($this->y == 0 || $jd == 0) { + if ($this->year == 0 || $jd == 0) { return 0; } if ($this->minimum_julian_day < $jd && $this->maximum_julian_day > $jd) { @@ -546,14 +393,13 @@ class CalendarDate return 0; } list($y, $m, $d) = $this->calendar->jdToYmd($jd); - $dy = $y - $this->y; - $dm = $m - max($this->m, 1); - $dd = $d - max($this->d, 1); + $dy = $y - $this->year; + $dm = $m - max($this->month, 1); + $dd = $d - max($this->day, 1); if ($dd < 0) { $dm--; } if ($dm < 0) { - $dm += $this->calendar->monthsInYear(); $dy--; } @@ -571,7 +417,7 @@ class CalendarDate */ public function getAgeFull(int $jd): string { - if ($this->y == 0 || $jd == 0) { + if ($this->year == 0 || $jd == 0) { return ''; } if ($this->minimum_julian_day < $jd && $this->maximum_julian_day > $jd) { @@ -584,9 +430,9 @@ class CalendarDate return '<i class="icon-warning"></i>'; } list($y, $m, $d) = $this->calendar->jdToYmd($jd); - $dy = $y - $this->y; - $dm = $m - max($this->m, 1); - $dd = $d - max($this->d, 1); + $dy = $y - $this->year; + $dm = $m - max($this->month, 1); + $dd = $d - max($this->day, 1); if ($dd < 0) { $dm--; } @@ -613,9 +459,9 @@ class CalendarDate * * @param string $calendar * - * @return CalendarDate + * @return AbstractCalendarDate */ - public function convertToCalendar(string $calendar): CalendarDate + public function convertToCalendar(string $calendar): AbstractCalendarDate { switch ($calendar) { case 'gregorian': @@ -663,7 +509,7 @@ class CalendarDate public function daysInMonth(): int { try { - return $this->calendar->daysInMonth($this->y, $this->m); + return $this->calendar->daysInMonth($this->year, $this->month); } catch (\InvalidArgumentException $ex) { DebugBar::addThrowable($ex); @@ -694,11 +540,11 @@ class CalendarDate public function format(string $format, string $qualifier = ''): string { // Don’t show exact details for inexact dates - if (!$this->d) { + if (!$this->day) { // The comma is for US "M D, Y" dates $format = preg_replace('/%[djlDNSwz][,]?/', '', $format); } - if (!$this->m) { + if (!$this->month) { $format = str_replace([ '%F', '%m', @@ -707,7 +553,7 @@ class CalendarDate '%t', ], '', $format); } - if (!$this->y) { + if (!$this->year) { $format = str_replace([ '%t', '%L', @@ -717,10 +563,10 @@ class CalendarDate ], '', $format); } // If we’ve trimmed the format, also trim the punctuation - if (!$this->d || !$this->m || !$this->y) { + if (!$this->day || !$this->month || !$this->year) { $format = trim($format, ',. ;/-'); } - if ($this->d && preg_match('/%[djlDNSwz]/', $format)) { + if ($this->day && preg_match('/%[djlDNSwz]/', $format)) { // If we have a day-number *and* we are being asked to display it, then genitive $case = 'GENITIVE'; } else { @@ -798,7 +644,7 @@ class CalendarDate break; // These 4 extensions are useful for re-formatting gedcom dates. case '%@': - $format = str_replace($match, $this->calendar->gedcomCalendarEscape(), $format); + $format = str_replace($match, $this->formatGedcomCalendarEscape(), $format); break; case '%A': $format = str_replace($match, $this->formatGedcomDay(), $format); @@ -822,11 +668,11 @@ class CalendarDate */ protected function formatDayZeros(): string { - if ($this->d > 9) { - return I18N::digits($this->d); + if ($this->day > 9) { + return I18N::digits($this->day); } - return I18N::digits('0' . $this->d); + return I18N::digits('0' . $this->day); } /** @@ -836,7 +682,7 @@ class CalendarDate */ protected function formatDay(): string { - return I18N::digits($this->d); + return I18N::digits($this->day); } /** @@ -886,7 +732,7 @@ class CalendarDate */ protected function formatDayOfYear(): string { - return I18N::digits($this->minimum_julian_day - $this->calendar->ymdToJd($this->y, 1, 1)); + return I18N::digits($this->minimum_julian_day - $this->calendar->ymdToJd($this->year, 1, 1)); } /** @@ -896,7 +742,7 @@ class CalendarDate */ protected function formatMonth(): string { - return I18N::digits($this->m); + return I18N::digits($this->month); } /** @@ -906,11 +752,11 @@ class CalendarDate */ protected function formatMonthZeros(): string { - if ($this->m > 9) { - return I18N::digits($this->m); + if ($this->month > 9) { + return I18N::digits($this->month); } - return I18N::digits('0' . $this->m); + return I18N::digits('0' . $this->month); } /** @@ -924,13 +770,13 @@ class CalendarDate { switch ($case) { case 'GENITIVE': - return $this->monthNameGenitiveCase($this->m, $this->isLeapYear()); + return $this->monthNameGenitiveCase($this->month, $this->isLeapYear()); case 'NOMINATIVE': - return $this->monthNameNominativeCase($this->m, $this->isLeapYear()); + return $this->monthNameNominativeCase($this->month, $this->isLeapYear()); case 'LOCATIVE': - return $this->monthNameLocativeCase($this->m, $this->isLeapYear()); + return $this->monthNameLocativeCase($this->month, $this->isLeapYear()); case 'INSTRUMENTAL': - return $this->monthNameInstrumentalCase($this->m, $this->isLeapYear()); + return $this->monthNameInstrumentalCase($this->month, $this->isLeapYear()); default: throw new \InvalidArgumentException($case); } @@ -943,7 +789,7 @@ class CalendarDate */ protected function formatShortMonth(): string { - return $this->monthNameAbbreviated($this->m, $this->isLeapYear()); + return $this->monthNameAbbreviated($this->month, $this->isLeapYear()); } /** @@ -965,11 +811,11 @@ class CalendarDate */ protected function formatGedcomDay(): string { - if ($this->d == 0) { + if ($this->day == 0) { return ''; } - return sprintf('%02d', $this->d); + return sprintf('%02d', $this->day); } /** @@ -980,11 +826,11 @@ class CalendarDate protected function formatGedcomMonth(): string { // Our simple lookup table doesn't work correctly for Adar on leap years - if ($this->m == 7 && $this->calendar instanceof JewishCalendar && !$this->calendar->isLeapYear($this->y)) { + if ($this->month == 7 && $this->calendar instanceof JewishCalendar && !$this->calendar->isLeapYear($this->year)) { return 'ADR'; } - return array_search($this->m, static::MONTH_ABBREVIATIONS); + return array_search($this->month, static::MONTH_ABBREVIATIONS); } /** @@ -994,11 +840,21 @@ class CalendarDate */ protected function formatGedcomYear(): string { - if ($this->y == 0) { + if ($this->year == 0) { return ''; } - return sprintf('%04d', $this->y); + return sprintf('%04d', $this->year); + } + + /** + * Generate the %@ format for a calendar escape. + * + * @return string + */ + protected function formatGedcomCalendarEscape(): string + { + return static::ESCAPE; } /** @@ -1008,7 +864,7 @@ class CalendarDate */ protected function formatLongYear(): string { - return I18N::digits($this->y); + return I18N::digits($this->year); } /** @@ -1019,8 +875,8 @@ class CalendarDate protected function nextMonth(): array { return [ - $this->m === $this->calendar->monthsInYear() ? $this->nextYear($this->y) : $this->y, - ($this->m % $this->calendar->monthsInYear()) + 1, + $this->month === $this->calendar->monthsInYear() ? $this->nextYear($this->year) : $this->year, + ($this->month % $this->calendar->monthsInYear()) + 1, ]; } @@ -1037,15 +893,15 @@ class CalendarDate /** * Convert to today’s date. * - * @return CalendarDate + * @return AbstractCalendarDate */ - public function today(): CalendarDate + public function today(): AbstractCalendarDate { - $tmp = clone $this; - $ymd = $tmp->todayYmd(); - $tmp->y = $ymd[0]; - $tmp->m = $ymd[1]; - $tmp->d = $ymd[2]; + $tmp = clone $this; + $ymd = $tmp->todayYmd(); + $tmp->year = $ymd[0]; + $tmp->month = $ymd[1]; + $tmp->day = $ymd[2]; $tmp->setJdFromYmd(); return $tmp; @@ -1060,10 +916,10 @@ class CalendarDate */ public function calendarUrl(string $date_format): string { - if (strpbrk($date_format, 'dDj') && $this->d) { + if (strpbrk($date_format, 'dDj') && $this->day) { // If the format includes a day, and the date also includes a day, then use the day view $view = 'day'; - } elseif (strpbrk($date_format, 'FMmn') && $this->m) { + } elseif (strpbrk($date_format, 'FMmn') && $this->month) { // If the format includes a month, and the date also includes a month, then use the month view $view = 'month'; } else { diff --git a/app/Date/AbstractGregorianJulianDate.php b/app/Date/AbstractGregorianJulianDate.php new file mode 100644 index 0000000000..efcf7244aa --- /dev/null +++ b/app/Date/AbstractGregorianJulianDate.php @@ -0,0 +1,219 @@ +<?php +/** + * webtrees: online genealogy + * Copyright (C) 2018 webtrees development team + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +declare(strict_types=1); + +namespace Fisharebest\Webtrees\Date; + +use Fisharebest\ExtCalendar\CalendarInterface; +use Fisharebest\ExtCalendar\JewishCalendar; +use Fisharebest\Webtrees\DebugBar; +use Fisharebest\Webtrees\I18N; + +/** + * Common definitions for Gregorian and Julian dates. + */ +abstract class AbstractGregorianJulianDate extends AbstractCalendarDate +{ + // Convert GEDCOM month names to month numbers + const MONTH_ABBREVIATIONS = [ + '' => 0, + 'JAN' => 1, + 'FEB' => 2, + 'MAR' => 3, + 'APR' => 4, + 'MAY' => 5, + 'JUN' => 6, + 'JUL' => 7, + 'AUG' => 8, + 'SEP' => 9, + 'OCT' => 10, + 'NOV' => 11, + 'DEC' => 12, + ]; + + /** + * Full month name in nominative case. + * + * We put these in the base class, to save duplicating it in the Julian and Gregorian calendars. + * + * @param int $month_number + * @param bool $leap_year Some calendars use leap months + * + * @return string + */ + protected function monthNameNominativeCase(int $month_number, bool $leap_year): string + { + static $translated_month_names; + + if ($translated_month_names === null) { + $translated_month_names = [ + 0 => '', + 1 => I18N::translateContext('NOMINATIVE', 'January'), + 2 => I18N::translateContext('NOMINATIVE', 'February'), + 3 => I18N::translateContext('NOMINATIVE', 'March'), + 4 => I18N::translateContext('NOMINATIVE', 'April'), + 5 => I18N::translateContext('NOMINATIVE', 'May'), + 6 => I18N::translateContext('NOMINATIVE', 'June'), + 7 => I18N::translateContext('NOMINATIVE', 'July'), + 8 => I18N::translateContext('NOMINATIVE', 'August'), + 9 => I18N::translateContext('NOMINATIVE', 'September'), + 10 => I18N::translateContext('NOMINATIVE', 'October'), + 11 => I18N::translateContext('NOMINATIVE', 'November'), + 12 => I18N::translateContext('NOMINATIVE', 'December'), + ]; + } + + return $translated_month_names[$month_number]; + } + + /** + * Full month name in genitive case. + * + * We put these in the base class, to save duplicating it in the Julian and Gregorian calendars. + * + * @param int $month_number + * @param bool $leap_year Some calendars use leap months + * + * @return string + */ + protected function monthNameGenitiveCase(int $month_number, bool $leap_year): string + { + static $translated_month_names; + + if ($translated_month_names === null) { + $translated_month_names = [ + 0 => '', + 1 => I18N::translateContext('GENITIVE', 'January'), + 2 => I18N::translateContext('GENITIVE', 'February'), + 3 => I18N::translateContext('GENITIVE', 'March'), + 4 => I18N::translateContext('GENITIVE', 'April'), + 5 => I18N::translateContext('GENITIVE', 'May'), + 6 => I18N::translateContext('GENITIVE', 'June'), + 7 => I18N::translateContext('GENITIVE', 'July'), + 8 => I18N::translateContext('GENITIVE', 'August'), + 9 => I18N::translateContext('GENITIVE', 'September'), + 10 => I18N::translateContext('GENITIVE', 'October'), + 11 => I18N::translateContext('GENITIVE', 'November'), + 12 => I18N::translateContext('GENITIVE', 'December'), + ]; + } + + return $translated_month_names[$month_number]; + } + + /** + * Full month name in locative case. + * + * We put these in the base class, to save duplicating it in the Julian and Gregorian calendars. + * + * @param int $month_number + * @param bool $leap_year Some calendars use leap months + * + * @return string + */ + protected function monthNameLocativeCase(int $month_number, bool $leap_year): string + { + static $translated_month_names; + + if ($translated_month_names === null) { + $translated_month_names = [ + 0 => '', + 1 => I18N::translateContext('LOCATIVE', 'January'), + 2 => I18N::translateContext('LOCATIVE', 'February'), + 3 => I18N::translateContext('LOCATIVE', 'March'), + 4 => I18N::translateContext('LOCATIVE', 'April'), + 5 => I18N::translateContext('LOCATIVE', 'May'), + 6 => I18N::translateContext('LOCATIVE', 'June'), + 7 => I18N::translateContext('LOCATIVE', 'July'), + 8 => I18N::translateContext('LOCATIVE', 'August'), + 9 => I18N::translateContext('LOCATIVE', 'September'), + 10 => I18N::translateContext('LOCATIVE', 'October'), + 11 => I18N::translateContext('LOCATIVE', 'November'), + 12 => I18N::translateContext('LOCATIVE', 'December'), + ]; + } + + return $translated_month_names[$month_number]; + } + + /** + * Full month name in instrumental case. + * + * We put these in the base class, to save duplicating it in the Julian and Gregorian calendars. + * + * @param int $month_number + * @param bool $leap_year Some calendars use leap months + * + * @return string + */ + protected function monthNameInstrumentalCase(int $month_number, bool $leap_year): string + { + static $translated_month_names; + + if ($translated_month_names === null) { + $translated_month_names = [ + 0 => '', + 1 => I18N::translateContext('INSTRUMENTAL', 'January'), + 2 => I18N::translateContext('INSTRUMENTAL', 'February'), + 3 => I18N::translateContext('INSTRUMENTAL', 'March'), + 4 => I18N::translateContext('INSTRUMENTAL', 'April'), + 5 => I18N::translateContext('INSTRUMENTAL', 'May'), + 6 => I18N::translateContext('INSTRUMENTAL', 'June'), + 7 => I18N::translateContext('INSTRUMENTAL', 'July'), + 8 => I18N::translateContext('INSTRUMENTAL', 'August'), + 9 => I18N::translateContext('INSTRUMENTAL', 'September'), + 10 => I18N::translateContext('INSTRUMENTAL', 'October'), + 11 => I18N::translateContext('INSTRUMENTAL', 'November'), + 12 => I18N::translateContext('INSTRUMENTAL', 'December'), + ]; + } + + return $translated_month_names[$month_number]; + } + + /** + * Abbreviated month name + * + * @param int $month_number + * @param bool $leap_year Some calendars use leap months + * + * @return string + */ + protected function monthNameAbbreviated(int $month_number, bool $leap_year): string + { + static $translated_month_names; + + if ($translated_month_names === null) { + $translated_month_names = [ + 0 => '', + 1 => I18N::translateContext('Abbreviation for January', 'Jan'), + 2 => I18N::translateContext('Abbreviation for February', 'Feb'), + 3 => I18N::translateContext('Abbreviation for March', 'Mar'), + 4 => I18N::translateContext('Abbreviation for April', 'Apr'), + 5 => I18N::translateContext('Abbreviation for May', 'May'), + 6 => I18N::translateContext('Abbreviation for June', 'Jun'), + 7 => I18N::translateContext('Abbreviation for July', 'Jul'), + 8 => I18N::translateContext('Abbreviation for August', 'Aug'), + 9 => I18N::translateContext('Abbreviation for September', 'Sep'), + 10 => I18N::translateContext('Abbreviation for October', 'Oct'), + 11 => I18N::translateContext('Abbreviation for November', 'Nov'), + 12 => I18N::translateContext('Abbreviation for December', 'Dec'), + ]; + } + + return $translated_month_names[$month_number]; + } +} diff --git a/app/Date/FrenchDate.php b/app/Date/FrenchDate.php index fc668d8704..c5caae5193 100644 --- a/app/Date/FrenchDate.php +++ b/app/Date/FrenchDate.php @@ -22,10 +22,13 @@ use Fisharebest\Webtrees\I18N; use Fisharebest\Webtrees\Services\RomanNumeralsService; /** - * Definitions for the French Republican calendar + * Definitions for French Republican dates. */ -class FrenchDate extends CalendarDate +class FrenchDate extends AbstractCalendarDate { + // GEDCOM calendar escape + const ESCAPE = '@#DFRENCH R@'; + // Convert GEDCOM month names to month numbers const MONTH_ABBREVIATIONS = [ '' => 0, @@ -53,7 +56,7 @@ class FrenchDate extends CalendarDate * day/month/year strings from a GEDCOM date * another CalendarDate object * - * @param array|int|CalendarDate $date + * @param array|int|AbstractCalendarDate $date */ public function __construct($date) { @@ -266,7 +269,7 @@ class FrenchDate extends CalendarDate } /** - * Full day of th eweek + * Full day of the week * * @param int $day_number * @@ -323,6 +326,6 @@ class FrenchDate extends CalendarDate */ protected function formatLongYear(): string { - return $this->roman_numerals_service->numberToRomanNumerals($this->y); + return $this->roman_numerals_service->numberToRomanNumerals($this->year); } } diff --git a/app/Date/GregorianDate.php b/app/Date/GregorianDate.php index ca405608b4..9a8d1fa053 100644 --- a/app/Date/GregorianDate.php +++ b/app/Date/GregorianDate.php @@ -20,17 +20,20 @@ namespace Fisharebest\Webtrees\Date; use Fisharebest\ExtCalendar\GregorianCalendar; /** - * Definitions for the Gregorian calendar + * Definitions for Gregorian dates. */ -class GregorianDate extends CalendarDate +class GregorianDate extends AbstractGregorianJulianDate { + // GEDCOM calendar escape + const ESCAPE = '@#DGREGORIAN@'; + /** * Create a date from either: * a Julian day number * day/month/year strings from a GEDCOM date * another CalendarDate object * - * @param array|int|CalendarDate $date + * @param array|int|AbstractCalendarDate $date */ public function __construct($date) { diff --git a/app/Date/HijriDate.php b/app/Date/HijriDate.php index b228b55aac..20088e169d 100644 --- a/app/Date/HijriDate.php +++ b/app/Date/HijriDate.php @@ -21,13 +21,16 @@ use Fisharebest\ExtCalendar\ArabicCalendar; use Fisharebest\Webtrees\I18N; /** - * Definitions for the Hijri calendar. + * Definitions for Hijri dates. * * Note that these are "theoretical" dates. * "True" dates are based on local lunar observations, and can be a +/- one day. */ -class HijriDate extends CalendarDate +class HijriDate extends AbstractCalendarDate { + // GEDCOM calendar escape + const ESCAPE = '@#DHIJRI@'; + // Convert GEDCOM month names to month numbers const MONTH_ABBREVIATIONS = [ '' => 0, @@ -51,7 +54,7 @@ class HijriDate extends CalendarDate * day/month/year strings from a GEDCOM date * another CalendarDate object * - * @param array|int|CalendarDate $date + * @param array|int|AbstractCalendarDate $date */ public function __construct($date) { diff --git a/app/Date/JalaliDate.php b/app/Date/JalaliDate.php index d482e59cc9..187218800b 100644 --- a/app/Date/JalaliDate.php +++ b/app/Date/JalaliDate.php @@ -21,10 +21,13 @@ use Fisharebest\ExtCalendar\PersianCalendar; use Fisharebest\Webtrees\I18N; /** - * Definitions for the Jalali calendar + * Definitions for Jalali dates. */ -class JalaliDate extends CalendarDate +class JalaliDate extends AbstractCalendarDate { + // GEDCOM calendar escape + const ESCAPE = '@#DJALALI@'; + // Convert GEDCOM month names to month numbers const MONTH_ABBREVIATIONS = [ '' => 0, @@ -48,7 +51,7 @@ class JalaliDate extends CalendarDate * day/month/year strings from a GEDCOM date * another CalendarDate object * - * @param array|int|CalendarDate $date + * @param array|int|AbstractCalendarDate $date */ public function __construct($date) { diff --git a/app/Date/JewishDate.php b/app/Date/JewishDate.php index 478154f8a2..8a44efbdf8 100644 --- a/app/Date/JewishDate.php +++ b/app/Date/JewishDate.php @@ -23,8 +23,11 @@ use Fisharebest\Webtrees\I18N; /** * Definitions for the Jewish calendar */ -class JewishDate extends CalendarDate +class JewishDate extends AbstractCalendarDate { + // GEDCOM calendar escape + const ESCAPE = '@#DHEBREW@'; + // Convert GEDCOM month names to month numbers const MONTH_ABBREVIATIONS = [ '' => 0, @@ -49,7 +52,7 @@ class JewishDate extends CalendarDate * day/month/year strings from a GEDCOM date * another CalendarDate object * - * @param array|int|CalendarDate $date + * @param array|int|AbstractCalendarDate $date */ public function __construct($date) { @@ -65,7 +68,7 @@ class JewishDate extends CalendarDate protected function formatDay(): string { if (WT_LOCALE === 'he' || WT_LOCALE === 'yi') { - return (new JewishCalendar())->numberToHebrewNumerals($this->d, true); + return (new JewishCalendar())->numberToHebrewNumerals($this->day, true); } return parent::formatDay(); @@ -82,7 +85,7 @@ class JewishDate extends CalendarDate protected function formatShortYear(): string { if (WT_LOCALE === 'he' || WT_LOCALE === 'yi') { - return (new JewishCalendar())->numberToHebrewNumerals($this->y, false); + return (new JewishCalendar())->numberToHebrewNumerals($this->year, false); } return parent::formatLongYear(); @@ -96,7 +99,7 @@ class JewishDate extends CalendarDate protected function formatLongYear(): string { if (WT_LOCALE === 'he' || WT_LOCALE === 'yi') { - return (new JewishCalendar())->numberToHebrewNumerals($this->y, true); + return (new JewishCalendar())->numberToHebrewNumerals($this->year, true); } return parent::formatLongYear(); @@ -334,16 +337,16 @@ class JewishDate extends CalendarDate */ protected function nextMonth(): array { - if ($this->m == 6 && !$this->isLeapYear()) { + if ($this->month == 6 && !$this->isLeapYear()) { return [ - $this->y, + $this->year, 8, ]; } return [ - $this->y + ($this->m == 13 ? 1 : 0), - ($this->m % 13) + 1, + $this->year + ($this->month == 13 ? 1 : 0), + ($this->month % 13) + 1, ]; } } diff --git a/app/Date/JulianDate.php b/app/Date/JulianDate.php index 91d1eeb752..9d49a98baa 100644 --- a/app/Date/JulianDate.php +++ b/app/Date/JulianDate.php @@ -21,11 +21,13 @@ use Fisharebest\ExtCalendar\JulianCalendar; use Fisharebest\Webtrees\I18N; /** - * Definitions for the Julian Proleptic calendar - * (Proleptic means we extend it backwards, prior to its introduction in 46BC) + * Definitions for proleptic Julian dates. */ -class JulianDate extends CalendarDate +class JulianDate extends AbstractGregorianJulianDate { + // GEDCOM calendar escape + const ESCAPE = '@#DJULIAN@'; + /** @var bool True for dates recorded in new-style/old-style format, e.g. 2 FEB 1743/44 */ private $new_old_style = false; @@ -35,7 +37,7 @@ class JulianDate extends CalendarDate * day/month/year strings from a GEDCOM date * another CalendarDate object * - * @param array|int|CalendarDate $date + * @param array|int|AbstractCalendarDate $date */ public function __construct($date) { @@ -89,17 +91,17 @@ class JulianDate extends CalendarDate */ protected function formatLongYear(): string { - if ($this->y < 0) { + if ($this->year < 0) { return /* I18N: BCE=Before the Common Era, for Julian years < 0. See http://en.wikipedia.org/wiki/Common_Era */ - I18N::translate('%s BCE', I18N::digits(-$this->y)); + I18N::translate('%s BCE', I18N::digits(-$this->year)); } if ($this->new_old_style) { - return I18N::translate('%s CE', I18N::digits(sprintf('%d/%02d', $this->y - 1, $this->y % 100))); + return I18N::translate('%s CE', I18N::digits(sprintf('%d/%02d', $this->year - 1, $this->year % 100))); } /* I18N: CE=Common Era, for Julian years > 0. See http://en.wikipedia.org/wiki/Common_Era */ - return I18N::translate('%s CE', I18N::digits($this->y)); + return I18N::translate('%s CE', I18N::digits($this->year)); } /** @@ -109,14 +111,14 @@ class JulianDate extends CalendarDate */ protected function formatGedcomYear(): string { - if ($this->y < 0) { - return sprintf('%04d B.C.', -$this->y); + if ($this->year < 0) { + return sprintf('%04d B.C.', -$this->year); } if ($this->new_old_style) { - return sprintf('%04d/%02d', $this->y - 1, $this->y % 100); + return sprintf('%04d/%02d', $this->year - 1, $this->year % 100); } - return sprintf('%04d', $this->y); + return sprintf('%04d', $this->year); } } diff --git a/app/Date/RomanDate.php b/app/Date/RomanDate.php index 6be538a7b6..39f05b3415 100644 --- a/app/Date/RomanDate.php +++ b/app/Date/RomanDate.php @@ -18,7 +18,7 @@ declare(strict_types=1); namespace Fisharebest\Webtrees\Date; /** - * Definitions for the Roman calendar + * Definitions for Roman dtes. * * The 5.5.1 gedcom spec mentions this calendar, but gives no details of * how it is to be represented.... This class is just a place holder so that @@ -26,6 +26,9 @@ namespace Fisharebest\Webtrees\Date; */ class RomanDate extends JulianDate { + // GEDCOM calendar escape + const ESCAPE = '@#DROMAN@'; + /** * Generate the %E format for a date. * @@ -33,7 +36,7 @@ class RomanDate extends JulianDate */ protected function formatGedcomYear(): string { - return sprintf('%04dAUC', $this->y); + return sprintf('%04dAUC', $this->year); } /** @@ -43,6 +46,6 @@ class RomanDate extends JulianDate */ protected function formatLongYear(): string { - return $this->y . 'AUC'; + return $this->year . 'AUC'; } } |
