|
@@ -0,0 +1,185 @@
|
|
|
|
|
+<?php
|
|
|
|
|
+Class DateRanges {
|
|
|
|
|
+
|
|
|
|
|
+ private $dateFormat = "Y-m-d";
|
|
|
|
|
+ private $rangeSeparator = ";";
|
|
|
|
|
+ private $dateSeparator = ":";
|
|
|
|
|
+
|
|
|
|
|
+ private function strToArr($str) {
|
|
|
|
|
+ return array_map(function ($range) {
|
|
|
|
|
+ $_range = array_combine(['from', 'to'], array_map(function ($date) {
|
|
|
|
|
+ if (!$date) return '';
|
|
|
|
|
+ return DateTime::createFromFormat('!' . $this->dateFormat, $date)->getTimestamp();
|
|
|
|
|
+ }, explode($this->dateSeparator, $range)));
|
|
|
|
|
+ if ($_range['to'] && $_range['to'] < $_range['from']) throw new Exception("Date 'to' before date 'from'");
|
|
|
|
|
+ return $_range;
|
|
|
|
|
+ }, explode($this->rangeSeparator, $str));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function arrToStr($arr) {
|
|
|
|
|
+ return implode($this->rangeSeparator, array_map(function ($range) {
|
|
|
|
|
+ return implode($this->dateSeparator, array_map(function ($date) {
|
|
|
|
|
+ if (!$date) return '';
|
|
|
|
|
+ return date($this->dateFormat, $date);
|
|
|
|
|
+ }, $range));
|
|
|
|
|
+ }, $arr));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static function sort(&$ranges) {
|
|
|
|
|
+ uasort($ranges, function($a, $b) {
|
|
|
|
|
+ if ($a['from'] == $b['from']) {
|
|
|
|
|
+ if ($a['to'] == $b['to']) return 0;
|
|
|
|
|
+ if (!$a['to']) return 1;
|
|
|
|
|
+ if (!$b['to']) return -1;
|
|
|
|
|
+ return ($a['to'] < $b['to']) ? -1 : 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ return ($a['from'] < $b['from']) ? -1 : 1;
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function implodeRanges($ranges) {
|
|
|
|
|
+ if (is_array($ranges)) {
|
|
|
|
|
+ $ranges = implode($this->rangeSeparator, array_filter($ranges, function($range) {
|
|
|
|
|
+ if ($range) return true;
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }));
|
|
|
|
|
+ }
|
|
|
|
|
+ return $ranges;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function _merge($ranges) {
|
|
|
|
|
+ $ranges = $this->implodeRanges($ranges);
|
|
|
|
|
+ if (!$ranges) throw new Exception("DataRanges::_merges - bad ranges");
|
|
|
|
|
+ $ranges = $this->strToArr($ranges);
|
|
|
|
|
+ self::sort($ranges);
|
|
|
|
|
+
|
|
|
|
|
+ $result = [];
|
|
|
|
|
+ foreach ($ranges as $range) {
|
|
|
|
|
+ if (!$result) $result[] = $range;
|
|
|
|
|
+ else {
|
|
|
|
|
+ if (!end($result)['to']) break;
|
|
|
|
|
+ $k = key($result);
|
|
|
|
|
+ if ($range['from'] == $result[$k]['from']) $result[$k]['to'] = $range['to'];
|
|
|
|
|
+ elseif ((!$range['to']) || ($range['to'] > $result[$k]['to'])) {
|
|
|
|
|
+ if ($range['from'] > $result[$k]['to']) $result[] = $range;
|
|
|
|
|
+ else $result[$k]['to'] = $range['to'];
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return $this->arrToStr($result);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static function lt($date1, $date2) {
|
|
|
|
|
+ if (!$date1) return false;
|
|
|
|
|
+ if (!$date2) return true;
|
|
|
|
|
+ return ($date1 < $date2);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static function gt($date1, $date2) {
|
|
|
|
|
+ if (!$date2) return false;
|
|
|
|
|
+ if (!$date1) return true;
|
|
|
|
|
+ return ($date1 > $date2);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static function le($date1, $date2) {
|
|
|
|
|
+ return (!self::gt($date1, $date2));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static function ge($date1, $date2) {
|
|
|
|
|
+ return (!self::lt($date1, $date2));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static function min($dates) {
|
|
|
|
|
+ if ((!is_array($dates)) || (!$dates)) throw new Exception("DateRanges::min - bad arguments");
|
|
|
|
|
+ $min = null;
|
|
|
|
|
+ foreach ($dates as $date) {
|
|
|
|
|
+ if ($min === null) {
|
|
|
|
|
+ $min = $date;
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (self::lt($date, $min)) $min = $date;
|
|
|
|
|
+ }
|
|
|
|
|
+ return $min;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static function max($dates) {
|
|
|
|
|
+ if ((!is_array($dates)) || (!$dates)) throw new Exception("DateRanges::max - bad arguments");
|
|
|
|
|
+ $max = null;
|
|
|
|
|
+ foreach ($dates as $date) {
|
|
|
|
|
+ if ($max === null) {
|
|
|
|
|
+ $max = $date;
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (self::gt($date, $max)) $max = $date;
|
|
|
|
|
+ }
|
|
|
|
|
+ return $max;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function _common($rangesA, $rangesB) {
|
|
|
|
|
+ $rangesA = $this->implodeRanges($rangesA);
|
|
|
|
|
+ $rangesB = $this->implodeRanges($rangesB);
|
|
|
|
|
+ if (!($rangesA && $rangesB)) throw new Exception("DataRanges::_common - bad ranges");
|
|
|
|
|
+ $rangesA = $this->strToArr($rangesA);
|
|
|
|
|
+ $rangesB = $this->strToArr($rangesB);
|
|
|
|
|
+ self::sort($rangesA);
|
|
|
|
|
+ self::sort($rangesB);
|
|
|
|
|
+ $ranges = [];
|
|
|
|
|
+ foreach ($rangesA as $rangeA) {
|
|
|
|
|
+ foreach ($rangesB as $rangeB) {
|
|
|
|
|
+// if ($rangeA['from'] == $rangeB['from'] && $rangeA['to'] == $rangeB['to']) {
|
|
|
|
|
+// $ranges[] = $rangeA;
|
|
|
|
|
+// continue;
|
|
|
|
|
+// }
|
|
|
|
|
+ $range = [
|
|
|
|
|
+ 'from' => self::max([$rangeA['from'], $rangeB['from']]),
|
|
|
|
|
+ 'to' => self::min([$rangeA['to'], $rangeB['to']]),
|
|
|
|
|
+ ];
|
|
|
|
|
+//print_r($range);
|
|
|
|
|
+ if (self::le($range['from'], $range['to'])) { // || ($range['from'] == $range['to'] && ($rangeA['from'] == $rangeB['from'] || $rangeA['to'] == $rangeB['to']))) {
|
|
|
|
|
+ $ranges[] = $range;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!$ranges) return null;
|
|
|
|
|
+ return $this->_merge($this->arrToStr($ranges));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function _days($ranges) {
|
|
|
|
|
+ $ranges = $this->implodeRanges($ranges);
|
|
|
|
|
+ if (!$ranges) throw new Exception("DataRanges::_days - bad ranges ({$ranges})");
|
|
|
|
|
+ $ranges = $this->strToArr($ranges);
|
|
|
|
|
+ $result = (int) 0;
|
|
|
|
|
+ foreach ($ranges as $range) {
|
|
|
|
|
+ if (!$range['from']) throw new Exception("DataRanges::_days - bad date from");
|
|
|
|
|
+ if (!$range['to']) return -1;
|
|
|
|
|
+ if ($range['to'] < $range['from']) throw new Exception("DataRanges::_days - date to before date from");
|
|
|
|
|
+ $result += round(($range['to'] - $range['from']) / 86400);
|
|
|
|
|
+ }
|
|
|
|
|
+ return round($result);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function __construct($dateFormat = null, $rangeSeparator = null, $dateSeparator = null) {
|
|
|
|
|
+ if ($dateFormat !== null) $this->dateFormat = $dateFormat;
|
|
|
|
|
+ if ($rangeSeparator !== null) $this->rangeSeparator = $rangeSeparator;
|
|
|
|
|
+ if ($dateSeparator !== null) $this->dateSeparator = $dateSeparator;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static function merge($ranges, $dateFormat = null, $rangeSeparator = null, $dateSeparator = null) {
|
|
|
|
|
+ $obj = new DateRanges($dateFormat, $rangeSeparator, $dateSeparator);
|
|
|
|
|
+ return $obj->_merge($ranges);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static function common($rangesA, $rangesB, $dateFormat = null, $rangeSeparator = null, $dateSeparator = null) {
|
|
|
|
|
+ $obj = new DateRanges($dateFormat, $rangeSeparator, $dateSeparator);
|
|
|
|
|
+ return $obj->_common($rangesA, $rangesB);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static function days($ranges, $dateFormat = null, $rangeSeparator = null, $dateSeparator = null) {
|
|
|
|
|
+ $obj = new DateRanges($dateFormat, $rangeSeparator, $dateSeparator);
|
|
|
|
|
+ return $obj->_days($ranges);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+class DateRanges_debug extends DateRanges {
|
|
|
|
|
+}
|