DateRanges.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. <?php
  2. Class DateRanges {
  3. private $dateFormat = "Y-m-d";
  4. private $rangeSeparator = ";";
  5. private $dateSeparator = ":";
  6. private function strToArr($str) {
  7. return array_map(function ($range) {
  8. $_range = array_combine(['from', 'to'], array_map(function ($date) {
  9. if (!$date) return '';
  10. return DateTime::createFromFormat('!' . $this->dateFormat, $date)->getTimestamp();
  11. }, explode($this->dateSeparator, $range)));
  12. if ($_range['to'] && $_range['to'] < $_range['from']) throw new Exception("Date 'to' before date 'from'");
  13. return $_range;
  14. }, explode($this->rangeSeparator, $str));
  15. }
  16. private function arrToStr($arr) {
  17. return implode($this->rangeSeparator, array_map(function ($range) {
  18. return implode($this->dateSeparator, array_map(function ($date) {
  19. if (!$date) return '';
  20. return date($this->dateFormat, $date);
  21. }, $range));
  22. }, $arr));
  23. }
  24. private static function sort(&$ranges) {
  25. uasort($ranges, function($a, $b) {
  26. if ($a['from'] == $b['from']) {
  27. if ($a['to'] == $b['to']) return 0;
  28. if (!$a['to']) return 1;
  29. if (!$b['to']) return -1;
  30. return ($a['to'] < $b['to']) ? -1 : 1;
  31. }
  32. return ($a['from'] < $b['from']) ? -1 : 1;
  33. });
  34. }
  35. private function implodeRanges($ranges) {
  36. if (is_array($ranges)) {
  37. $ranges = implode($this->rangeSeparator, array_filter($ranges, function($range) {
  38. if ($range) return true;
  39. return false;
  40. }));
  41. }
  42. return $ranges;
  43. }
  44. private function _merge($ranges) {
  45. $ranges = $this->implodeRanges($ranges);
  46. if (!$ranges) throw new Exception("DataRanges::_merges - bad ranges");
  47. $ranges = $this->strToArr($ranges);
  48. self::sort($ranges);
  49. $result = [];
  50. foreach ($ranges as $range) {
  51. if (!$result) $result[] = $range;
  52. else {
  53. if (!end($result)['to']) break;
  54. $k = key($result);
  55. if ($range['from'] == $result[$k]['from']) $result[$k]['to'] = $range['to'];
  56. elseif ((!$range['to']) || ($range['to'] > $result[$k]['to'])) {
  57. if ($range['from'] > $result[$k]['to']) $result[] = $range;
  58. else $result[$k]['to'] = $range['to'];
  59. }
  60. }
  61. }
  62. return $this->arrToStr($result);
  63. }
  64. private static function lt($date1, $date2) {
  65. if (!$date1) return false;
  66. if (!$date2) return true;
  67. return ($date1 < $date2);
  68. }
  69. private static function gt($date1, $date2) {
  70. if (!$date2) return false;
  71. if (!$date1) return true;
  72. return ($date1 > $date2);
  73. }
  74. private static function le($date1, $date2) {
  75. return (!self::gt($date1, $date2));
  76. }
  77. private static function ge($date1, $date2) {
  78. return (!self::lt($date1, $date2));
  79. }
  80. private static function min($dates) {
  81. if ((!is_array($dates)) || (!$dates)) throw new Exception("DateRanges::min - bad arguments");
  82. $min = null;
  83. foreach ($dates as $date) {
  84. if ($min === null) {
  85. $min = $date;
  86. continue;
  87. }
  88. if (self::lt($date, $min)) $min = $date;
  89. }
  90. return $min;
  91. }
  92. private static function max($dates) {
  93. if ((!is_array($dates)) || (!$dates)) throw new Exception("DateRanges::max - bad arguments");
  94. $max = null;
  95. foreach ($dates as $date) {
  96. if ($max === null) {
  97. $max = $date;
  98. continue;
  99. }
  100. if (self::gt($date, $max)) $max = $date;
  101. }
  102. return $max;
  103. }
  104. private function _common($rangesA, $rangesB) {
  105. $rangesA = $this->implodeRanges($rangesA);
  106. $rangesB = $this->implodeRanges($rangesB);
  107. if (!($rangesA && $rangesB)) throw new Exception("DataRanges::_common - bad ranges");
  108. $rangesA = $this->strToArr($rangesA);
  109. $rangesB = $this->strToArr($rangesB);
  110. self::sort($rangesA);
  111. self::sort($rangesB);
  112. $ranges = [];
  113. foreach ($rangesA as $rangeA) {
  114. foreach ($rangesB as $rangeB) {
  115. // if ($rangeA['from'] == $rangeB['from'] && $rangeA['to'] == $rangeB['to']) {
  116. // $ranges[] = $rangeA;
  117. // continue;
  118. // }
  119. $range = [
  120. 'from' => self::max([$rangeA['from'], $rangeB['from']]),
  121. 'to' => self::min([$rangeA['to'], $rangeB['to']]),
  122. ];
  123. //print_r($range);
  124. if (self::le($range['from'], $range['to'])) { // || ($range['from'] == $range['to'] && ($rangeA['from'] == $rangeB['from'] || $rangeA['to'] == $rangeB['to']))) {
  125. $ranges[] = $range;
  126. }
  127. }
  128. }
  129. if (!$ranges) return null;
  130. return $this->_merge($this->arrToStr($ranges));
  131. }
  132. private function _days($ranges) {
  133. $ranges = $this->implodeRanges($ranges);
  134. if (!$ranges) throw new Exception("DataRanges::_days - bad ranges ({$ranges})");
  135. $ranges = $this->strToArr($ranges);
  136. $result = (int) 0;
  137. foreach ($ranges as $range) {
  138. if (!$range['from']) throw new Exception("DataRanges::_days - bad date from");
  139. if (!$range['to']) return -1;
  140. if ($range['to'] < $range['from']) throw new Exception("DataRanges::_days - date to before date from");
  141. $result += round(($range['to'] - $range['from']) / 86400);
  142. }
  143. return round($result);
  144. }
  145. public function __construct($dateFormat = null, $rangeSeparator = null, $dateSeparator = null) {
  146. if ($dateFormat !== null) $this->dateFormat = $dateFormat;
  147. if ($rangeSeparator !== null) $this->rangeSeparator = $rangeSeparator;
  148. if ($dateSeparator !== null) $this->dateSeparator = $dateSeparator;
  149. }
  150. public static function merge($ranges, $dateFormat = null, $rangeSeparator = null, $dateSeparator = null) {
  151. $obj = new DateRanges($dateFormat, $rangeSeparator, $dateSeparator);
  152. return $obj->_merge($ranges);
  153. }
  154. public static function common($rangesA, $rangesB, $dateFormat = null, $rangeSeparator = null, $dateSeparator = null) {
  155. $obj = new DateRanges($dateFormat, $rangeSeparator, $dateSeparator);
  156. return $obj->_common($rangesA, $rangesB);
  157. }
  158. public static function days($ranges, $dateFormat = null, $rangeSeparator = null, $dateSeparator = null) {
  159. $obj = new DateRanges($dateFormat, $rangeSeparator, $dateSeparator);
  160. return $obj->_days($ranges);
  161. }
  162. }
  163. class DateRanges_debug extends DateRanges {
  164. }