Date::parseDate()   F
last analyzed

Complexity

Conditions 22
Paths 3072

Size

Total Lines 108
Code Lines 78

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 22
eloc 78
nc 3072
nop 1
dl 0
loc 108
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2022 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees;
21
22
use DomainException;
23
use Fisharebest\ExtCalendar\GregorianCalendar;
24
use Fisharebest\Webtrees\Date\AbstractCalendarDate;
25
use Fisharebest\Webtrees\Date\FrenchDate;
26
use Fisharebest\Webtrees\Date\GregorianDate;
27
use Fisharebest\Webtrees\Date\HijriDate;
28
use Fisharebest\Webtrees\Date\JalaliDate;
29
use Fisharebest\Webtrees\Date\JewishDate;
30
use Fisharebest\Webtrees\Date\JulianDate;
31
use Fisharebest\Webtrees\Date\RomanDate;
32
33
use function app;
34
use function trigger_error;
35
36
use const E_USER_DEPRECATED;
37
38
/**
39
 * A representation of GEDCOM dates and date ranges.
40
 *
41
 * Since different calendars start their days at different times, (civil
42
 * midnight, solar midnight, sunset, sunrise, etc.), we convert on the basis of
43
 * midday.
44
 *
45
 * We assume that years start on the first day of the first month. Where
46
 * this is not the case (e.g. England prior to 1752), we need to use modified
47
 * years or the OS/NS notation "4 FEB 1750/51".
48
 */
49
class Date
50
{
51
    /** @var string Optional qualifier, such as BEF, FROM, ABT */
52
    public $qual1 = '';
53
54
    /** @var AbstractCalendarDate  The first (or only) date */
55
    private $date1;
56
57
    /** @var string  Optional qualifier, such as TO, AND */
58
    public $qual2 = '';
59
60
    /** @var AbstractCalendarDate|null Optional second date */
61
    private $date2;
62
63
    /** @var string Optional text, as included with an INTerpreted date */
64
    private $text = '';
65
66
    /**
67
     * Create a date, from GEDCOM data.
68
     *
69
     * @param string $date A date in GEDCOM format
70
     */
71
    public function __construct(string $date)
72
    {
73
        // Extract any explanatory text
74
        if (preg_match('/^(.*) ?[(](.*)[)]/', $date, $match)) {
75
            $date       = $match[1];
76
            $this->text = $match[2];
77
        }
78
        if (preg_match('/^(FROM|BET) (.+) (AND|TO) (.+)/', $date, $match)) {
79
            $this->qual1 = $match[1];
80
            $this->date1 = $this->parseDate($match[2]);
81
            $this->qual2 = $match[3];
82
            $this->date2 = $this->parseDate($match[4]);
83
        } elseif (preg_match('/^(TO|FROM|BEF|AFT|CAL|EST|INT|ABT) (.+)/', $date, $match)) {
84
            $this->qual1 = $match[1];
85
            $this->date1 = $this->parseDate($match[2]);
86
        } else {
87
            $this->date1 = $this->parseDate($date);
88
        }
89
    }
90
91
    /**
92
     * When we copy a date object, we need to create copies of
93
     * its child objects.
94
     */
95
    public function __clone()
96
    {
97
        $this->date1 = clone $this->date1;
98
        if ($this->date2 !== null) {
99
            $this->date2 = clone $this->date2;
100
        }
101
    }
102
103
    /**
104
     * Convert a calendar date, such as "12 JUN 1943" into calendar date object.
105
     * A GEDCOM date range may have two calendar dates.
106
     *
107
     * @param string $date
108
     *
109
     * @return AbstractCalendarDate
110
     * @throws DomainException
111
     */
112
    private function parseDate(string $date): AbstractCalendarDate
113
    {
114
        // Valid calendar escape specified? - use it
115
        if (preg_match('/^(@#D(?:GREGORIAN|JULIAN|HEBREW|HIJRI|JALALI|FRENCH R|ROMAN)+@) ?(.*)/', $date, $match)) {
116
            $cal  = $match[1];
117
            $date = $match[2];
118
        } else {
119
            $cal = '';
120
        }
121
        // A date with a month: DM, M, MY or DMY
122
        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)) {
123
            $d = $match[1];
124
            $m = $match[2];
125
            $y = $match[3];
126
        } elseif (preg_match('/^(\d{1,4}(?: B\.C\.)?|\d\d\d\d\/\d\d)$/', $date, $match)) {
127
            // A date with just a year
128
            $d = '';
129
            $m = '';
130
            $y = $match[1];
131
        } else {
132
            // An invalid date - do the best we can.
133
            $d = '';
134
            $m = '';
135
            $y = '';
136
            // Look for a 3/4 digit year anywhere in the date
137
            if (preg_match('/\b(\d{3,4})\b/', $date, $match)) {
138
                $y = $match[1];
139
            }
140
            // Look for a month anywhere in the date
141
            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)) {
142
                $m = $match[1];
143
                // Look for a day number anywhere in the date
144
                if (preg_match('/\b(\d\d?)\b/', $date, $match)) {
145
                    $d = $match[1];
146
                }
147
            }
