Date   F
last analyzed

Complexity

Total Complexity 106

Size/Duplication

Total Lines 564
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 280
dl 0
loc 564
rs 2
c 0
b 0
f 0
wmc 106

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 17 4
A __clone() 0 5 2
C compare() 0 41 13
C getAge() 0 43 15
A maximumDate() 0 6 2
F parseDate() 0 88 22
A getAgeGedcom() 0 12 5
A addYears() 0 12 1
A minimumJulianDay() 0 3 1
A minimumDate() 0 3 1
A calendarNames() 0 9 1
A maximumJulianDay() 0 3 1
A isOK() 0 3 2
A gregorianYear() 0 9 2
A julianDay() 0 3 1
F display() 0 124 33

How to fix   Complexity   

Complex Class

Complex classes like Date often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Date, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * webtrees: online genealogy
4
 * Copyright (C) 2019 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
 */
16
namespace Fisharebest\Webtrees;
17
18
use Fisharebest\ExtCalendar\GregorianCalendar;
19
use Fisharebest\Webtrees\Date\CalendarDate;
20
use Fisharebest\Webtrees\Date\FrenchDate;
21
use Fisharebest\Webtrees\Date\GregorianDate;
22
use Fisharebest\Webtrees\Date\HijriDate;
23
use Fisharebest\Webtrees\Date\JalaliDate;
24
use Fisharebest\Webtrees\Date\JewishDate;
25
use Fisharebest\Webtrees\Date\JulianDate;
26
use Fisharebest\Webtrees\Date\RomanDate;
27
28
/**
29
 * A representation of GEDCOM dates and date ranges.
30
 *
31
 * Since different calendars start their days at different times, (civil
32
 * midnight, solar midnight, sunset, sunrise, etc.), we convert on the basis of
33
 * midday.
34
 *
35
 * We assume that years start on the first day of the first month. Where
36
 * this is not the case (e.g. England prior to 1752), we need to use modified
37
 * years or the OS/NS notation "4 FEB 1750/51".
38
 */
