1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2023 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 18declare(strict_types=1); 19 20namespace Fisharebest\Webtrees; 21 22use function view; 23 24/** 25 * The difference between two GEDCOM dates. 26 */ 27class Age 28{ 29 private int $years; 30 31 private int $months; 32 33 private int $days; 34 35 private int $total_days; 36 37 private bool $is_exact; 38 39 private bool $is_valid; 40 41 /** 42 * Age constructor. 43 * 44 * @param Date $x - The first date 45 * @param Date $y - The second date 46 */ 47 public function __construct(Date $x, Date $y) 48 { 49 // If the dates are ranges, use the start/end calendar dates. 50 $start = $x->minimumDate(); 51 $end = $y->maximumDate(); 52 53 [$this->years, $this->months, $this->days] = $start->ageDifference($end); 54 55 $this->total_days = $end->minimumJulianDay() - $start->minimumJulianDay(); 56 57 // Use the same precision as found in the dates. 58 if ($start->day() === 0 || $end->day() === 0) { 59 $this->days = 0; 60 } 61 62 if ($start->month() === 0 || $end->month() === 0) { 63 $this->months = 0; 64 } 65 66 // Are the dates exact? 67 $this->is_exact = $start->day() !== 0 && $end->day() !== 0; 68 69 // Are the dates valid? 70 $this->is_valid = $x->isOK() && $y->isOK(); 71 } 72 73 /** 74 * Show an age in a human-friendly form, such as "34 years", "8 months", "20 days". 75 * Show an empty string for invalid/missing dates. 76 * Show a warning icon for negative ages. 77 * Show zero ages without any units. 78 * 79 * @return string 80 */ 81 public function __toString(): string 82 { 83 if (!$this->is_valid) { 84 return ''; 85 } 86 87 if ($this->years < 0) { 88 return view('icons/warning'); 89 } 90 91 if ($this->years > 0) { 92 return I18N::plural('%s year', '%s years', $this->years, I18N::number($this->years)); 93 } 94 95 if ($this->months > 0) { 96 return I18N::plural('%s month', '%s months', $this->months, I18N::number($this->months)); 97 } 98 99 if ($this->days > 0 || $this->is_exact) { 100 return I18N::plural('%s day', '%s days', $this->days, I18N::number($this->days)); 101 } 102 103 return I18N::number(0); 104 } 105 106 /** 107 * How many days between two events? 108 * If either date is invalid return -1. 109 * 110 * @return int 111 */ 112 public function ageDays(): int 113 { 114 if ($this->is_valid) { 115 return $this->total_days; 116 } 117 118 return -1; 119 } 120 121 /** 122 * How many years between two events? 123 * Return -1 for invalid or reversed dates. 124 * 125 * @return int 126 */ 127 public function ageYears(): int 128 { 129 if ($this->is_valid) { 130 return $this->years; 131 } 132 133 return -1; 134 } 135 136 /** 137 * How many years between two events? 138 * If either date is invalid return -1. 139 * 140 * @return string 141 */ 142 public function ageYearsString(): string 143 { 144 if (!$this->is_valid) { 145 return ''; 146 } 147 148 if ($this->years < 0) { 149 return view('icons/warning'); 150 } 151 152 153 return I18N::number($this->years); 154 } 155} 156