148
        }
149
150
        // Unambiguous dates - override calendar escape
151
        if (preg_match('/^(TSH|CSH|KSL|TVT|SHV|ADR|ADS|NSN|IYR|SVN|TMZ|AAV|ELL)$/', $m)) {
152
            $cal = JewishDate::ESCAPE;
153
        } elseif (preg_match('/^(VEND|BRUM|FRIM|NIVO|PLUV|VENT|GERM|FLOR|PRAI|MESS|THER|FRUC|COMP)$/', $m)) {
154
            $cal = FrenchDate::ESCAPE;
155
        } elseif (preg_match('/^(MUHAR|SAFAR|RABI[AT]|JUMA[AT]|RAJAB|SHAAB|RAMAD|SHAWW|DHUAQ|DHUAH)$/', $m)) {
156
            $cal = HijriDate::ESCAPE; // This is a WT extension
157
        } elseif (preg_match('/^(FARVA|ORDIB|KHORD|TIR|MORDA|SHAHR|MEHR|ABAN|AZAR|DEY|BAHMA|ESFAN)$/', $m)) {
158
            $cal = JalaliDate::ESCAPE; // This is a WT extension
159
        } elseif (preg_match('/^\d{1,4}( B\.C\.)|\d\d\d\d\/\d\d$/', $y)) {
160
            $cal = JulianDate::ESCAPE;
161
        }
162
163
        // Ambiguous dates - don't override calendar escape
164
        if ($cal === '') {
165
            if (preg_match('/^(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)$/', $m)) {
166
                $cal =  GregorianDate::ESCAPE;
167
            } elseif (preg_match('/^[345]\d\d\d$/', $y)) {
168
                // Year 3000-5999
169
                $cal = JewishDate::ESCAPE;
170
            } else {
171
                $cal = GregorianDate::ESCAPE;
172
            }
173
        }
174
        // Now construct an object of the correct type
175
        switch ($cal) {
176
            case GregorianDate::ESCAPE:
177
                return new GregorianDate([
178
                    $y,
179
                    $m,
180
                    $d,
181
                ]);
182
            case JulianDate::ESCAPE:
183
                return new JulianDate([
184
                    $y,
185
                    $m,
186
                    $d,
187
                ]);
188
            case JewishDate::ESCAPE:
189
                return new JewishDate([
190
                    $y,
191
                    $m,
192
                    $d,
193
                ]);
194
            case HijriDate::ESCAPE:
195
                return new HijriDate([
196
                    $y,
197
                    $m,
198
                    $d,
199
                ]);
200
            case FrenchDate::ESCAPE:
201
                return new FrenchDate([
202
                    $y,
203
                    $m,
204
                    $d,
205
                ]);
206
            case JalaliDate::ESCAPE:
207
                return new JalaliDate([
208
                    $y,
209
                    $m,
210
                    $d,
211
                ]);
212
            case RomanDate::ESCAPE:
213
                return new RomanDate([
214
                    $y,
215
                    $m,
216
                    $d,
217
                ]);
218
            default:
219
                throw new DomainException('Invalid calendar');
220
        }
