1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
<?php
namespace Gedcomx\Util;
/**
* Class representing a Duration in a GedcomX formal date.
* The form of a Duration string is
*
* P[yyyyY][mmM][ddD][T[hhH][mmM][ssS]]
*
* for a duration in years, months, days, hours, minutes and/or seconds.
*
* User: Danny Yeshurun (ported from Java code)
*
*/
class Duration
{
// P[yyyyY][mmM][ddD][T[hhH][mmM][ssS]]
private static $DURATION_PATTERN =
"/P(\\d{4}Y)?(\\d{2}M)?(\\d{2}D)?(?:T(\\d{2}H)?(\\d{2}M)?(\\d{2}S)?)?/";
/**
* @var int
*/
private $year;
/**
* @var int
*/
private $month;
/**
* @var int
*/
private $day;
/**
* @var int
*/
private $hour;
/**
* @var int
*/
private $minute;
/**
* @var int
*/
private $second;
/**
* A parsing method that takes a valid duration string and parses it into a Duration object.
* @param string $durationString of the form "P[yyyyY][mmM][ddD][T[hhH][mmM][ssS]]" that specifies a duration.
* @throws \Exception
*/
public function parse($durationString)
{
$matches = array();
$status = preg_match(self::$DURATION_PATTERN, $durationString, $matches);
if ($status === false) {
throw new \Exception("Malformed simple date string {$durationString} must be P[yyyyY][mmM][ddD][T[hhH][mmM][ssS]]");
}
$this->year = $this->grabInt($matches, 1);
$this->month = $this->grabInt($matches, 2);
$this->day = $this->grabInt($matches, 3);
$this->hour = $this->grabInt($matches, 4);
$this->minute = $this->grabInt($matches, 5);
$this->second = $this->grabInt($matches, 6);
}
/**
* Convert the string in the given group to an Integer, unless there are not that many groups or the group is null.
*
* @param array $matches
* @param int $group
* @return int|null
*/
private function grabInt($matches, $group)
{
if (count($matches) < $group + 1) {
return null;
} else {
$s = $matches[$group];
if (empty($s)) {
return null;
}
// Strip off last character (e.g., "12H" -> "12")
$s = substr($s, 0, strlen($s) - 1);
return intval($s);
}
}
/**
* Tell whether the Duration is valid, which means that at least one of its values is non-null,
* and none exceeds the 2-digit limit (or 4 digits for year).
* @return true if valid, false otherwise.
*/
public function isValid()
{
if ($this->year === null && $this->month === null && $this->day === null && $this->hour === null && $this->minute === null && $this->second === null) {
return false; // must have at least one value to be valid.
}
// While we might expect the two-digit values to be more constrained (e.g., month=1..12; minute=1..59),
// the format allows larger values, and perhaps a duration of 55 days is a reasonable thing to express.
return self::ok($this->year, 0, 9999) && self::ok($this->month, 0, 99) && self::ok($this->day, 0, 99) && self::ok($this->hour, 0, 99) && self::ok($this->minute, 0, 99) && self::ok($this->second, 0, 99);
}
/**
* Convert this Duration to the canonical string for a Duration, of the form:
* P[yyyyY][mmM][ddD][T[hhH][mmM][ssS]]
* for use in a GedcomX formal date string.
*
* @return string
*/
public function __toString()
{
$result = array();
$result[] = "P";
$result[] = $this->formatNumber($this->year, 4, "Y");
$result[] = $this->formatNumber($this->month, 2, "M");
$result[] = $this->formatNumber($this->day, 2, "D");
if (($this->hour !== null) || ($this->minute !== null) || ($this->second !== null)) {
$result[] = "T";
$result[] = $this->formatNumber($this->hour, 2, "H");
$result[] = $this->formatNumber($this->minute, 2, "M");
$result[] = $this->formatNumber($this->second, 2, "S");
}
return implode("", $result);
}
/**
* @param int|null $number
* @param int $digits
* @param string $suffix
* @return string
*/
private function formatNumber($number, $digits, $suffix)
{
if ($number === null) {
return "";
}
$format = "%0{$digits}d{$suffix}";
return sprintf($format, $number);
}
/**
* Set the duration day.
* @param int $day
*/
public function setDay($day)
{
$this->day = $day;
}
/**
* Get the duration day, or null if not specified.
* @return int
*/
public function getDay()
{
return $this->day;
}
/**
* Set the duration hour.
* @param int $hour
*/
public function setHour($hour)
{
$this->hour = $hour;
}
/**
* Get the duration hour, or null if not specified.
* @return int
*/
public function getHour()
{
return $this->hour;
}
/**
* Set the duration minute.
* @param int $minute
*/
public function setMinute($minute)
{
$this->minute = $minute;
}
/**
* Get the duration minute, or null if not specified.
* @return int
*/
public function getMinute()
{
return $this->minute;
}
/**
* Set the duration month.
* @param int $month
*/
public function setMonth($month)
{
$this->month = $month;
}
/**
* Get the duration month, or null if not specified.
* @return int
*/
public function getMonth()
{
return $this->month;
}
/**
* Set the duration second.
* @param int $second
*/
public function setSecond($second)
{
$this->second = $second;
}
/**
* Get the duration second, or null if not specified.
* @return int
*/
public function getSecond()
{
return $this->second;
}
/**
* Set the duration year.
* @param int $year
*/
public function setYear($year)
{
$this->year = $year;
}
/**
* Get the duration year, or null if not specified.
* @return int
*/
public function getYear()
{
return $this->year;
}
/**
* Checks if a number is in range
*
* @param int|null $number
* @param int $min
* @param int $max
* @return bool
*/
private static function ok($number, $min, $max)
{
return ($number === null) || (($number >= $min) && ($number <= $max));
}
}