xref: /webtrees/app/Date.php (revision 1ff45046fabc22237b5d0d8e489c96f031fc598d)
1a25f0a04SGreg Roach<?php
23976b470SGreg Roach
3a25f0a04SGreg Roach/**
4a25f0a04SGreg Roach * webtrees: online genealogy
5d11be702SGreg Roach * Copyright (C) 2023 webtrees development team
6a25f0a04SGreg Roach * This program is free software: you can redistribute it and/or modify
7a25f0a04SGreg Roach * it under the terms of the GNU General Public License as published by
8a25f0a04SGreg Roach * the Free Software Foundation, either version 3 of the License, or
9a25f0a04SGreg Roach * (at your option) any later version.
10a25f0a04SGreg Roach * This program is distributed in the hope that it will be useful,
11a25f0a04SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
12a25f0a04SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13a25f0a04SGreg Roach * GNU General Public License for more details.
14a25f0a04SGreg Roach * You should have received a copy of the GNU General Public License
1589f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>.
16a25f0a04SGreg Roach */
17fcfa147eSGreg Roach
18e7f56f2aSGreg Roachdeclare(strict_types=1);
19e7f56f2aSGreg Roach
2076692c8bSGreg Roachnamespace Fisharebest\Webtrees;
21a25f0a04SGreg Roach
22a25f0a04SGreg Roachuse Fisharebest\ExtCalendar\GregorianCalendar;
234a83f5d7SGreg Roachuse Fisharebest\Webtrees\Date\AbstractCalendarDate;
24a25f0a04SGreg Roach
25a25f0a04SGreg Roach/**
2676692c8bSGreg Roach * A representation of GEDCOM dates and date ranges.
2776692c8bSGreg Roach *
2876692c8bSGreg Roach * Since different calendars start their days at different times, (civil
29a25f0a04SGreg Roach * midnight, solar midnight, sunset, sunrise, etc.), we convert on the basis of
30a25f0a04SGreg Roach * midday.
31a25f0a04SGreg Roach *
3276692c8bSGreg Roach * We assume that years start on the first day of the first month. Where
33a25f0a04SGreg Roach * this is not the case (e.g. England prior to 1752), we need to use modified
34a25f0a04SGreg Roach * years or the OS/NS notation "4 FEB 1750/51".
35a25f0a04SGreg Roach */
36c1010edaSGreg Roachclass Date
37c1010edaSGreg Roach{
3833c746f1SGreg Roach    // Optional qualifier, such as BEF, FROM, ABT
3933c746f1SGreg Roach    public string $qual1 = '';
40a25f0a04SGreg Roach
4133c746f1SGreg Roach    // The first (or only) date
4233c746f1SGreg Roach    private AbstractCalendarDate $date1;
43a25f0a04SGreg Roach
4433c746f1SGreg Roach    // Optional qualifier, such as TO, AND
4533c746f1SGreg Roach    public string $qual2 = '';
46a25f0a04SGreg Roach
4733c746f1SGreg Roach    // Optional second date
48*1ff45046SGreg Roach    private AbstractCalendarDate|null $date2 = null;
49a25f0a04SGreg Roach
5033c746f1SGreg Roach    // Optional text, as included with an INTerpreted date
5133c746f1SGreg Roach    private string $text = '';
52a25f0a04SGreg Roach
53a25f0a04SGreg Roach    /**
54a25f0a04SGreg Roach     * Create a date, from GEDCOM data.
55a25f0a04SGreg Roach     *
56a25f0a04SGreg Roach     * @param string $date A date in GEDCOM format
57a25f0a04SGreg Roach     */
58cfe766afSGreg Roach    public function __construct(string $date)
59c1010edaSGreg Roach    {
60f882f05dSGreg Roach        $calendar_date_factory = Registry::calendarDateFactory();
61f882f05dSGreg Roach
62a25f0a04SGreg Roach        // Extract any explanatory text
63a25f0a04SGreg Roach        if (preg_match('/^(.*) ?[(](.*)[)]/', $date, $match)) {
64a25f0a04SGreg Roach            $date       = $match[1];
65a25f0a04SGreg Roach            $this->text = $match[2];
66a25f0a04SGreg Roach        }
67a25f0a04SGreg Roach        if (preg_match('/^(FROM|BET) (.+) (AND|TO) (.+)/', $date, $match)) {
68a25f0a04SGreg Roach            $this->qual1 = $match[1];
69f882f05dSGreg Roach            $this->date1 = $calendar_date_factory->make($match[2]);
70a25f0a04SGreg Roach            $this->qual2 = $match[3];
71f882f05dSGreg Roach            $this->date2 = $calendar_date_factory->make($match[4]);
723506b2e5SGreg Roach        } elseif (preg_match('/^(TO|FROM|BEF|AFT|CAL|EST|INT|ABT) (.+)/', $date, $match)) {
73a25f0a04SGreg Roach            $this->qual1 = $match[1];
74f882f05dSGreg Roach            $this->date1 = $calendar_date_factory->make($match[2]);
75a25f0a04SGreg Roach        } else {
76f882f05dSGreg Roach            $this->date1 = $calendar_date_factory->make($date);
77a25f0a04SGreg Roach        }
78a25f0a04SGreg Roach    }
79a25f0a04SGreg Roach
80a25f0a04SGreg Roach    /**
81a25f0a04SGreg Roach     * When we copy a date object, we need to create copies of
82a25f0a04SGreg Roach     * its child objects.
83a25f0a04SGreg Roach     */
84c1010edaSGreg Roach    public function __clone()
85c1010edaSGreg Roach    {
86a25f0a04SGreg Roach        $this->date1 = clone $this->date1;
877e96c925SGreg Roach        if ($this->date2 !== null) {
88a25f0a04SGreg Roach            $this->date2 = clone $this->date2;
89a25f0a04SGreg Roach        }
90a25f0a04SGreg Roach    }
91a25f0a04SGreg Roach
92a25f0a04SGreg Roach    /**
93a25f0a04SGreg Roach     * Convert a date to the preferred format and calendar(s) display.
94a25f0a04SGreg Roach     *
9566ecd017SGreg Roach     * @param Tree|null   $tree              Wrap the date in a link to the calendar page for the tree
96a25f0a04SGreg Roach     * @param string|null $date_format       Override the default date format
9766ecd017SGreg Roach     * @param bool        $convert_calendars Convert the date into other calendars (requires a tree)
98a25f0a04SGreg Roach     *
99a25f0a04SGreg Roach     * @return string
100a25f0a04SGreg Roach     */
1012c6f1bd5SGreg Roach    public function display(Tree|null $tree = null, string|null $date_format = null, bool $convert_calendars = false): string
102c1010edaSGreg Roach    {
10366ecd017SGreg Roach        if ($tree instanceof Tree) {
10412dcbc32SGreg Roach            $CALENDAR_FORMAT = $tree->getPreference('CALENDAR_FORMAT');
1053cccdb42SGreg Roach        } else {
10666ecd017SGreg Roach            $CALENDAR_FORMAT = 'none';
1073cccdb42SGreg Roach        }
108a25f0a04SGreg Roach
1093529c469SGreg Roach        $date_format ??= I18N::dateFormat();
110a25f0a04SGreg Roach
111a25f0a04SGreg Roach        if ($convert_calendars) {
112a25f0a04SGreg Roach            $calendar_format = explode('_and_', $CALENDAR_FORMAT);
113a25f0a04SGreg Roach        } else {
11413abd6f3SGreg Roach            $calendar_format = [];
115a25f0a04SGreg Roach        }
116a25f0a04SGreg Roach
117a25f0a04SGreg Roach        // Two dates with text before, between and after
118a25f0a04SGreg Roach        $q1 = $this->qual1;
119a25f0a04SGreg Roach        $d1 = $this->date1->format($date_format, $this->qual1);
120a25f0a04SGreg Roach        $q2 = $this->qual2;
1218f038c36SRico Sonntag        if ($this->date2 === null) {
122a25f0a04SGreg Roach            $d2 = '';
123a25f0a04SGreg Roach        } else {
124a25f0a04SGreg Roach            $d2 = $this->date2->format($date_format, $this->qual2);
125a25f0a04SGreg Roach        }
126a25f0a04SGreg Roach        // Con vert to other calendars, if requested
127a25f0a04SGreg Roach        $conv1 = '';
128a25f0a04SGreg Roach        $conv2 = '';
129a25f0a04SGreg Roach        foreach ($calendar_format as $cal_fmt) {
130e364afe4SGreg Roach            if ($cal_fmt !== 'none') {
131a25f0a04SGreg Roach                $d1conv = $this->date1->convertToCalendar($cal_fmt);
132a25f0a04SGreg Roach                if ($d1conv->inValidRange()) {
133a25f0a04SGreg Roach                    $d1tmp = $d1conv->format($date_format, $this->qual1);
134a25f0a04SGreg Roach                } else {
135a25f0a04SGreg Roach                    $d1tmp = '';
136a25f0a04SGreg Roach                }
1378f038c36SRico Sonntag                if ($this->date2 === null) {
138a25f0a04SGreg Roach                    $d2conv = null;
139a25f0a04SGreg Roach                    $d2tmp  = '';
140a25f0a04SGreg Roach                } else {
141a25f0a04SGreg Roach                    $d2conv = $this->date2->convertToCalendar($cal_fmt);
142a25f0a04SGreg Roach                    if ($d2conv->inValidRange()) {
143a25f0a04SGreg Roach                        $d2tmp = $d2conv->format($date_format, $this->qual2);
144a25f0a04SGreg Roach                    } else {
145a25f0a04SGreg Roach                        $d2tmp = '';
146a25f0a04SGreg Roach                    }
147a25f0a04SGreg Roach                }
148d4f10423SGreg Roach                // If the date is different from the unconverted date, add it to the date string.
1497fa97a69SGreg Roach                if ($d1 !== $d1tmp && $d1tmp !== '') {
15066ecd017SGreg Roach                    if ($tree instanceof Tree) {
1513cfaf201SGreg Roach                        if ($CALENDAR_FORMAT !== 'none') {
1527560a51fSGreg Roach                            $conv1 .= ' <span dir="' . I18N::direction() . '">(<a href="' . e($d1conv->calendarUrl($date_format, $tree)) . '" rel="nofollow">' . $d1tmp . '</a>)</span>';
153a25f0a04SGreg Roach                        } else {
1547560a51fSGreg Roach                            $conv1 .= ' <span dir="' . I18N::direction() . '"><br><a href="' . e($d1conv->calendarUrl($date_format, $tree)) . '" rel="nofollow">' . $d1tmp . '</a></span>';
155a25f0a04SGreg Roach                        }
156a25f0a04SGreg Roach                    } else {
157c0af647eSGreg Roach                        $conv1 .= ' <span dir="' . I18N::direction() . '">(' . $d1tmp . ')</span>';
158a25f0a04SGreg Roach                    }
159a25f0a04SGreg Roach                }
1607fa97a69SGreg Roach                if ($this->date2 !== null && $d2 !== $d2tmp && $d1tmp !== '') {
16166ecd017SGreg Roach                    if ($tree instanceof Tree) {
1627560a51fSGreg Roach                        $conv2 .= ' <span dir="' . I18N::direction() . '">(<a href="' . e($d2conv->calendarUrl($date_format, $tree)) . '" rel="nofollow">' . $d2tmp . '</a>)</span>';
163a25f0a04SGreg Roach                    } else {
164c0af647eSGreg Roach                        $conv2 .= ' <span dir="' . I18N::direction() . '">(' . $d2tmp . ')</span>';
165a25f0a04SGreg Roach                    }
166a25f0a04SGreg Roach                }
167a25f0a04SGreg Roach            }
168a25f0a04SGreg Roach        }
169a25f0a04SGreg Roach
170a25f0a04SGreg Roach        // Add URLs, if requested
17166ecd017SGreg Roach        if ($tree instanceof Tree) {
1727560a51fSGreg Roach            $d1 = '<a href="' . e($this->date1->calendarUrl($date_format, $tree)) . '" rel="nofollow">' . $d1 . '</a>';
1734a83f5d7SGreg Roach            if ($this->date2 instanceof AbstractCalendarDate) {
1747560a51fSGreg Roach                $d2 = '<a href="' . e($this->date2->calendarUrl($date_format, $tree)) . '" rel="nofollow">' . $d2 . '</a>';
175a25f0a04SGreg Roach            }
176a25f0a04SGreg Roach        }
177a25f0a04SGreg Roach
178a25f0a04SGreg Roach        // Localise the date
179a25f0a04SGreg Roach        switch ($q1 . $q2) {
180a25f0a04SGreg Roach            case '':
181a25f0a04SGreg Roach                $tmp = $d1 . $conv1;
182a25f0a04SGreg Roach                break;
183a25f0a04SGreg Roach            case 'ABT':
184bbb76c12SGreg Roach                /* I18N: Gedcom ABT dates */
185bbb76c12SGreg Roach                $tmp = I18N::translate('about %s', $d1 . $conv1);
186a25f0a04SGreg Roach                break;
187a25f0a04SGreg Roach            case 'CAL':
188bbb76c12SGreg Roach                /* I18N: Gedcom CAL dates */
189bbb76c12SGreg Roach                $tmp = I18N::translate('calculated %s', $d1 . $conv1);
190a25f0a04SGreg Roach                break;
191a25f0a04SGreg Roach            case 'EST':
192bbb76c12SGreg Roach                /* I18N: Gedcom EST dates */
193bbb76c12SGreg Roach                $tmp = I18N::translate('estimated %s', $d1 . $conv1);
194a25f0a04SGreg Roach                break;
195a25f0a04SGreg Roach            case 'INT':
196bbb76c12SGreg Roach                /* I18N: Gedcom INT dates */
197bbb76c12SGreg Roach                $tmp = I18N::translate('interpreted %s (%s)', $d1 . $conv1, e($this->text));
198a25f0a04SGreg Roach                break;
199a25f0a04SGreg Roach            case 'BEF':
200bbb76c12SGreg Roach                /* I18N: Gedcom BEF dates */
201bbb76c12SGreg Roach                $tmp = I18N::translate('before %s', $d1 . $conv1);
202a25f0a04SGreg Roach                break;
203a25f0a04SGreg Roach            case 'AFT':
204bbb76c12SGreg Roach                /* I18N: Gedcom AFT dates */
205bbb76c12SGreg Roach                $tmp = I18N::translate('after %s', $d1 . $conv1);
206a25f0a04SGreg Roach                break;
207a25f0a04SGreg Roach            case 'FROM':
208bbb76c12SGreg Roach                /* I18N: Gedcom FROM dates */
209bbb76c12SGreg Roach                $tmp = I18N::translate('from %s', $d1 . $conv1);
210a25f0a04SGreg Roach                break;
211a25f0a04SGreg Roach            case 'TO':
212bbb76c12SGreg Roach                /* I18N: Gedcom TO dates */
213bbb76c12SGreg Roach                $tmp = I18N::translate('to %s', $d1 . $conv1);
214a25f0a04SGreg Roach                break;
215a25f0a04SGreg Roach            case 'BETAND':
216bbb76c12SGreg Roach                /* I18N: Gedcom BET-AND dates */
217bbb76c12SGreg Roach                $tmp = I18N::translate('between %s and %s', $d1 . $conv1, $d2 . $conv2);
218a25f0a04SGreg Roach                break;
219a25f0a04SGreg Roach            case 'FROMTO':
220bbb76c12SGreg Roach                /* I18N: Gedcom FROM-TO dates */
221bbb76c12SGreg Roach                $tmp = I18N::translate('from %s to %s', $d1 . $conv1, $d2 . $conv2);
222a25f0a04SGreg Roach                break;
223a25f0a04SGreg Roach            default:
224a25f0a04SGreg Roach                $tmp = I18N::translate('Invalid date');
225bbb76c12SGreg Roach                break;
226a25f0a04SGreg Roach        }
227a25f0a04SGreg Roach
2285fec6c0aSGreg Roach        if (strip_tags($tmp) === '') {
2295fec6c0aSGreg Roach            return '';
230a25f0a04SGreg Roach        }
231b2ce94c6SRico Sonntag
232b2ce94c6SRico Sonntag        return '<span class="date">' . $tmp . '</span>';
233a25f0a04SGreg Roach    }
234a25f0a04SGreg Roach
235a25f0a04SGreg Roach    /**
236a25f0a04SGreg Roach     * Get the earliest calendar date from this GEDCOM date.
237a25f0a04SGreg Roach     *
238a25f0a04SGreg Roach     * In the date “FROM 1900 TO 1910”, this would be 1900.
239a25f0a04SGreg Roach     *
2404a83f5d7SGreg Roach     * @return AbstractCalendarDate
241a25f0a04SGreg Roach     */
2424a83f5d7SGreg Roach    public function minimumDate(): AbstractCalendarDate
243c1010edaSGreg Roach    {
244a25f0a04SGreg Roach        return $this->date1;
245a25f0a04SGreg Roach    }
246a25f0a04SGreg Roach
247a25f0a04SGreg Roach    /**
248a25f0a04SGreg Roach     * Get the latest calendar date from this GEDCOM date.
249a25f0a04SGreg Roach     *
250a25f0a04SGreg Roach     * In the date “FROM 1900 TO 1910”, this would be 1910.
251a25f0a04SGreg Roach     *
2524a83f5d7SGreg Roach     * @return AbstractCalendarDate
253a25f0a04SGreg Roach     */
254e364afe4SGreg Roach    public function maximumDate(): AbstractCalendarDate
255c1010edaSGreg Roach    {
256af2489e5SGreg Roach        return $this->date2 ?? $this->date1;
257a25f0a04SGreg Roach    }
258a25f0a04SGreg Roach
259a25f0a04SGreg Roach    /**
260a25f0a04SGreg Roach     * Get the earliest Julian day number from this GEDCOM date.
261a25f0a04SGreg Roach     *
262cbc1590aSGreg Roach     * @return int
263a25f0a04SGreg Roach     */
2648f53f488SRico Sonntag    public function minimumJulianDay(): int
265c1010edaSGreg Roach    {
266af2489e5SGreg Roach        return $this->minimumDate()->minimumJulianDay();
267a25f0a04SGreg Roach    }
268a25f0a04SGreg Roach
269a25f0a04SGreg Roach    /**
270a25f0a04SGreg Roach     * Get the latest Julian day number from this GEDCOM date.
271a25f0a04SGreg Roach     *
272cbc1590aSGreg Roach     * @return int
273a25f0a04SGreg Roach     */
2748f53f488SRico Sonntag    public function maximumJulianDay(): int
275c1010edaSGreg Roach    {
276af2489e5SGreg Roach        return $this->maximumDate()->maximumJulianDay();
277a25f0a04SGreg Roach    }
278a25f0a04SGreg Roach
279a25f0a04SGreg Roach    /**
280a25f0a04SGreg Roach     * Get the middle Julian day number from the GEDCOM date.
281a25f0a04SGreg Roach     *
282f5b60decSGreg Roach     * For a month-only date, this would be somewhere around the 16th day.
283a25f0a04SGreg Roach     * For a year-only date, this would be somewhere around 1st July.
284a25f0a04SGreg Roach     *
285cbc1590aSGreg Roach     * @return int
286a25f0a04SGreg Roach     */
2878f53f488SRico Sonntag    public function julianDay(): int
288c1010edaSGreg Roach    {
289cdaafeeeSGreg Roach        return intdiv($this->minimumJulianDay() + $this->maximumJulianDay(), 2);
290a25f0a04SGreg Roach    }
291a25f0a04SGreg Roach
292a25f0a04SGreg Roach    /**
293a25f0a04SGreg Roach     * Offset this date by N years, and round to the whole year.
294a25f0a04SGreg Roach     *
295a25f0a04SGreg Roach     * This is typically used to create an estimated death date,
296a25f0a04SGreg Roach     * which is before a certain number of years after the birth date.
297a25f0a04SGreg Roach     *
298cbc1590aSGreg Roach     * @param int    $years     a number of years, positive or negative
299cbc1590aSGreg Roach     * @param string $qualifier typically “BEF” or “AFT”
300a25f0a04SGreg Roach     *
301a25f0a04SGreg Roach     * @return Date
302a25f0a04SGreg Roach     */
3038f53f488SRico Sonntag    public function addYears(int $years, string $qualifier = ''): Date
304c1010edaSGreg Roach    {
305a25f0a04SGreg Roach        $tmp               = clone $this;
3064a83f5d7SGreg Roach        $tmp->date1->year  += $years;
3074a83f5d7SGreg Roach        $tmp->date1->month = 0;
3084a83f5d7SGreg Roach        $tmp->date1->day   = 0;
309a25f0a04SGreg Roach        $tmp->date1->setJdFromYmd();
310a25f0a04SGreg Roach        $tmp->qual1 = $qualifier;
311a25f0a04SGreg Roach        $tmp->qual2 = '';
312a25f0a04SGreg Roach        $tmp->date2 = null;
313a25f0a04SGreg Roach
314a25f0a04SGreg Roach        return $tmp;
315a25f0a04SGreg Roach    }
316a25f0a04SGreg Roach
317a25f0a04SGreg Roach    /**
318a25f0a04SGreg Roach     * Compare two dates, so they can be sorted.
319a25f0a04SGreg Roach     *
320054771e9SGreg Roach     * return -1 if $a<$b
321054771e9SGreg Roach     * return +1 if $b>$a
322a25f0a04SGreg Roach     * return  0 if dates same/overlap
323a25f0a04SGreg Roach     * BEF/AFT sort as the day before/after
324a25f0a04SGreg Roach     *
325a25f0a04SGreg Roach     * @param Date $a
326a25f0a04SGreg Roach     * @param Date $b
327a25f0a04SGreg Roach     *
328cbc1590aSGreg Roach     * @return int
329a25f0a04SGreg Roach     */
330e364afe4SGreg Roach    public static function compare(Date $a, Date $b): int
331c1010edaSGreg Roach    {
332a25f0a04SGreg Roach        // Get min/max JD for each date.
333a25f0a04SGreg Roach        switch ($a->qual1) {
334a25f0a04SGreg Roach            case 'BEF':
335f5b60decSGreg Roach                $amin = $a->minimumJulianDay() - 1;
336a25f0a04SGreg Roach                $amax = $amin;
337a25f0a04SGreg Roach                break;
338a25f0a04SGreg Roach            case 'AFT':
339f5b60decSGreg Roach                $amax = $a->maximumJulianDay() + 1;
340a25f0a04SGreg Roach                $amin = $amax;
341a25f0a04SGreg Roach                break;
342a25f0a04SGreg Roach            default:
343f5b60decSGreg Roach                $amin = $a->minimumJulianDay();
344f5b60decSGreg Roach                $amax = $a->maximumJulianDay();
345a25f0a04SGreg Roach                break;
346a25f0a04SGreg Roach        }
347a25f0a04SGreg Roach        switch ($b->qual1) {
348a25f0a04SGreg Roach            case 'BEF':
349f5b60decSGreg Roach                $bmin = $b->minimumJulianDay() - 1;
350a25f0a04SGreg Roach                $bmax = $bmin;
351a25f0a04SGreg Roach                break;
352a25f0a04SGreg Roach            case 'AFT':
353f5b60decSGreg Roach                $bmax = $b->maximumJulianDay() + 1;
354a25f0a04SGreg Roach                $bmin = $bmax;
355a25f0a04SGreg Roach                break;
356a25f0a04SGreg Roach            default:
357f5b60decSGreg Roach                $bmin = $b->minimumJulianDay();
358f5b60decSGreg Roach                $bmax = $b->maximumJulianDay();
359a25f0a04SGreg Roach                break;
360a25f0a04SGreg Roach        }
361a25f0a04SGreg Roach        if ($amax < $bmin) {
362a25f0a04SGreg Roach            return -1;
363a25f0a04SGreg Roach        }
364b2ce94c6SRico Sonntag
365b2ce94c6SRico Sonntag        if ($amin > $bmax && $bmax > 0) {
366b2ce94c6SRico Sonntag            return 1;
367b2ce94c6SRico Sonntag        }
368b2ce94c6SRico Sonntag
369b2ce94c6SRico Sonntag        if ($amin < $bmin && $amax <= $bmax) {
370b2ce94c6SRico Sonntag            return -1;
371b2ce94c6SRico Sonntag        }
372b2ce94c6SRico Sonntag
373b2ce94c6SRico Sonntag        if ($amin > $bmin && $amax >= $bmax && $bmax > 0) {
374b2ce94c6SRico Sonntag            return 1;
375b2ce94c6SRico Sonntag        }
376b2ce94c6SRico Sonntag
377b2ce94c6SRico Sonntag        return 0;
378a25f0a04SGreg Roach    }
379a25f0a04SGreg Roach
380a25f0a04SGreg Roach    /**
381a25f0a04SGreg Roach     * Check whether a gedcom date contains usable calendar date(s).
382a25f0a04SGreg Roach     *
383a25f0a04SGreg Roach     * An incomplete date such as "12 AUG" would be invalid, as
384a25f0a04SGreg Roach     * we cannot sort it.
385a25f0a04SGreg Roach     *
386cbc1590aSGreg Roach     * @return bool
387a25f0a04SGreg Roach     */
3888f53f488SRico Sonntag    public function isOK(): bool
389c1010edaSGreg Roach    {
390f5b60decSGreg Roach        return $this->minimumJulianDay() && $this->maximumJulianDay();
391a25f0a04SGreg Roach    }
392a25f0a04SGreg Roach
393a25f0a04SGreg Roach    /**
394a25f0a04SGreg Roach     * Calculate the gregorian year for a date. This should NOT be used internally
395a25f0a04SGreg Roach     * within WT - we should keep the code "calendar neutral" to allow support for
396a25f0a04SGreg Roach     * jewish/arabic users. This is only for interfacing with external entities,
397a25f0a04SGreg Roach     * such as the ancestry.com search interface or the dated fact icons.
398a25f0a04SGreg Roach     *
399cbc1590aSGreg Roach     * @return int
400a25f0a04SGreg Roach     */
401e364afe4SGreg Roach    public function gregorianYear(): int
402c1010edaSGreg Roach    {
403a25f0a04SGreg Roach        if ($this->isOK()) {
40459f2f229SGreg Roach            $gregorian_calendar = new GregorianCalendar();
40565e02381SGreg Roach            [$year] = $gregorian_calendar->jdToYmd($this->julianDay());
406a25f0a04SGreg Roach
407a25f0a04SGreg Roach            return $year;
408a25f0a04SGreg Roach        }
409b2ce94c6SRico Sonntag
410b2ce94c6SRico Sonntag        return 0;
411a25f0a04SGreg Roach    }
412a25f0a04SGreg Roach}
413