221
    }
222
223
    /**
224
     * A list of supported calendars and their names.
225
     *
226
     * @return array<string>
227
     */
228
    public static function calendarNames(): array
229
    {
230
        return [
231
            /* I18N: The gregorian calendar */
232
            'gregorian' => I18N::translate('Gregorian'),
233
            /* I18N: The julian calendar */
234
            'julian'    => I18N::translate('Julian'),
235
            /* I18N: The French calendar */
236
            'french'    => I18N::translate('French'),
237
            /* I18N: The Hebrew/Jewish calendar */
238
            'jewish'    => I18N::translate('Jewish'),
239
            /* I18N: The Arabic/Hijri calendar */
240
            'hijri'     => I18N::translate('Hijri'),
241
            /* I18N: The Persian/Jalali calendar */
242
            'jalali'    => I18N::translate('Jalali'),
243
        ];
244
    }
245
246
    /**
247
     * Convert a date to the preferred format and calendar(s) display.
248
     *
249
     * @param bool|null   $url               Wrap the date in a link to calendar.php
250
     * @param string|null $date_format       Override the default date format
251
     * @param bool|null   $convert_calendars Convert the date into other calendars
252
     *
253
     * @return string
254
     */
255
    public function display($url = false, $date_format = null, $convert_calendars = true): string
