1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2021 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 /** @var int */ 30 private $years; 31 32 /** @var int */ 33 private $months; 34 35 /** @var int */ 36 private $days; 37 38 /** @var int */ 39 private $total_days; 40 41 /** @var bool */ 42 private $is_exact; 43 44 /** @var bool */ 45 private $is_valid; 46 47 /** 48 * Age constructor. 49 * 50 * @param Date $x - The first date 51 * @param Date $y - The second date 52 */ 53 public function __construct(Date $x, Date $y) 54 { 55 // If the dates are ranges, use the start/end calendar dates. 56 $start = $x->minimumDate(); 57 $end = $y->maximumDate(); 58 59 [$this->years, $this->months, $this->days] = $start->ageDifference($end); 60 61 $this->total_days = $end->minimumJulianDay() - $start->minimumJulianDay(); 62 63 // Use the same precision as found in the dates. 64 if ($start->day() === 0 || $end->day() === 0) { 65 $this->days = 0; 66 } 67 68 if ($start->month() === 0 || $end->month() === 0) { 69 $this->months = 0; 70 } 71 72 // Are the dates exact? 73 $this->is_exact = $start->day() !== 0 && $end->day() !== 0; 74 75 // Are the dates valid? 76 $this->is_valid = $x->isOK() && $y->isOK(); 77 } 78 79 /** 80 * Show an age in a human-friendly form, such as "34 years", "8 months", "20 days". 81 * Show an empty string for invalid/missing dates. 82 * Show a warning icon for negative ages. 83 * Show zero ages without any units. 84 * 85 * @return string 86 * @deprecated - will be removed in 2.1.0 87 */ 88 public function ageString(): string 89 { 90 return $this->__toString(); 91 } 92 93 /** 94 * Show an age in a human-friendly form, such as "34 years", "8 months", "20 days". 95 * Show an empty string for invalid/missing dates. 96 * Show a warning icon for negative ages. 97 * Show zero ages without any units. 98 * 99 * @return string 100 */ 101 public function __toString(): string 102 { 103 if (!$this->is_valid) { 104 return ''; 105 } 106 107 if ($this->years < 0) { 108 return view('icons/warning'); 109 } 110 111 if ($this->years > 0) { 112 return I18N::plural('%s year', '%s years', $this->years, I18N::number($this->years)); 113 } 114 115 if ($this->months > 0) { 116 return I18N::plural('%s month', '%s months', $this->months, I18N::number($this->months)); 117 } 118 119 if ($this->days > 0 || $this->is_exact) { 120 return I18N::plural('%s day', '%s days', $this->days, I18N::number($this->days)); 121 } 122 123 return I18N::number(0); 124 } 125 126 /** 127 * How many days between two events? 128 * If either date is invalid return -1. 129 * 130 * @return int 131 */ 132 public function ageDays(): int 133 { 134 if ($this->is_valid) { 135 return $this->total_days; 136 } 137 138 return -1; 139 } 140 141 /** 142 * How many years between two events? 143 * Return -1 for invalid or reversed dates. 144 * 145 * @return int 146 */ 147 public function ageYears(): int 148 { 149 if ($this->is_valid) { 150 return $this->years; 151 } 152 153 return -1; 154 } 155 156 /** 157 * How many years between two events? 158 * If either date is invalid return -1. 159 * 160 * @return string 161 */ 162 public function ageYearsString(): string 163 { 164 if (!$this->is_valid) { 165 return ''; 166 } 167 168 if ($this->years < 0) { 169 return view('icons/warning'); 170 } 171 172 173 return I18N::number($this->years); 174 } 175 176 /** 177 * @param bool $living 178 * 179 * @return string 180 * @deprecated - will be removed in 2.1.0 181 */ 182 public function ageAtEvent(bool $living): string 183 { 184 $age = (string) $this; 185 186 if ($age === '') { 187 return ''; 188 } 189 190 if ($living) { 191 /* I18N: The current age of a living individual */ 192 return I18N::translate('(age %s)', $age); 193 } 194 195 /* I18N: The age of an individual at a given date */ 196 return I18N::translate('(aged %s)', $age); 197 } 198 199 /** 200 * Similar to ageAtEvent, but for events such as burial, cremation, etc. 201 * 202 * @return string 203 * @deprecated - will be removed in 2.1.0 204 */ 205 public function timeAfterDeath(): string 206 { 207 if (!$this->is_valid) { 208 return ''; 209 } 210 211 if ($this->years === 0 && $this->months === 0 && $this->days === 0) { 212 if ($this->is_exact) { 213 return I18N::translate('(on the date of death)'); 214 } 215 216 return ''; 217 } 218 219 return I18N::translate('(%s after death)', (string) $this); 220 } 221} 222