Go to the documentation of this file.00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00020 class DateRange implements Stringable, SingleRange
00021 {
00022 private $start = null;
00023 private $end = null;
00024
00025 private $dayStartStamp = null;
00026 private $dayEndStamp = null;
00027
00031 public static function create($start = null, $end = null)
00032 {
00033 return new self($start, $end);
00034 }
00035
00036 public function __construct($start = null, $end = null)
00037 {
00038 if ($start)
00039 $this->setStart($start);
00040
00041 if ($end)
00042 $this->setEnd($end);
00043 }
00044
00045 public function __clone()
00046 {
00047 if ($this->start)
00048 $this->start = clone $this->start;
00049
00050 if ($this->end)
00051 $this->end = clone $this->end;
00052 }
00053
00058 public function setStart( $start)
00059 {
00060 $this->checkType($start);
00061
00062 if ($this->end && $this->end->toStamp() < $start->toStamp())
00063 throw new WrongArgumentException(
00064 'start must be lower than end'
00065 );
00066
00067 $this->start = $start;
00068 $this->dayStartStamp = null;
00069
00070 return $this;
00071 }
00072
00076 public function safeSetStart( $start)
00077 {
00078 if (
00079 !$this->getEnd()
00080 || Timestamp::compare(
00081 $start, $this->getEnd()
00082 ) < 0
00083 )
00084 $this->setStart($start);
00085
00086 elseif ($this->getEnd())
00087 $this->setStart($this->getEnd());
00088
00089 return $this;
00090 }
00091
00095 public function safeSetEnd( $end)
00096 {
00097 if (
00098 !$this->getStart()
00099 || Timestamp::compare(
00100 $end, $this->getStart()
00101 ) > 0
00102 )
00103 $this->setEnd($end);
00104
00105 elseif ($this->getStart())
00106 $this->setEnd($this->getStart());
00107
00108 return $this;
00109 }
00110
00115 public function setEnd( $end)
00116 {
00117 $this->checkType($end);
00118
00119 if ($this->start && $this->start->toStamp() > $end->toStamp())
00120 throw new WrongArgumentException(
00121 'end must be higher than start'
00122 );
00123
00124 $this->end = $end;
00125 $this->dayEndStamp = null;
00126 return $this;
00127 }
00128
00132 public function lazySet($start = null, $end = null)
00133 {
00134 if ($start)
00135 $this->checkType($start);
00136
00137 if ($end)
00138 $this->checkType($end);
00139
00140 if ($start && $end) {
00141 if ($start->toStamp() >= $end->toStamp())
00142 $this->setEnd($start)->setStart($end);
00143 else
00144 $this->setStart($start)->setEnd($end);
00145 } elseif ($start)
00146 $this->setStart($start);
00147 elseif ($end)
00148 $this->setEnd($end);
00149
00150 return $this;
00151 }
00152
00156 public function dropStart()
00157 {
00158 $this->start = null;
00159 $this->dayStartStamp = null;
00160 return $this;
00161 }
00162
00166 public function dropEnd()
00167 {
00168 $this->end = null;
00169 $this->dayEndStamp = null;
00170 return $this;
00171 }
00172
00173 public function isEmpty()
00174 {
00175 return
00176 ($this->start === null)
00177 && ($this->end === null);
00178 }
00179
00183 public function getStart()
00184 {
00185 return $this->start;
00186 }
00187
00191 public function getEnd()
00192 {
00193 return $this->end;
00194 }
00195
00196 public function toDateString(
00197 $internalDelimiter = '-',
00198 $dateDelimiter = ' - '
00199 )
00200 {
00201 if ($this->start && $this->end)
00202 return
00203 "{$this->start->toDate($internalDelimiter)}"
00204 .$dateDelimiter
00205 ."{$this->end->toDate($internalDelimiter)}";
00206 elseif ($this->start)
00207 return $this->start->toDate($internalDelimiter);
00208 elseif ($this->end)
00209 return $this->end->toDate($internalDelimiter);
00210
00211 return null;
00212 }
00213
00214 public function toString($delimiter = ' - ')
00215 {
00216 if ($this->start && $this->end)
00217 return
00218 $this->start->toString()
00219 .$delimiter
00220 .$this->end->toString();
00221 elseif ($this->start)
00222 return $this->start->toString();
00223 elseif ($this->end)
00224 return $this->end->toString();
00225
00226 return null;
00227 }
00228
00229 public function overlaps(DateRange $range)
00230 {
00231 if ($this->isEmpty() || $range->isEmpty())
00232 return true;
00233
00234 $left = $this->getStartStamp();
00235 $right = $this->getEndStamp();
00236 $min = $range->getStartStamp();
00237 $max = $range->getEndStamp();
00238
00239 return (
00240 (
00241 $min
00242 && $max
00243 && (
00244 (
00245 $left
00246 && $right
00247 && (
00248 (($left <= $min) && ($min <= $right))
00249 || (($min <= $left) && ($left <= $max))
00250 )
00251 ) || (
00252 !$left
00253 && ($min <= $right)
00254 ) || (
00255 !$right
00256 && ($left <= $max)
00257 )
00258 )
00259 ) || (
00260 $min
00261 && !$max
00262 && (
00263 !$right
00264 || (
00265 $right
00266 && ($min <= $right)
00267 )
00268 )
00269 ) || (
00270 !$min
00271 && $max
00272 && (
00273 !$left
00274 || (
00275 $left
00276 && ($left <= $max)
00277 )
00278 )
00279 )
00280 );
00281 }
00282
00283 public function contains( $date)
00284 {
00285 $this->checkType($date);
00286
00287 $start = $this->getStartStamp();
00288 $end = $this->getEndStamp();
00289 $probe = $date->toStamp();
00290
00291 if (
00292 (!$start && !$end)
00293 || (!$start && $end >= $probe)
00294 || (!$end && $start <= $probe)
00295 || ($start <= $probe && $end >= $probe)
00296 )
00297 return true;
00298
00299 return false;
00300 }
00301
00302 public function split()
00303 {
00304 Assert::isFalse(
00305 $this->isOpen(),
00306 "open range can't be splitted"
00307 );
00308
00309 $dates = array();
00310
00311 $start = new Date($this->start->getDayStartStamp());
00312
00313 $endStamp = $this->end->getDayEndStamp();
00314
00315 for (
00316 $current = $start;
00317 $current->toStamp() < $endStamp;
00318 $current->modify('+1 day')
00319 ) {
00320 $dates[] = new Date($current->getDayStartStamp());
00321 }
00322
00323 return $dates;
00324 }
00325
00326 public static function merge($array )
00327 {
00328 $out = array();
00329
00330 foreach ($array as $range) {
00331 $accepted = false;
00332
00333 foreach ($out as $outRange)
00334 if ($outRange->isNeighbour($range)) {
00335 $outRange->enlarge($range);
00336 $accepted = true;
00337 }
00338
00339 if (!$accepted)
00340 $out[] = clone $range;
00341 }
00342
00343 return $out;
00344 }
00345
00346 public function isNeighbour(DateRange $range)
00347 {
00348 Assert::isTrue(!$this->isOpen() && !$range->isOpen());
00349
00350 if (
00351 $this->overlaps($range)
00352 || (
00353 $this->start->spawn('-1 day')->getDayStartStamp()
00354 == $range->end->getDayStartStamp()
00355 ) || (
00356 $this->end->spawn('+1 day')->getDayStartStamp()
00357 == $range->start->getDayStartStamp()
00358 )
00359 )
00360 return true;
00361
00362 return false;
00363 }
00364
00365 public function isOpen()
00366 {
00367 return !$this->start || !$this->end;
00368 }
00369
00375 public function enlarge(DateRange $range)
00376 {
00377 if (!$range->start)
00378 $this->start = null;
00379 elseif (
00380 $this->start
00381 && $this->start->toStamp() > $range->start->toStamp()
00382 )
00383 $this->start = clone $range->start;
00384
00385 if (!$range->end)
00386 $this->end = null;
00387 elseif (
00388 $this->end
00389 && $this->end->toStamp() < $range->end->toStamp()
00390 )
00391 $this->end = clone $range->end;
00392
00393 return $this;
00394 }
00395
00401 public function clip(DateRange $range)
00402 {
00403 Assert::isTrue($this->overlaps($range));
00404
00405 if (
00406 $range->start
00407 && (
00408 $this->start
00409 && $range->start->toStamp() > $this->start->toStamp()
00410 || !$this->start
00411 )
00412 )
00413 $this->start = clone $range->start;
00414
00415 if (
00416 $range->end
00417 && (
00418 $this->end
00419 && $range->end->toStamp() < $this->end->toStamp()
00420 || !$this->end
00421 )
00422 )
00423 $this->end = clone $range->end;
00424
00425 return $this;
00426 }
00427
00433 public function lightCopyOnClip(DateRange $range)
00434 {
00435 $copy = DateRange::create();
00436
00437 if (
00438 $range->start
00439 && (
00440 $this->start
00441 && $range->start->toStamp() > $this->start->toStamp()
00442 || !$this->start
00443 )
00444 )
00445 $copy->start = $range->start;
00446 else
00447 $copy->start = $this->start;
00448
00449 if (
00450 $range->end
00451 && (
00452 $this->end
00453 && $range->end->toStamp() < $this->end->toStamp()
00454 || !$this->end
00455 )
00456 )
00457 $copy->end = $range->end;
00458 else
00459 $copy->end = $this->end;
00460
00461 return $copy;
00462 }
00463
00464 public function getStartStamp()
00465 {
00466 if ($this->start) {
00467 if (!$this->dayStartStamp) {
00468 $this->dayStartStamp = $this->start->getDayStartStamp();
00469 }
00470
00471 return $this->dayStartStamp;
00472 }
00473
00474 return null;
00475 }
00476
00477 public function getEndStamp()
00478 {
00479 if ($this->end) {
00480 if (!$this->dayEndStamp) {
00481 $this->dayEndStamp = $this->end->getDayEndStamp();
00482 }
00483
00484 return $this->dayEndStamp;
00485 }
00486
00487 return null;
00488 }
00489
00490 public static function compare(DateRange $left, DateRange $right)
00491 {
00492 if ($left->isEmpty() && $right->isEmpty())
00493 return 0;
00494 elseif ($left->isEmpty())
00495 return 1;
00496 elseif ($right->isEmpty())
00497 return -1;
00498
00499 $leftStart = $left->getStartStamp();
00500 $leftEnd = $left->getEndStamp();
00501
00502 $rightStart = $right->getStartStamp();
00503 $rightEnd = $right->getEndStamp();
00504
00505 if (
00506 !$leftStart && !$rightStart
00507 || $leftStart && $rightStart && ($leftStart == $rightStart)
00508 ) {
00509 if (
00510 !$leftEnd && !$rightEnd
00511 || $leftEnd && $rightEnd && ($leftEnd == $rightEnd)
00512 )
00513 return 0;
00514 elseif (!$leftEnd && $rightEnd)
00515 return 1;
00516 elseif ($leftEnd && !$rightEnd)
00517 return -1;
00518 elseif ($leftEnd < $rightEnd)
00519 return -1;
00520 else
00521 return 1;
00522 } elseif (!$leftStart && $rightStart)
00523 return -1;
00524 elseif ($leftStart && !$rightStart)
00525 return 1;
00526 elseif ($leftStart < $rightStart)
00527 return -1;
00528 else
00529 return 1;
00530 }
00531
00532 public function isOneDay()
00533 {
00534 return (!$this->isOpen())
00535 && ($this->start->toDate() == $this->end->toDate());
00536 }
00537
00541 public function toTimestampRange()
00542 {
00543 return
00544 TimestampRange::create(
00545 $this->getStart()->toTimestamp(),
00546 $this->getEnd()->toTimestamp()
00547 );
00548 }
00549
00550 protected function checkType($value)
00551 {
00552 Assert::isTrue(
00553 ClassUtils::isInstanceOf($value, $this->getObjectName())
00554 );
00555 }
00556
00557 protected function getObjectName()
00558 {
00559 return 'Date';
00560 }
00561 }
00562 ?>