256
    {
257
        // Do we need a new DateFormatterService class?
258
        if (app()->has(Tree::class)) {
259
            $tree            = app(Tree::class);
260
            $CALENDAR_FORMAT = $tree->getPreference('CALENDAR_FORMAT');
261
        } else {
262
            $tree            = null;
263
            $CALENDAR_FORMAT = '';
264
        }
265
266
        $date_format = $date_format ?? I18N::dateFormat();
267
268
        if ($convert_calendars) {
269
            $calendar_format = explode('_and_', $CALENDAR_FORMAT);
270
        } else {
271
            $calendar_format = [];
272
        }
273
274
        // Two dates with text before, between and after
275
        $q1 = $this->qual1;
276
        $d1 = $this->date1->format($date_format, $this->qual1);
277
        $q2 = $this->qual2;
278
        if ($this->date2 === null) {
279
            $d2 = '';
280
        } else {
281
            $d2 = $this->date2->format($date_format, $this->qual2);
282
        }
283
        // Con vert to other calendars, if requested
284
        $conv1 = '';
285
        $conv2 = '';
286
        foreach ($calendar_format as $cal_fmt) {
287
            if ($cal_fmt !== 'none') {
288
                $d1conv = $this->date1->convertToCalendar($cal_fmt);
289
                if ($d1conv->inValidRange()) {
290
                    $d1tmp = $d1conv->format($date_format, $this->qual1);
291
                } else {
292
                    $d1tmp = '';
293
                }
294
                if ($this->date2 === null) {
295
                    $d2conv = null;
296
                    $d2tmp  = '';
297
                } else {
298
                    $d2conv = $this->date2->convertToCalendar($cal_fmt);
299
                    if ($d2conv->inValidRange()) {
300
                        $d2tmp = $d2conv->format($date_format, $this->qual2);
301
                    } else {
302
                        $d2tmp = '';
303
                    }
304
                }
305
                // If the date is different from the unconverted date, add it to the date string.
306
                if ($d1 != $d1tmp && $d1tmp !== '') {
307
                    if ($url) {
308
                        if ($CALENDAR_FORMAT !== 'none') {
309
                            $conv1 .= ' <span dir="' . I18N::direction() . '">(<a href="' . e($d1conv->calendarUrl($date_format, $tree)) . '" rel="nofollow">' . $d1tmp . '</a>)</span>';
0 ignored issues
show
Bug introduced by
It seems like $tree can also be of type null; however, parameter $tree of Fisharebest\Webtrees\Dat...ndarDate::calendarUrl() does only seem to accept Fisharebest\Webtrees\Tree, maybe add an additional type check? ( Ignorable by Annotation )

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

309
                            $conv1 .= ' <span dir="' . I18N::direction() . '">(<a href="' . e($d1conv->calendarUrl($date_format, /** @scrutinizer ignore-type */ $tree)) . '" rel="nofollow">' . $d1tmp . '</a>)</span>';
Loading history...
310
                        } else {
311
                            $conv1 .= ' <span dir="' . I18N::direction() . '"><br><a href="' . e($d1conv->calendarUrl($date_format, $tree)) . '" rel="nofollow">' . $d1tmp . '</a></span>';
312
                        }
313
                    } else {
314
                        $conv1 .= ' <span dir="' . I18N::direction() . '">(' . $d1tmp . ')</span>';
315
                    }
316
                }
317
                if ($this->date2 !== null && $d2 != $d2tmp && $d1tmp != '') {
318
                    if ($url) {
319
                        $conv2 .= ' <span dir="' . I18N::direction() . '">(<a href="' . e($d2conv->calendarUrl($date_format, $tree)) . '" rel="nofollow">' . $d2tmp . '</a>)</span>';
320
                    } else {
321
                        $conv2 .= ' <span dir="' . I18N::direction() . '">(' . $d2tmp . ')</span>';
322
                    }
323
                }
324
            }
325
        }
326
327
        // Add URLs, if requested
328
        if ($url) {
329
            $d1 = '<a href="' . e($this->date1->calendarUrl($date_format, $tree)) . '" rel="nofollow">' . $d1 . '</a>';
330
            if ($this->date2 instanceof AbstractCalendarDate) {
331
                $d2 = '<a href="' . e($this->date2->calendarUrl($date_format, $tree)) . '" rel="nofollow">' . $d2 . '</a>';
332
            }
333
        }
334
335
        // Localise the date
336
        switch ($q1 . $q2) {
337
            case '':
338
                $tmp = $d1 . $conv1;
339
                break;
340
            case 'ABT':
341
                /* I18N: Gedcom ABT dates */
342
                $tmp = I18N::translate('about %s', $d1 . $conv1);
343
                break;
344
            case 'CAL':
345
                /* I18N: Gedcom CAL dates */
346
                $tmp = I18N::translate('calculated %s', $d1 . $conv1);
347
                break;
348
            case 'EST':
349
                /* I18N: Gedcom EST dates */
350
                $tmp = I18N::translate('estimated %s', $d1 . $conv1);
351
                break;
352
            case 'INT':
353
                /* I18N: Gedcom INT dates */
354
                $tmp = I18N::translate('interpreted %s (%s)', $d1 . $conv1, e($this->text));
355
                break;
356
            case 'BEF':
357
                /* I18N: Gedcom BEF dates */
358
                $tmp = I18N::translate('before %s', $d1 . $conv1);
359
                break;
360
            case 'AFT':
361
                /* I18N: Gedcom AFT dates */
362
                $tmp = I18N::translate('after %s', $d1 . $conv1);
363
                break;
364
            case 'FROM':
365
                /* I18N: Gedcom FROM dates */
366
                $tmp = I18N::translate('from %s', $d1 . $conv1);
367
                break;
368
            case 'TO':
369
                /* I18N: Gedcom TO dates */
370
                $tmp = I18N::translate('to %s', $d1 . $conv1);
371
                break;
372
            case 'BETAND':
373
                /* I18N: Gedcom BET-AND dates */
374
                $tmp = I18N::translate('between %s and %s', $d1 . $conv1, $d2 . $conv2);
375
                break;
376
            case 'FROMTO':
377
                /* I18N: Gedcom FROM-TO dates */
378
                $tmp = I18N::translate('from %s to %s', $d1 . $conv1, $d2 . $conv2);
379
                break;
380
            default:
381
                $tmp = I18N::translate('Invalid date');
382
                break;
383
        }
384
385
        if (strip_tags($tmp) === '') {
386
            return '';
387
        }
388
389
        return '<span class="date">' . $tmp . '</span>';
390
    }
