DateRange.class.php

Go to the documentation of this file.
00001 <?php
00002 /***************************************************************************
00003  *   Copyright (C) 2004-2007 by Anton E. Lebedevich                        *
00004  *                                                                         *
00005  *   This program is free software; you can redistribute it and/or modify  *
00006  *   it under the terms of the GNU Lesser General Public License as        *
00007  *   published by the Free Software Foundation; either version 3 of the    *
00008  *   License, or (at your option) any later version.                       *
00009  *                                                                         *
00010  ***************************************************************************/
00011 /* $Id: DateRange.class.php 4687 2007-12-09 18:57:18Z voxus $ */
00012 
00021     class DateRange implements Stringable, SingleRange
00022     {
00023         private $start  = null;
00024         private $end    = null;
00025         
00026         private $dayStartStamp  = null;
00027         private $dayEndStamp    = null;
00028         
00032         public static function create($start = null, $end = null)
00033         {
00034             return new self($start, $end);
00035         }
00036         
00037         public function __construct($start = null, $end = null)
00038         {
00039             if ($start)
00040                 $this->setStart($start);
00041             
00042             if ($end)
00043                 $this->setEnd($end);
00044         }
00045         
00046         public function __clone()
00047         {
00048             if ($this->start)
00049                 $this->start = clone $this->start;
00050             
00051             if ($this->end)
00052                 $this->end = clone $this->end;
00053         }
00054         
00059         public function setStart(/* Date */ $start)
00060         {
00061             $this->checkType($start);
00062             
00063             if ($this->end && $this->end->toStamp() < $start->toStamp())
00064                 throw new WrongArgumentException(
00065                     'start must be lower than end'
00066                 );
00067             
00068             $this->start = $start;
00069             $this->dayStartStamp = null;
00070             
00071             return $this;
00072         }
00073         
00078         public function setEnd(/* Date */ $end)
00079         {
00080             $this->checkType($end);
00081             
00082             if ($this->start && $this->start->toStamp() > $end->toStamp())
00083                 throw new WrongArgumentException(
00084                     'end must be higher than start'
00085                 );
00086             
00087             $this->end = $end;
00088             $this->dayEndStamp = null;
00089             return $this;
00090         }
00091         
00095         public function lazySet($start = null, $end = null)
00096         {
00097             if ($start)
00098                 $this->checkType($start);
00099             
00100             if ($end)
00101                 $this->checkType($end);
00102             
00103             if ($start && $end) {
00104                 if ($start->toStamp() >= $end->toStamp())
00105                     $this->setEnd($start)->setStart($end);
00106                 else
00107                     $this->setStart($start)->setEnd($end);
00108             } elseif ($start)
00109                 $this->setStart($start);
00110             elseif ($end)
00111                 $this->setEnd($end);
00112             
00113             return $this;
00114         }
00115         
00119         public function dropStart()
00120         {
00121             $this->start = null;
00122             $this->dayStartStamp = null;
00123             return $this;
00124         }
00125         
00129         public function dropEnd()
00130         {
00131             $this->end = null;
00132             $this->dayEndStamp = null;
00133             return $this;
00134         }
00135         
00136         public function isEmpty()
00137         {
00138             return
00139                 ($this->start === null)
00140                 && ($this->end === null);
00141         }
00142         
00146         public function getStart()
00147         {
00148             return $this->start;
00149         }
00150         
00154         public function getEnd()
00155         {
00156             return $this->end;
00157         }
00158         
00159         public function toDateString(
00160             $internalDelimiter = '-',
00161             $dateDelimiter = ' - '
00162         )
00163         {
00164             if ($this->start && $this->end)
00165                 return
00166                     "{$this->start->toDate($internalDelimiter)}"
00167                     .$dateDelimiter
00168                     ."{$this->end->toDate($internalDelimiter)}";
00169             elseif ($this->start)
00170                 return $this->start->toDate($internalDelimiter);
00171             elseif ($this->end)
00172                 return $this->end->toDate($internalDelimiter);
00173             
00174             return null;
00175         }
00176         
00177         public function toString($delimiter = ' - ')
00178         {
00179             if ($this->start && $this->end)
00180                 return
00181                     $this->start->toString()
00182                     .$delimiter
00183                     .$this->end->toString();
00184             elseif ($this->start)
00185                 return $this->start->toString();
00186             elseif ($this->end)
00187                 return $this->end->toString();
00188             
00189             return null;
00190         }
00191         
00192         public function overlaps(DateRange $range)
00193         {
00194             if ($this->isEmpty() || $range->isEmpty())
00195                 return true;
00196             
00197             $left = $this->getStartStamp();
00198             $right = $this->getEndStamp();
00199             $min = $range->getStartStamp();
00200             $max = $range->getEndStamp();
00201             
00202             return (
00203                 (
00204                     $min
00205                     && $max
00206                     && (
00207                         (
00208                             $left
00209                             && $right
00210                             && (
00211                                 (($left <= $min) && ($min <= $right))
00212                                 || (($min <= $left) && ($left <= $max))
00213                             )
00214                         ) || (
00215                             !$left
00216                             && ($min <= $right)
00217                         ) || (
00218                             !$right
00219                             && ($left <= $max)
00220                         )
00221                     )
00222                 ) || (
00223                     $min
00224                     && !$max
00225                     && (
00226                         !$right
00227                         || (
00228                             $right
00229                             && ($min <= $right)
00230                         )
00231                     )
00232                 ) || (
00233                     !$min
00234                     && $max
00235                     && (
00236                         !$left
00237                         || (
00238                             $left
00239                             && ($left <= $max)
00240                         )
00241                     )
00242                 )
00243             );
00244         }
00245         
00246         public function contains(/* Timestamp */ $date)
00247         {
00248             $this->checkType($date);
00249             
00250             $start = $this->getStartStamp();
00251             $end = $this->getEndStamp();
00252             $probe = $date->toStamp();
00253             
00254             if (
00255                 (!$start && !$end)
00256                 || (!$start && $end >= $probe)
00257                 || (!$end && $start <= $probe)
00258                 || ($start <= $probe && $end >= $probe)
00259             )
00260                 return true;
00261             
00262             return false;
00263         }
00264 
00265         public function split()
00266         {
00267             Assert::isFalse(
00268                 $this->isOpen(),
00269                 "open range can't be splitted"
00270             );
00271             
00272             $dates = array();
00273             
00274             $start = new Date($this->start->getDayStartStamp());
00275             
00276             $endStamp = $this->end->getDayEndStamp();
00277             
00278             for (
00279                 $current = $start;
00280                 $current->toStamp() < $endStamp;
00281                 $current->modify('+1 day')
00282             ) {
00283                 $dates[] = new Date($current->getDayStartStamp());
00284             }
00285             
00286             return $dates;
00287         }
00288         
00289         public static function merge($array /* of DateRanges */)
00290         {
00291             $out = array();
00292             
00293             foreach ($array as $range) {
00294                 $accepted = false;
00295                 
00296                 foreach ($out as $outRange)
00297                     if ($outRange->isNeighbour($range)) {
00298                         $outRange->enlarge($range);
00299                         $accepted = true;
00300                     }
00301                 
00302                 if (!$accepted)
00303                     $out[] = clone $range;
00304             }
00305             
00306             return $out;
00307         }
00308         
00309         public function isNeighbour(DateRange $range)
00310         {
00311             Assert::isTrue(!$this->isOpen() && !$range->isOpen());
00312             
00313             if (
00314                 $this->overlaps($range)
00315                 || (
00316                     $this->start->spawn('-1 day')->getDayStartStamp()
00317                     == $range->end->getDayStartStamp()
00318                 ) || (
00319                     $this->end->spawn('+1 day')->getDayStartStamp()
00320                     == $range->start->getDayStartStamp()
00321                 )
00322             )
00323                 return true;
00324             
00325             return false;
00326         }
00327         
00328         public function isOpen()
00329         {
00330             return !$this->start || !$this->end;
00331         }
00332         
00338         public function enlarge(DateRange $range)
00339         {
00340             if (!$range->start)
00341                 $this->start = null;
00342             elseif (
00343                 $this->start
00344                 && $this->start->toStamp() > $range->start->toStamp()
00345             )
00346                 $this->start = clone $range->start;
00347             
00348             if (!$range->end)
00349                 $this->end = null;
00350             elseif (
00351                 $this->end
00352                 && $this->end->toStamp() < $range->end->toStamp()
00353             )
00354                 $this->end = clone $range->end;
00355             
00356             return $this;
00357         }
00358         
00364         public function clip(DateRange $range)
00365         {
00366             Assert::isTrue($this->overlaps($range));
00367 
00368             if (
00369                 $range->start
00370                 && (
00371                     $this->start
00372                     && $range->start->toStamp() > $this->start->toStamp()
00373                     || !$this->start
00374                 )
00375             )
00376                 $this->start = clone $range->start;
00377             
00378             if (
00379                 $range->end
00380                 && (
00381                     $this->end
00382                     && $range->end->toStamp() < $this->end->toStamp()
00383                     || !$this->end
00384                 )
00385             )
00386                 $this->end = clone $range->end;
00387 
00388             return $this;
00389         }
00390 
00396         public function lightCopyOnClip(DateRange $range)
00397         {
00398             $copy = DateRange::create();
00399             
00400             if (
00401                 $range->start
00402                 && (
00403                     $this->start
00404                     && $range->start->toStamp() > $this->start->toStamp()
00405                     || !$this->start
00406                 )
00407             )
00408                 $copy->start = $range->start;
00409             else
00410                 $copy->start = $this->start;
00411             
00412             if (
00413                 $range->end
00414                 && (
00415                     $this->end
00416                     && $range->end->toStamp() < $this->end->toStamp()
00417                     || !$this->end
00418                 )
00419             )
00420                 $copy->end = $range->end;
00421             else
00422                 $copy->end = $this->end;
00423             
00424             return $copy;
00425         }
00426 
00427         public function getStartStamp() // null if start is null
00428         {
00429             if ($this->start) {
00430                 if (!$this->dayStartStamp) {
00431                     $this->dayStartStamp = $this->start->getDayStartStamp();
00432                 }
00433                 
00434                 return $this->dayStartStamp;
00435             }
00436             
00437             return null;
00438         }
00439         
00440         public function getEndStamp() // null if end is null
00441         {
00442             if ($this->end) {
00443                 if (!$this->dayEndStamp) {
00444                     $this->dayEndStamp = $this->end->getDayEndStamp();
00445                 }
00446                 
00447                 return $this->dayEndStamp;
00448             }
00449             
00450             return null;
00451         }
00452         
00453         public static function compare(DateRange $left, DateRange $right)
00454         {
00455             if ($left->isEmpty() && $right->isEmpty())
00456                 return 0;
00457             elseif ($left->isEmpty())
00458                 return 1;
00459             elseif ($right->isEmpty())
00460                 return -1;
00461             
00462             $leftStart = $left->getStartStamp();
00463             $leftEnd = $left->getEndStamp();
00464             
00465             $rightStart = $right->getStartStamp();
00466             $rightEnd = $right->getEndStamp();
00467             
00468             if (
00469                 !$leftStart && !$rightStart
00470                 || $leftStart && $rightStart && ($leftStart == $rightStart)
00471             ) {
00472                 if (
00473                     !$leftEnd && !$rightEnd
00474                     || $leftEnd && $rightEnd && ($leftEnd == $rightEnd)
00475                 )
00476                     return 0;
00477                 elseif (!$leftEnd && $rightEnd)
00478                     return 1;
00479                 elseif ($leftEnd && !$rightEnd)
00480                     return -1;
00481                 elseif ($leftEnd < $rightEnd)
00482                     return -1;
00483                 else
00484                     return 1;
00485             } elseif (!$leftStart && $rightStart)
00486                 return -1;
00487             elseif ($leftStart && !$rightStart)
00488                 return 1;
00489             elseif ($leftStart < $rightStart)
00490                 return -1;
00491             else
00492                 return 1;
00493         }
00494         
00495         public function isOneDay()
00496         {
00497             return (!$this->isOpen())
00498                 && ($this->start->toDate() == $this->end->toDate());
00499         }
00500         
00501         protected function checkType($value)
00502         {
00503             Assert::isTrue(
00504                 ClassUtils::isInstanceOf($value, $this->getObjectName())
00505             );
00506         }
00507         
00508         protected function getObjectName()
00509         {
00510             return 'Date';
00511         }
00512     }
00513 ?>

Generated on Sun Dec 9 21:56:23 2007 for onPHP by  doxygen 1.5.4