创建有例外的日期范围


Create range of date with exception

有两个日期数组(mm/dd/yyyy)

array(2) {
  ["useless_range"]=>
  array(4) {
    [0]=>
    string(10) "08/15/2014"
    [1]=>
    string(10) "08/30/2014"
    [2]=>
    string(10) "09/10/2014"
    [3]=>
    string(10) "09/20/2014"
  }
  ["range"]=>
  array(2) {
    [0]=>
    string(10) "08/01/2014"
    [1]=>
    string(10) "10/31/2014"
  }
}

考虑数组range_useless是假日、罢工或类似的东西。它已经排序了。如何从给定的数组范围中获得有用的范围?

对于该示例,我希望范围为

'08/01/2014 to 08/14/2014 **and** 08/31/2014 to 09/09/2014 **and** 09/21/2014 to 10/31/2014'

我的第一个解决方案是在范围之间创建一个所有日期的数组,然后使用 in_array 创建完整的字符串循环,但我认为可能存在更好的解决方案

在所有范围之间创建所有日期的代码

    $strDateFrom = '2014/08/15';
    $strDateTo = '2014/08/30';
    // takes two dates formatted as YYYY-MM-DD and creates an
  // inclusive array of the dates between the from and to dates.
  $aryRange=array();
  $iDateFrom=mktime(1,0,0,substr($strDateFrom,5,2),     substr($strDateFrom,8,2),substr($strDateFrom,0,4));
  $iDateTo=mktime(1,0,0,substr($strDateTo,5,2),     substr($strDateTo,8,2),substr($strDateTo,0,4));
  if ($iDateTo>=$iDateFrom) {
    array_push($aryRange,date('Y-m-d',$iDateFrom)); // first entry
    while ($iDateFrom<$iDateTo) {
      $iDateFrom+=86400; // add 24 hours
      array_push($aryRange,date('Y-m-d',$iDateFrom));
    }
  }
  print_r($aryRange);
有趣的

挑战:)!所以我尝试了一些东西:

$myDates = array(
  "useless_range" => array(
    "01/08/2015",
    "01/15/2015",
    "09/10/2015",
    "09/20/2015"
  ),
  "range" => array(
    "08/01/2015",
    "10/31/2015"
  )
);

// results of strotime
$dateStart = 1420066800; //01/01/2015
$dateTo = 1422658800; // 31/01/2015

$tab = $myDates['useless_range'];
$len = count($tab);
$result = array();
$previous = $dateStart;
for($i = 0; $i < $len; $i++){
  $position = $tab[$i]; // we get the current element
  $iPosition = strtotime($position); // strotime it
  if($previous < $iPosition && $iPosition < $dateTo){ // if the useless date is in the range
    if($i % 2 == 0){ //if start of range
      $result[] = array("start" => date("Y-m-d", $previous + 3600*24), "end" => date("Y-m-d", $iPosition));
    } 
    $previous = $iPosition;
  }
  else { // the date is out of range, we finish the result and leave the loop
    $result[] = array("start" => date("Y-m-d", $previous), "end" => date("Y-m-d", $dateTo + 3600*24));
    break; //end of search
  }
}
print_r($result);

输出结果:

Array
(
    [0] => Array
        (
            [start] => 2015-01-01
            [end] => 2015-01-08
        )
    [1] => Array
        (
            [start] => 2015-01-15
            [end] => 2015-01-31
        )
)

现在创建句子:

$sentences = array();
foreach($result as $elem){
    $sentences[] = $elem['start']." to ".$elem['end'];
}
echo implode(' **and** ', $sentences);

好的,这与你所拥有的没有太大区别,但这是另一种观点,我使用的是 strtotime() 函数而不是至少看起来更好的mktime()

我假设输入已经过验证,并且忽略了极端情况,例如当所需范围/期间的开始日期在第一个"无用"范围内时,或者结束日期在"无用"范围内,但这可以很容易地验证如有必要。

我只是想让你分享我将如何解决这个问题。

这是我的代码:

<?php
    $uselessRanges = [ "08/15/2014", "08/30/2014", "09/10/2014", "09/20/2014" ];
    $range = [ "08/01/2014", "10/31/2014" ];
    // Convert to timestamps for easier manipulation
    $rangeTs = array_map( 'strtotime', $range );
    $uselessRangesTs = array_map( 'strtotime', $uselessRanges );
    // Let the first date be the start of the whole range
    $finalResult = [ array_shift( $rangeTs ) ];
    /**
     * Assuming that the useless ranges array is of the following format:
     * $useless = [ 'start_useless_range_1', 'end_useless_range_1', 'start_useless_range_2', 'end_useless_range_2', ... ]
     *
     * For each of the useless range entries we decide whether to take the previous or the next day based on the parity of the index
     */
    for( $i = 0; $i < count( $uselessRangesTs); $i++ ){
        // Whether we should take the previous or the next day
        $diff = $i % 2 === 0 ? -86400 : 86400;
        $finalResult[] = $uselessRangesTs[$i] + $diff;
    }
    // Finally add the end of the whole range
    $finalResult[] = array_shift( $rangeTs );
    foreach( $finalResult as $date ){
        echo date('m/d/Y', $date) . '<br />';
    }

假设您只需要生成一个字符串,并且日期已经过验证并按顺序排列,我不会打扰日期函数。以下是我将如何做到这一点的快速示例:

$dates = array(
  "useless_range" => array(
    "08/15/2014",
    "08/30/2014",
    "09/10/2014",
    "09/20/2014"
  ),
  "range" => array(
    "08/01/2015",
    "10/31/2015"
  )
);
$str = array();
for ( $i = 0; $i < sizeof($dates['useless_range']); $i += 2 ) {
  $str[] = sprintf('%s and %s', $dates['useless_range'][$i], $dates['useless_range'][$i+1]);
}
array_unshift($str, $dates['range'][0]);
$str[] = $dates['range'][1];
$str = implode(' to ',$str);
echo $str;
// 08/01/2015 to 08/15/2014 and 08/30/2014 to 09/10/2014 and 09/20/2014 to 10/31/2015

解释:

一般的想法是构建一个句子,所以我不会为日期或范围或任何东西而烦恼。想象一下需要拼凑在一起的单词。最终结果需要是:

"范围 0useless_range0,useless_range1useless_range2,useless_range3范围 1"

这可以像这样分解:

[范围 0] 到 [useless_range0useless_range1] 到 [useless_range2useless_range3] 到 [范围 1]

这可以非常容易地通过使用"to"作为分隔符的内爆来实现。我们只需要生成一个数组,其中 range0 作为第一个元素,range1 作为最后一个元素,以及介于两者之间的每段句子(两个连续的日期 + " 和 ")。这是一个简单的循环 + array_shift/取消移位。

注意:如果您的日期未经过验证,或者如果您需要检查重叠,或者类似的事情,那么您很可能必须像现在一样构建一系列日期。