391
392
    /**
393
     * Get the earliest calendar date from this GEDCOM date.
394
     *
395
     * In the date “FROM 1900 TO 1910”, this would be 1900.
396
     *
397
     * @return AbstractCalendarDate
398
     */
399
    public function minimumDate(): AbstractCalendarDate
400
    {
401
        return $this->date1;
402
    }
403
404
    /**
405
     * Get the latest calendar date from this GEDCOM date.
406
     *
407
     * In the date “FROM 1900 TO 1910”, this would be 1910.
408
     *
409
     * @return AbstractCalendarDate
410
     */
411
    public function maximumDate(): AbstractCalendarDate
412
    {
413
        return $this->date2 ?? $this->date1;
414
    }
415
416
    /**
417
     * Get the earliest Julian day number from this GEDCOM date.
418
     *
419
     * @return int
420
     */
421
    public function minimumJulianDay(): int
422
    {
423
        return $this->minimumDate()->minimumJulianDay();
424
    }
425
426
    /**
427
     * Get the latest Julian day number from this GEDCOM date.
428
     *
429
     * @return int
430
     */
431
    public function maximumJulianDay(): int
432
    {
433
        return $this->maximumDate()->maximumJulianDay();
434
    }
435
436
    /**
437
     * Get the middle Julian day number from the GEDCOM date.
438
     *
439
     * For a month-only date, this would be somewhere around the 16th day.
440
     * For a year-only date, this would be somewhere around 1st July.
441
     *
442
     * @return int
443
     */
444
    public function julianDay(): int
445
    {
446
        return intdiv($this->minimumJulianDay() + $this->maximumJulianDay(), 2);
447
    }
448
449
    /**
450
     * Offset this date by N years, and round to the whole year.
451
     *
452
     * This is typically used to create an estimated death date,
453
     * which is before a certain number of years after the birth date.
454
     *
455
     * @param int    $years     a number of years, positive or negative
456
     * @param string $qualifier typically “BEF” or “AFT”
457
     *
458
     * @return Date
459
     */
460
    public function addYears(int $years, string $qualifier = ''): Date
461
    {
462
        $tmp               = clone $this;
463
        $tmp->date1->year  += $years;
464
        $tmp->date1->month = 0;
465
        $tmp->date1->day   = 0;
466
        $tmp->date1->setJdFromYmd();
467
        $tmp->qual1 = $qualifier;
468
        $tmp->qual2 = '';
469
        $tmp->date2 = null;
470
471
        return $tmp;
472
    }
473
474
    /**
475
     * Calculate the the age of a person (n years), on a given date.
476
     *
477
     * @param Date $d1
478
     * @param Date $d2
479
     *
480
     * @return int
481
     *
482
     * @deprecated since 2.0.4.  Will be removed in 2.1.0
483
     */
484
    public static function getAgeYears(Date $d1, Date $d2): int
485
    {
486
        trigger_error('Date::getAgeYears() is deprecated. Use class Age instead.', E_USER_DEPRECATED);
487
488
        if (self::compare($d1, $d2) === 0) {
489
            // Overlapping dates
490
            $jd = $d1->minimumJulianDay();
491
        } else {
492
            // Non-overlapping dates
493
            $jd = $d2->minimumJulianDay();
494
        }
495
496
        if ($jd && $d1->minimumJulianDay() && $d1->minimumJulianDay() <= $jd) {
497
            return $d1->minimumDate()->getAge($jd);
0 ignored issues
show
Deprecated Code introduced by
The function Fisharebest\Webtrees\Dat...tCalendarDate::getAge() has been deprecated: since 2.0.4. Will be removed in 2.1.0 ( Ignorable by Annotation )

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

497
            return /** @scrutinizer ignore-deprecated */ $d1->minimumDate()->getAge($jd);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
498
        }
499
500
        return -1;
501
    }