39
class Date
40
{
41
    /** @var string Optional qualifier, such as BEF, FROM, ABT */
42
    public $qual1;
43
44
    /** @var CalendarDate  The first (or only) date */
45
    private $date1;
46
47
    /** @var string  Optional qualifier, such as TO, AND*/
48
    public $qual2;
49
50
    /** @var CalendarDate Optional second date */
51
    private $date2;
52
53
    /** @var string ptional text, as included with an INTerpreted date */
54
    private $text;
55
56
    /**
57
     * Create a date, from GEDCOM data.
58
     *
59
     * @param string $date A date in GEDCOM format
60
     */
61
    public function __construct($date)
62
    {
63
        // Extract any explanatory text
64
        if (preg_match('/^(.*) ?[(](.*)[)]/', $date, $match)) {
65
            $date       = $match[1];
66
            $this->text = $match[2];
67
        }
68
        if (preg_match('/^(FROM|BET) (.+) (AND|TO) (.+)/', $date, $match)) {
69
            $this->qual1 = $match[1];
70
            $this->date1 = $this->parseDate($match[2]);
71
            $this->qual2 = $match[3];
72
            $this->date2 = $this->parseDate($match[4]);
73
        } elseif (preg_match('/^(TO|FROM|BEF|AFT|CAL|EST|INT|ABT) (.+)/', $date, $match)) {
74
            $this->qual1 = $match[1];
75
            $this->date1 = $this->parseDate($match[2]);
76
        } else {
77
            $this->date1 = $this->parseDate($date);
78
        }
79
    }
80
81
    /**
82
     * When we copy a date object, we need to create copies of
83
     * its child objects.
84
     */
85
    public function __clone()
86
    {
87
        $this->date1 = clone $this->date1;
88
        if (is_object($this->date2)) {
89
            $this->date2 = clone $this->date2;
90
        }
91
    }
92
93
    /**
94
     * Convert a calendar date, such as "12 JUN 1943" into calendar date object.
95
     *
96
     * A GEDCOM date range may have two calendar dates.
97
     *
98
     * @param string $date
99
     *
100
     * @throws \DomainException
101
     *
102
     * @return CalendarDate
103
     */
104
    private function parseDate($date)
105
    {
106
        // Valid calendar escape specified? - use it
107
        if (preg_match('/^(@#D(?:GREGORIAN|JULIAN|HEBREW|HIJRI|JALALI|FRENCH R|ROMAN)+@) ?(.*)/', $date, $match)) {
108
            $cal  = $match[1];
109
            $date = $match[2];
110
        } else {
111
            $cal = '';
112
        }
113
        // A date with a month: DM, M, MY or DMY
114
        if (preg_match('/^(\d?\d?) ?(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC|TSH|CSH|KSL|TVT|SHV|ADR|ADS|NSN|IYR|SVN|TMZ|AAV|ELL|VEND|BRUM|FRIM|NIVO|PLUV|VENT|GERM|FLOR|PRAI|MESS|THER|FRUC|COMP|MUHAR|SAFAR|RABI[AT]|JUMA[AT]|RAJAB|SHAAB|RAMAD|SHAWW|DHUAQ|DHUAH|FARVA|ORDIB|KHORD|TIR|MORDA|SHAHR|MEHR|ABAN|AZAR|DEY|BAHMA|ESFAN) ?((?:\d{1,4}(?: B\.C\.)?|\d\d\d\d\/\d\d)?)$/', $date, $match)) {
115
            $d = $match[1];
116
            $m = $match[2];
117
            $y = $match[3];
118
        } elseif (preg_match('/^(\d{1,4}(?: B\.C\.)?|\d\d\d\d\/\d\d)$/', $date, $match)) {
119
            // A date with just a year
120
            $d = '';
121
            $m = '';
122
            $y = $match[1];
123
        } else {
124
            // An invalid date - do the best we can.
125
            $d = '';
126
            $m = '';
127
            $y = '';
128
            // Look for a 3/4 digit year anywhere in the date
129
            if (preg_match('/\b(\d{3,4})\b/', $date, $match)) {
130
                $y = $match[1];
131
            }
132
            // Look for a month anywhere in the date
133
            if (preg_match('/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC|TSH|CSH|KSL|TVT|SHV|ADR|ADS|NSN|IYR|SVN|TMZ|AAV|ELL|VEND|BRUM|FRIM|NIVO|PLUV|VENT|GERM|FLOR|PRAI|MESS|THER|FRUC|COMP|MUHAR|SAFAR|RABI[AT]|JUMA[AT]|RAJAB|SHAAB|RAMAD|SHAWW|DHUAQ|DHUAH|FARVA|ORDIB|KHORD|TIR|MORDA|SHAHR|MEHR|ABAN|AZAR|DEY|BAHMA|ESFAN)/', $date, $match)) {
134
                $m = $match[1];
135
                // Look for a day number anywhere in the date
136
                if (preg_match('/\b(\d\d?)\b/', $date, $match)) {
137
                    $d = $match[1];
138
                }
139
            }
140
        }
141
142
        // Unambiguous dates - override calendar escape
143
        if (preg_match('/^(TSH|CSH|KSL|TVT|SHV|ADR|ADS|NSN|IYR|SVN|TMZ|AAV|ELL)$/', $m)) {
144
            $cal = '@#DHEBREW@';
145
        } else {
146
            if (preg_match('/^(VEND|BRUM|FRIM|NIVO|PLUV|VENT|GERM|FLOR|PRAI|MESS|THER|FRUC|COMP)$/', $m)) {
147
                $cal = '@#DFRENCH R@';
148
            } else {
149
                if (preg_match('/^(MUHAR|SAFAR|RABI[AT]|JUMA[AT]|RAJAB|SHAAB|RAMAD|SHAWW|DHUAQ|DHUAH)$/', $m)) {
150
                    $cal = '@#DHIJRI@'; // This is a WT extension
151
                } else {
152
                    if (preg_match('/^(FARVA|ORDIB|KHORD|TIR|MORDA|SHAHR|MEHR|ABAN|AZAR|DEY|BAHMA|ESFAN)$/', $m)) {
153
                        $cal = '@#DJALALI@'; // This is a WT extension
154
                    } elseif (preg_match('/^\d{1,4}( B\.C\.)|\d\d\d\d\/\d\d$/', $y)) {
155
                        $cal = '@#DJULIAN@';
156
                    }
157
                }
158
            }
159
        }
160
161
        // Ambiguous dates - don't override calendar escape
162
        if ($cal == '') {
163
            if (preg_match('/^(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)$/', $m)) {
164
                $cal = '@#DGREGORIAN@';
165
            } else {
166
                if (preg_match('/^[345]\d\d\d$/', $y)) {
167
                    // Year 3000-5999
168
                    $cal = '@#DHEBREW@';
169
                } else {
170
                    $cal = '@#DGREGORIAN@';
171
                }
172
            }
173
        }
174
        // Now construct an object of the correct type
175
        switch ($cal) {
176
            case '@#DGREGORIAN@':
177
                return new GregorianDate(array($y, $m, $d));
178
            case '@#DJULIAN@':
179
                return new JulianDate(array($y, $m, $d));
180
            case '@#DHEBREW@':
181
                return new JewishDate(array($y, $m, $d));
182
            case '@#DHIJRI@':
183
                return new HijriDate(array($y, $m, $d));
184
            case '@#DFRENCH R@':
185
                return new FrenchDate(array($y, $m, $d));
186
            case '@#DJALALI@':
187
                return new JalaliDate(array($y, $m, $d));
188
            case '@#DROMAN@':
189
                return new RomanDate(array($y, $m, $d));
190
            default:
191
                throw new \DomainException('Invalid calendar');
192
        }
193
    }
194
195
    /**
196
     * A list of supported calendars and their names.
197
     *
198
     * @return string[]
199
     */
200
    public static function calendarNames()
201
    {
202
        return array(
203
            'gregorian' => /* I18N: The gregorian calendar */ I18N::translate('Gregorian'),
204
            'julian'    => /* I18N: The julian calendar */ I18N::translate('Julian'),
205
            'french'    => /* I18N: The French calendar */ I18N::translate('French'),
206
            'jewish'    => /* I18N: The Hebrew/Jewish calendar */ I18N::translate('Jewish'),
207
            'hijri'     => /* I18N: The Arabic/Hijri calendar */ I18N::translate('Hijri'),
208
            'jalali'    => /* I18N: The Persian/Jalali calendar */ I18N::translate('Jalali'),
209
        );
210
    }
211
212
    /**
213
     * Convert a date to the preferred format and calendar(s) display.
214
     *
215
     * @param bool|null   $url               Wrap the date in a link to calendar.php
216
     * @param string|null $date_format       Override the default date format
217
     * @param bool|null   $convert_calendars Convert the date into other calendars
218
     *
219
     * @return string
220
     */
221
    public function display($url = false, $date_format = null, $convert_calendars = true)
222
    {
223
        global $WT_TREE;
224
225
        $CALENDAR_FORMAT = $WT_TREE->getPreference('CALENDAR_FORMAT');
226
227
        if ($date_format === null) {
228
            $date_format = I18N::dateFormat();
229
        }
230
231
        if ($convert_calendars) {
232
            $calendar_format = explode('_and_', $CALENDAR_FORMAT);
233
        } else {
234
            $calendar_format = array();
235
        }
236
237
        // Two dates with text before, between and after
238
        $q1 = $this->qual1;
239
        $d1 = $this->date1->format($date_format, $this->qual1);
240
        $q2 = $this->qual2;
241
        if ($this->date2 === null) {
242
            $d2 = '';
243
        } else {
244
            $d2 = $this->date2->format($date_format, $this->qual2);
245
        }
246
        // Con vert to other calendars, if requested
247
        $conv1 = '';
248
        $conv2 = '';
249
        foreach ($calendar_format as $cal_fmt) {
250
            if ($cal_fmt != 'none') {
251
                $d1conv = $this->date1->convertToCalendar($cal_fmt);
252
                if ($d1conv->inValidRange()) {
253
                    $d1tmp = $d1conv->format($date_format, $this->qual1);
254
                } else {
255
                    $d1tmp = '';
256
                }
257
                if ($this->date2 === null) {
258
                    $d2conv = null;
259
                    $d2tmp  = '';
260
                } else {
261
                    $d2conv = $this->date2->convertToCalendar($cal_fmt);
262
                    if ($d2conv->inValidRange()) {
263
                        $d2tmp = $d2conv->format($date_format, $this->qual2);
264
                    } else {
265
                        $d2tmp = '';
266
                    }
267
                }
268
                // If the date is different from the unconverted date, add it to the date string.
269
                if ($d1 != $d1tmp && $d1tmp !== '') {
270
                    if ($url) {
271
                        if ($CALENDAR_FORMAT !== 'none') {
272
                            $conv1 .= ' <span dir="' . I18N::direction() . '">(<a href="' . $d1conv->calendarUrl($date_format) . '" rel="nofollow">' . $d1tmp . '</a>)</span>';
273
                        } else {
274
                            $conv1 .= ' <span dir="' . I18N::direction() . '"><br><a href="' . $d1conv->calendarUrl($date_format) . '" rel="nofollow">' . $d1tmp . '</a></span>';
275
                        }
276
                    } else {
277
                        $conv1 .= ' <span dir="' . I18N::direction() . '">(' . $d1tmp . ')</span>';
278
                    }
279
                }
280
                if ($this->date2 instanceof CalendarDate && $d2 != $d2tmp && $d1tmp != '') {
281
                    if ($url) {
282
                        $conv2 .= ' <span dir="' . I18N::direction() . '">(<a href="' . $d2conv->calendarUrl($date_format) . '" rel="nofollow">' . $d2tmp . '</a>)</span>';
283
                    } else {
284
                        $conv2 .= ' <span dir="' . I18N::direction() . '">(' . $d2tmp . ')</span>';
285
                    }
286
                }
287
            }
288
        }
289
290
        // Add URLs, if requested
291
        if ($url) {
292
            $d1 = '<a href="' . $this->date1->calendarUrl($date_format) . '" rel="nofollow">' . $d1 . '</a>';
293
            if ($this->date2 instanceof CalendarDate) {
294
                $d2 = '<a href="' . $this->date2->calendarUrl($date_format) . '" rel="nofollow">' . $d2 . '</a>';
295
            }
296
        }
297
298
        // Localise the date
299
        switch ($q1 . $q2) {
300
            case '':
301
                $tmp = $d1 . $conv1;
302
                break;
303
            case 'ABT':
304
                $tmp = /* I18N: Gedcom ABT dates */ I18N::translate('about %s', $d1 . $conv1);
305
                break;
306
            case 'CAL':
307
                $tmp = /* I18N: Gedcom CAL dates */ I18N::translate('calculated %s', $d1 . $conv1);
308
                break;
309
            case 'EST':
310
                $tmp = /* I18N: Gedcom EST dates */ I18N::translate('estimated %s', $d1 . $conv1);
311
                break;
312
            case 'INT':
313
                $tmp = /* I18N: Gedcom INT dates */ I18N::translate('interpreted %s (%s)', $d1 . $conv1, Filter::escapeHtml($this->text));
314
                break;
315
            case 'BEF':
316
                $tmp = /* I18N: Gedcom BEF dates */ I18N::translate('before %s', $d1 . $conv1);
317
                break;
318
            case 'AFT':
319
                $tmp = /* I18N: Gedcom AFT dates */ I18N::translate('after %s', $d1 . $conv1);
320
                break;
321
            case 'FROM':
322
                $tmp = /* I18N: Gedcom FROM dates */ I18N::translate('from %s', $d1 . $conv1);
323
                break;
324
            case 'TO':
325
                $tmp = /* I18N: Gedcom TO dates */ I18N::translate('to %s', $d1 . $conv1);
326
                break;
327
            case 'BETAND':
328
                $tmp = /* I18N: Gedcom BET-AND dates */ I18N::translate('between %s and %s', $d1 . $conv1, $d2 . $conv2);
329
                break;
330
            case 'FROMTO':
331
                $tmp = /* I18N: Gedcom FROM-TO dates */ I18N::translate('from %s to %s', $d1 . $conv1, $d2 . $conv2);
332
                break;
333
            default:
334
                $tmp = I18N::translate('Invalid date');
335
                break; // e.g. BET without AND
336
        }
337
        if ($this->text && !$q1) {
338
            $tmp = I18N::translate('%1$s (%2$s)', $tmp, $this->text);
339
        }
340
341
        if (strip_tags($tmp) === '') {
342
            return '';
343
        } else {
344
            return '<span class="date">' . $tmp . '</span>';
345
        }
346
    }
347
348
    /**
349
     * Get the earliest calendar date from this GEDCOM date.
350
     *
351
     * In the date “FROM 1900 TO 1910”, this would be 1900.
352
     *
353
     * @return CalendarDate
354
     */
355
    public function minimumDate()
356
    {
357
        return $this->date1;
358
    }
359
360
    /**
361
     * Get the latest calendar date from this GEDCOM date.
362
     *
363
     * In the date “FROM 1900 TO 1910”, this would be 1910.
364
     *
365
     * @return CalendarDate
366
     */
367
    public function maximumDate()
368
    {
369
        if ($this->date2 === null) {
370
            return $this->date1;
371
        } else {
372
            return $this->date2;
373
        }
374
    }
375
376
    /**
377
     * Get the earliest Julian day number from this GEDCOM date.
378
     *
379
     * @return int
380
     */
381
    public function minimumJulianDay()
382
    {
383
        return $this->minimumDate()->minJD;
384
    }
385
386
    /**
387
     * Get the latest Julian day number from this GEDCOM date.
388
     *
389
     * @return int
390
     */
391
    public function maximumJulianDay()
392
    {
393
        return $this->maximumDate()->maxJD;
394
    }
395
396
    /**
397
     * Get the middle Julian day number from the GEDCOM date.
398
     *
399
     * For a month-only date, this would be somewhere around the 16th day.
400
     * For a year-only date, this would be somewhere around 1st July.
401
     *
402
     * @return int
403
     */
404
    public function julianDay()
405
    {
406
        return (int) (($this->minimumJulianDay() + $this->maximumJulianDay()) / 2);
407
    }
408
409
    /**
410
     * Offset this date by N years, and round to the whole year.
411
     *
412
     * This is typically used to create an estimated death date,
413
     * which is before a certain number of years after the birth date.
414
     *
415
     * @param int     $years     a number of years, positive or negative
416
     * @param string  $qualifier typically “BEF” or “AFT”
417
     *
418
     * @return Date
419
     */
420
    public function addYears($years, $qualifier = '')
421
    {
422
        $tmp = clone $this;
423
        $tmp->date1->y += $years;
424
        $tmp->date1->m = 0;
425
        $tmp->date1->d = 0;
426
        $tmp->date1->setJdFromYmd();
427
        $tmp->qual1 = $qualifier;
428
        $tmp->qual2 = '';
429
        $tmp->date2 = null;
430
431
        return $tmp;
432
    }
433
434
    /**
435
     * Calculate the the age of a person, on a date.
436
     *
437
     * @param Date $d1
438
     * @param Date $d2
439
     * @param int  $format
440
     *
441
     * @throws \InvalidArgumentException
442
     *
443
     * @return int|string
444
     */
445
    public static function getAge(Date $d1, Date $d2 = null, $format = 0)
446
    {
447
        if ($d2) {
448
            if ($d2->maximumJulianDay() >= $d1->minimumJulianDay() && $d2->minimumJulianDay() <= $d1->minimumJulianDay()) {
449
                // Overlapping dates
450
                $jd = $d1->minimumJulianDay();
451
            } else {
452
                // Non-overlapping dates
453
                $jd = $d2->minimumJulianDay();
454
            }
455
        } else {
456
            // If second date not specified, use today’s date
457
            $jd = WT_CLIENT_JD;
458
        }
459
460
        switch ($format) {
461
            case 0:
462
                // Years - integer only (for statistics, rather than for display)
463
                if ($jd && $d1->minimumJulianDay() && $d1->minimumJulianDay() <= $jd) {
464
                    return $d1->minimumDate()->getAge(false, $jd, false);
465
                } else {
466
                    return -1;
467
                }
468
            case 1:
469
                // Days - integer only (for sorting, rather than for display)
470
                if ($jd && $d1->minimumJulianDay()) {
471
                    return $jd - $d1->minimumJulianDay();
472
                } else {
473
                    return -1;
474
                }
475
            case 2:
476
                // Just years, in local digits, with warning for negative/
477
                if ($jd && $d1->minimumJulianDay()) {
478
                    if ($d1->minimumJulianDay() > $jd) {
479
                        return '<i class="icon-warning"></i>';
480
                    } else {
481
                        return I18N::number($d1->minimumDate()->getAge(false, $jd));
0 ignored issues
show
Bug introduced by
$d1->minimumDate()->getAge(false, $jd) of type string is incompatible with the type double expected by parameter $n of Fisharebest\Webtrees\I18N::number(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

481
                        return I18N::number(/** @scrutinizer ignore-type */ $d1->minimumDate()->getAge(false, $jd));
Loading history...
482
                    }
483
                } else {
484
                    return '';
485
                }
486
            default:
487
                throw new \InvalidArgumentException('format: ' . $format);
488
        }
489
    }
490
491
    /**
492
     * Calculate the years/months/days between two events
493
     * Return a gedcom style age string: "1y 2m 3d" (for fact details)
494
     *
495
     * @param Date      $d1
496
     * @param Date|null $d2
497
     *
498
     * @return string
499
     */
500
    public static function getAgeGedcom(Date $d1, Date $d2 = null)
501
    {
502
        if ($d2 === null) {
503
            return $d1->date1->getAge(true, WT_CLIENT_JD, true);
504
        } else {
505
            // If dates overlap, then can’t calculate age.
506
            if (self::compare($d1, $d2)) {
507
                return $d1->date1->getAge(true, $d2->minimumJulianDay(), true);
508
            } elseif (self::compare($d1, $d2) == 0 && $d1->minimumJulianDay() == $d2->minimumJulianDay()) {
509
                return '0d';
510
            } else {
511
                return '';
512
            }
513
        }
514
    }
515
516
    /**
517
     * Compare two dates, so they can be sorted.
518
     *
519
     * return <0 if $a<$b
520
     * return >0 if $b>$a
521
     * return  0 if dates same/overlap
522
     * BEF/AFT sort as the day before/after
523
     *
524
     * @param Date $a
525
     * @param Date $b
526
     *
527
     * @return int
528
     */
529
    public static function compare(Date $a, Date $b)
530
    {
531
        // Get min/max JD for each date.
532
        switch ($a->qual1) {
533
            case 'BEF':
534
                $amin = $a->minimumJulianDay() - 1;
535
                $amax = $amin;
536
                break;
537
            case 'AFT':
538
                $amax = $a->maximumJulianDay() + 1;
539
                $amin = $amax;
540
                break;
541
            default:
542
                $amin = $a->minimumJulianDay();
543
                $amax = $a->maximumJulianDay();
544
                break;
545
        }
546
        switch ($b->qual1) {
547
            case 'BEF':
548
                $bmin = $b->minimumJulianDay() - 1;
549
                $bmax = $bmin;
550
                break;
551
            case 'AFT':
552
                $bmax = $b->maximumJulianDay() + 1;
553
                $bmin = $bmax;
554
                break;
555
            default:
556
                $bmin = $b->minimumJulianDay();
557
                $bmax = $b->maximumJulianDay();
558
                break;
559
        }
560
        if ($amax < $bmin) {
561
            return -1;
562
        } elseif ($amin > $bmax && $bmax > 0) {
563
            return 1;
564
        } elseif ($amin < $bmin && $amax <= $bmax) {
565
            return -1;
566
        } elseif ($amin > $bmin && $amax >= $bmax && $bmax > 0) {
567
            return 1;
568
        } else {
569
            return 0;
570
        }
571
    }
572
573
    /**
574
     * Check whether a gedcom date contains usable calendar date(s).
575
     *
576
     * An incomplete date such as "12 AUG" would be invalid, as
577
     * we cannot sort it.
578
     *
579
     * @return bool
580
     */
581
    public function isOK()
582
    {
583
        return $this->minimumJulianDay() && $this->maximumJulianDay();
584
    }
585
586
    /**
587
     * Calculate the gregorian year for a date. This should NOT be used internally
588
     * within WT - we should keep the code "calendar neutral" to allow support for
589
     * jewish/arabic users. This is only for interfacing with external entities,
590
     * such as the ancestry.com search interface or the dated fact icons.
591
     *
592
     * @return int
593
     */
594
    public function gregorianYear()
595
    {
596
        if ($this->isOK()) {
597
            $gregorian_calendar = new GregorianCalendar;
598
            list($year)         = $gregorian_calendar->jdToYmd($this->julianDay());
599
600
            return $year;
601
        } else {
602
            return 0;
603
        }
604
    }
605
}
606