502
503
    /**
504
     * Calculate the the age of a person (n days), on a given date.
505
     *
506
     * @param Date $d1
507
     * @param Date $d2
508
     *
509
     * @return int
510
     *
511
     * @deprecated since 2.0.4.  Will be removed in 2.1.0
512
     */
513
    public static function getAgeDays(Date $d1, Date $d2): int
514
    {
515
        trigger_error('Date::getAgeDays() is deprecated. Use class Age instead.', E_USER_DEPRECATED);
516
517
        if ($d2->maximumJulianDay() >= $d1->minimumJulianDay() && $d2->minimumJulianDay() <= $d1->maximumJulianDay()) {
518
            // Overlapping dates
519
            $jd = $d1->minimumJulianDay();
520
        } else {
521
            // Non-overlapping dates
522
            $jd = $d2->minimumJulianDay();
523
        }
524
525
        // Days - integer only (for sorting, rather than for display)
526
        if ($jd && $d1->minimumJulianDay()) {
527
            return $jd - $d1->minimumJulianDay();
528
        }
529
530
        return -1;
531
    }
532
533
    /**
534
     * Calculate the the age of a person, on a date.
535
     *
536
     * @param Date      $d1
537
     * @param Date|null $d2
538
     *
539
     * @return string
540
     *
541
     * @deprecated since 2.0.4.  Will be removed in 2.1.0
542
     */
543
    public static function getAge(Date $d1, Date $d2 = null): string
544
    {
545
        trigger_error('Date::getAge() is deprecated. Use class Age instead.', E_USER_DEPRECATED);
546
547
        if ($d2 instanceof self) {
548
            if ($d2->maximumJulianDay() >= $d1->minimumJulianDay() && $d2->minimumJulianDay() <= $d1->maximumJulianDay()) {
549
                // Overlapping dates
550
                $jd = $d1->minimumJulianDay();
551
            } else {
552
                // Non-overlapping dates
553
                $jd = $d2->minimumJulianDay();
554
            }
555
        } else {
556
            // If second date not specified, use today’s date
557
            $jd = Carbon::now()->julianDay();
558
        }
559
560
        // Just years, in local digits, with warning for negative/
561
        if ($jd && $d1->minimumJulianDay()) {
562
            if ($d1->minimumJulianDay() > $jd) {
563
                return view('icons/warning');
564
            }
565
566
            $years = $d1->minimumDate()->getAge($jd);
0 ignored issues
show
Deprecated Code introduced by
The function Fisharebest\Webtrees\Dat...tCalendarDate::getAge() has been deprecated: since 2.0.4. Will be removed in 2.1.0 ( Ignorable by Annotation )

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

566
            $years = /** @scrutinizer ignore-deprecated */ $d1->minimumDate()->getAge($jd);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
567
568
            return I18N::number($years);
569
        }
570
571
        return '';
572
    }
573
574
    /**
575
     * Calculate the years/months/days between two events
576
     * Return a gedcom style age string: "1y 2m 3d" (for fact details)
577
     *
578
     * @param Date      $d1
579
     * @param Date|null $d2
580
     *
581
     * @return string
582
     *
583
     * @deprecated since 2.0.4.  Will be removed in 2.1.0
584
     */
585
    public static function getAgeGedcom(Date $d1, Date $d2 = null): string
586
    {
587
        trigger_error('Date::getAgeGedcom() is deprecated. Use class Age instead.', E_USER_DEPRECATED);
588
589
        if ($d2 === null) {
590
            return $d1->date1->getAgeFull(Carbon::now()->julianDay());
0 ignored issues
show
Deprecated Code introduced by
The function Fisharebest\Webtrees\Dat...endarDate::getAgeFull() has been deprecated: since 2.0.4. Will be removed in 2.1.0 ( Ignorable by Annotation )

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

590
            return /** @scrutinizer ignore-deprecated */ $d1->date1->getAgeFull(Carbon::now()->julianDay());

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
591
        }
592
593
        if (self::compare($d1, $d2) !== 0) {
594
            return $d1->date1->getAgeFull($d2->minimumJulianDay());
0 ignored issues
show
Deprecated Code introduced by
The function Fisharebest\Webtrees\Dat...endarDate::getAgeFull() has been deprecated: since 2.0.4. Will be removed in 2.1.0 ( Ignorable by Annotation )

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

594
            return /** @scrutinizer ignore-deprecated */ $d1->date1->getAgeFull($d2->minimumJulianDay());

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
595
        }
596
597
        if ($d1->minimumJulianDay() == $d2->minimumJulianDay()) {
598
            return '0d';
599
        }
600
601
        return '';
602
    }
603
604
    /**
605
     * Compare two dates, so they can be sorted.
606
     *
607
     * return -1 if $a<$b
608
     * return +1 if $b>$a
609
     * return  0 if dates same/overlap
610
     * BEF/AFT sort as the day before/after
611
     *
612
     * @param Date $a
613
     * @param Date $b
614
     *
615
     * @return int
616
     */
617
    public static function compare(Date $a, Date $b): int
618
    {
619
        // Get min/max JD for each date.
620
        switch ($a->qual1) {
621
            case 'BEF':
622
                $amin = $a->minimumJulianDay() - 1;
623
                $amax = $amin;
624
                break;
625
            case 'AFT':
626
                $amax = $a->maximumJulianDay() + 1;
627
                $amin = $amax;
628
                break;
629
            default:
630
                $amin = $a->minimumJulianDay();
631
                $amax = $a->maximumJulianDay();
632
                break;
633
        }
634
        switch ($b->qual1) {
635
            case 'BEF':
636
                $bmin = $b->minimumJulianDay() - 1;
637
                $bmax = $bmin;
638
                break;
639
            case 'AFT':
640
                $bmax = $b->maximumJulianDay() + 1;
641
                $bmin = $bmax;
642
                break;
643
            default:
644
                $bmin = $b->minimumJulianDay();
645
                $bmax = $b->maximumJulianDay();
646
                break;
647
        }
648
        if ($amax < $bmin) {
649
            return -1;
650
        }
651
652
        if ($amin > $bmax && $bmax > 0) {
653
            return 1;
654
        }
655
656
        if ($amin < $bmin && $amax <= $bmax) {
657
            return -1;
658
        }
659
660
        if ($amin > $bmin && $amax >= $bmax && $bmax > 0) {
661
            return 1;
662
        }
663
664
        return 0;
665
    }
666
667
    /**
668
     * Check whether a gedcom date contains usable calendar date(s).
669
     *
670
     * An incomplete date such as "12 AUG" would be invalid, as
671
     * we cannot sort it.
672
     *
673
     * @return bool
674
     */
675
    public function isOK(): bool
676
    {
677
        return $this->minimumJulianDay() && $this->maximumJulianDay();
678
    }
679
680
    /**
681
     * Calculate the gregorian year for a date. This should NOT be used internally
682
     * within WT - we should keep the code "calendar neutral" to allow support for
683
     * jewish/arabic users. This is only for interfacing with external entities,
684
     * such as the ancestry.com search interface or the dated fact icons.
685
     *
686
     * @return int
687
     */
688
    public function gregorianYear(): int
689
    {
690
        if ($this->isOK()) {
691
            $gregorian_calendar = new GregorianCalendar();
692
            [$year] = $gregorian_calendar->jdToYmd($this->julianDay());
693
694
            return $year;
695
        }
696
697
        return 0;
698
    }
699
}
700