PHP:返回两个日期之间的所有日期数组

295

期望输入:

getDatesFromRange( '2010-10-01', '2010-10-05' );

期望输出:

Array( '2010-10-01', '2010-10-02', '2010-10-03', '2010-10-04', '2010-10-05' )
26个回答

567

你还可以看一下DatePeriod类:

$period = new DatePeriod(
     new DateTime('2010-10-01'),
     new DateInterval('P1D'),
     new DateTime('2010-10-05')
);

这应该会给你一个包含 DateTime 对象的数组。

要进行迭代。

foreach ($period as $key => $value) {
    //$value->format('Y-m-d')       
}

24
请注意,这并不能直接得到一个 DateTime 对象的数组;它只能得到一个实现了 Traversable 接口的 DatePeriod 对象。如果你只是想使用 foreach 遍历一系列日期,那么很好;但否则,你仍然需要使用 foreach 来构建自己的数组。 - Aaron Adams
65
事实证明,您可以使用 iterator_to_array($period) 来获取数组。此函数在所有支持 DateInterval 的 PHP 版本中都可用。 - Aaron Adams
38
foreach( $period as $date) { $array[] = $date->format('Y-m-d'); } 将生成所需的数组。 - Naveed
20
尽管这是一段不错的代码,“DatePeriod”类却排除了结束日期。$period 不会包含 '2010-10-05'。您可以使用第四个参数 DatePeriod::EXCLUDE_START_DATE 来排除开始日期。最后,它甚至没有返回字符串数组(正如所要求的)。回答很好,但回答的是错误的问题。 - Maxime
23
为了包括结束日期,您需要添加时间,例如:new DateTime('2010-10-05 23:59:59') - James F
显示剩余7条评论

162
function createDateRangeArray($strDateFrom,$strDateTo)
{
    // takes two dates formatted as YYYY-MM-DD and creates an
    // inclusive array of the dates between the from and to dates.

    // could test validity of dates here but I'm already doing
    // that in the main script

    $aryRange = [];

    $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));
        }
    }
    return $aryRange;
}

来源:http://boonedocks.net/mike/archives/137-Creating-a-Date-Range-Array-with-PHP.html


1
使其递归 if ($iDateTo>=$iDateFrom){... }else{ return $this->createDateRangeArray($strDateTo, $strDateFrom); } - levye
@Chris,你能提供一个在线演示来证明这个答案在处理与夏令时相关的日期时失败了吗? - mickmackusa
如果您评论掉时区设置函数,输出就会正确。我不知道为什么。 - Kelvin
@Kelvin Gee,你发现得真好!我想确保我的代码不会出问题(https://dev59.com/W2855IYBdhLWcg3wfUZM#42558584)——没问题。由于这个错误,这个答案似乎不应该有绿色的勾选标记。 - mickmackusa
1
修复跨越夏令时的额外一天的方法是,在推送之前再次检查 if($iDateFrom<=$iDateTo { /* push to $aryRange */ }。这是因为在我测试过的某些服务器上,小时数的增加会将 $iDateFrom 移动到 $iDateTo 之后。 - FiddlingAway
显示剩余4条评论

143

这非常非常灵活。

/**
 * Creating date collection between two dates
 *
 * <code>
 * <?php
 * # Example 1
 * date_range("2014-01-01", "2014-01-20", "+1 day", "m/d/Y");
 *
 * # Example 2. you can use even time
 * date_range("01:00:00", "23:00:00", "+1 hour", "H:i:s");
 * </code>
 *
 * @author Ali OYGUR <alioygur@gmail.com>
 * @param string since any date, time or datetime format
 * @param string until any date, time or datetime format
 * @param string step
 * @param string date of output format
 * @return array
 */
function date_range($first, $last, $step = '+1 day', $output_format = 'd/m/Y' ) {

    $dates = array();
    $current = strtotime($first);
    $last = strtotime($last);

    while( $current <= $last ) {

        $dates[] = date($output_format, $current);
        $current = strtotime($step, $current);
    }

    return $dates;
}

1
注意参数format仅用于输出。这意味着您的输入日期可能与作为参数使用的format没有关系。 - Maxime
@Maxime,你是对的。我会更新文档。 - alioygur
3
绝对简单而出色!我很想看到这个解决方案与面向对象的解决方案之间的网络请求内存和速度比较。 - Dario Fumagalli
1
要小心strtotime()函数。虽然它在普通年份中运行正常,但在我的情况下,在闰年计算时它没有包括最后一个日期。我认为这与二月份的额外一天有关。 - Alex
此示例不支持以下场景:
  1. $first = '2020-11-26'; $last = '2020-11-26';
  2. $first = '2020-11-26 13:00:00'; $last = '2020-11-26 14:00:00';
- М.Б.
非常好用...这应该是推荐的答案。 - undefined

45

请注意,ViNce提供的答案没有包括期间的结束日期。

如果您正在使用PHP 5.3+版本,则最好使用此函数:

/**
 * Generate an array of string dates between 2 dates
 *
 * @param string $start Start date
 * @param string $end End date
 * @param string $format Output format (Default: Y-m-d)
 *
 * @return array
 */
function getDatesFromRange($start, $end, $format = 'Y-m-d') {
    $array = array();
    $interval = new DateInterval('P1D');

    $realEnd = new DateTime($end);
    $realEnd->add($interval);

    $period = new DatePeriod(new DateTime($start), $interval, $realEnd);

    foreach($period as $date) { 
        $array[] = $date->format($format); 
    }

    return $array;
}

然后,您将按预期调用该函数:

getDatesFromRange('2010-10-01', '2010-10-05');

运行演示

关于DatePeriod类的注意事项:您可以使用DatePeriod的第四个参数来排除开始日期(DatePeriod::EXCLUDE_START_DATE),但目前无法包括结束日期。


1
请将以下与编程有关的内容从英语翻译成中文。只返回翻译后的文本:请勿编辑其他答案以促进您的答案。可以在答案下方添加评论。 - Chronial
4
我使用了社区点赞的提供的代码,并花时间进行调试和找出为什么它不能工作的原因。目标是避免其他用户花费时间在一个“漂亮的代码片段”上,而该代码片段实际上并不起作用,并提供真正完整的答案。很遗憾我的编辑在被点赞的答案中被删除了,这误导了社区,因为人们在未经过测试就点赞。这不是关于我的答案,而是关于那个答案。无论如何... - Maxime
如果我冒犯了您,我很抱歉。您想提高答案的质量是非常好的。但在SO上,编辑答案以链接到另一个答案并不是正确的方式。不过,您随时可以编辑他人的答案以改进它。 - Chronial
你没有冒犯我。我认为我们有着相同的目标:帮助他人获得答案。我曾经认为这是最好的方式,但是 Stack Overflow 并不这样认为。我觉得修改别人写的整个答案(因为这不是一个简单的编辑)让我感到不舒服。这里没有任何不愉快的情绪。我们没问题! :-) - Maxime
这很棒,但当我修改以允许您传递间隔时(例如月份),由于$realEnd->Add,它会返回额外的一个月,我希望有一种可重复使用的方法来使其成为通用日期范围函数。 - M H

22

简单而优雅:

    $period = new DatePeriod(new DateTime('2015-01-01'), new DateInterval('P1D'), new DateTime('2015-01-15 +1 day'));
    foreach ($period as $date) {
        $dates[] = $date->format("Y-m-d");
    }

    //ONLY SHOWING
    echo '<pre>';
    var_dump($dates);
    echo '</pre>';
    exit();

19
  function GetDays($sStartDate, $sEndDate){  
      // Firstly, format the provided dates.  
      // This function works best with YYYY-MM-DD  
      // but other date formats will work thanks  
      // to strtotime().  
      $sStartDate = gmdate("Y-m-d", strtotime($sStartDate));  
      $sEndDate = gmdate("Y-m-d", strtotime($sEndDate));  

      // Start the variable off with the start date  
     $aDays[] = $sStartDate;  

     // Set a 'temp' variable, sCurrentDate, with  
     // the start date - before beginning the loop  
     $sCurrentDate = $sStartDate;  

     // While the current date is less than the end date  
     while($sCurrentDate < $sEndDate){  
       // Add a day to the current date  
       $sCurrentDate = gmdate("Y-m-d", strtotime("+1 day", strtotime($sCurrentDate)));  

       // Add this new day to the aDays array  
       $aDays[] = $sCurrentDate;  
     }  

     // Once the loop has finished, return the  
     // array of days.  
     return $aDays;  
   }  

使用方式

GetDays('2007-01-01', '2007-01-31'); 

16

必须添加 $end->modify('+1 day') 以包括区间的最后一天,例如,如果不使用 modify() 方法,则一月将有 31 天而不是 30 天。 此版本代码将包括区间的最后一天:


此版本代码将包括区间的最后一天:

$begin = new DateTime( '2018-08-01' );
$end = new DateTime( '2018-08-31' );
$end = $end->modify( '+1 day' ); 

$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval ,$end);

foreach($daterange as $date){
    echo $date->format("Ymd") . "<br>";
}

PHP 文档链接


13

这段代码简洁明了,适用于PHP4及以上版本。

function getDatesFromRange($start, $end){
    $dates = array($start);
    while(end($dates) < $end){
        $dates[] = date('Y-m-d', strtotime(end($dates).' +1 day'));
    }
    return $dates;
}

1
Peavey,不,它不会... - drolex
1
这个方案非常好,只要你不介意它最终会运行太久并导致脚本一直运行直到达到 max_execution_time。 - dctucker
1
dctucker,听起来你是在创造一个问题。 - drolex
你一定做错了什么。多个人说它是有效的。 - drolex
我喜欢这个,因为它干净简洁,不需要使用类。不知道它是否比使用DatePeriod类的最佳答案更快? - drooh
显示剩余2条评论

11

这是一个简短的函数,适用于 PHP 5.3 及以上版本。可以接受任何 strtotime 可理解的日期格式作为第三个可选参数。如果结束时间小于开始时间,则自动反转方向。

function getDatesFromRange($start, $end, $format='Y-m-d') {
    return array_map(function($timestamp) use($format) {
        return date($format, $timestamp);
    },
    range(strtotime($start) + ($start < $end ? 4000 : 8000), strtotime($end) + ($start < $end ? 8000 : 4000), 86400));
}

测试:

date_default_timezone_set('Europe/Berlin');
print_r(getDatesFromRange( '2016-7-28','2016-8-2' ));
print_r(getDatesFromRange( '2016-8-2','2016-7-28' ));
print_r(getDatesFromRange( '2016-10-28','2016-11-2' ));
print_r(getDatesFromRange( '2016-11-2','2016-10-28' ));
print_r(getDatesFromRange( '2016-4-2','2016-3-25' ));
print_r(getDatesFromRange( '2016-3-25','2016-4-2' ));
print_r(getDatesFromRange( '2016-8-2','2016-7-25' ));
print_r(getDatesFromRange( '2016-7-25','2016-8-2' ));

输出:

Array ( [0] => 2016-07-28 [1] => 2016-07-29 [2] => 2016-07-30 [3] => 2016-07-31 [4] => 2016-08-01 [5] => 2016-08-02 ) 
Array ( [0] => 2016-08-02 [1] => 2016-08-01 [2] => 2016-07-31 [3] => 2016-07-30 [4] => 2016-07-29 [5] => 2016-07-28 ) 
Array ( [0] => 2016-10-28 [1] => 2016-10-29 [2] => 2016-10-30 [3] => 2016-10-31 [4] => 2016-11-01 [5] => 2016-11-02 ) 
Array ( [0] => 2016-11-02 [1] => 2016-11-01 [2] => 2016-10-31 [3] => 2016-10-30 [4] => 2016-10-29 [5] => 2016-10-28 ) 
Array ( [0] => 2016-04-02 [1] => 2016-04-01 [2] => 2016-03-31 [3] => 2016-03-30 [4] => 2016-03-29 [5] => 2016-03-28 [6] => 2016-03-27 [7] => 2016-03-26 [8] => 2016-03-25 ) 
Array ( [0] => 2016-03-25 [1] => 2016-03-26 [2] => 2016-03-27 [3] => 2016-03-28 [4] => 2016-03-29 [5] => 2016-03-30 [6] => 2016-03-31 [7] => 2016-04-01 [8] => 2016-04-02 ) 
Array ( [0] => 2016-08-02 [1] => 2016-08-01 [2] => 2016-07-31 [3] => 2016-07-30 [4] => 2016-07-29 [5] => 2016-07-28 [6] => 2016-07-27 [7] => 2016-07-26 [8] => 2016-07-25 ) 
Array ( [0] => 2016-07-25 [1] => 2016-07-26 [2] => 2016-07-27 [3] => 2016-07-28 [4] => 2016-07-29 [5] => 2016-07-30 [6] => 2016-07-31 [7] => 2016-08-01 [8] => 2016-08-02 )

8
以下是一段函数代码,可以返回两个方向上的日期范围,适用于PHP >=5.2.2版本:
function createRange($start, $end, $format = 'Y-m-d') {
    $start  = new DateTime($start);
    $end    = new DateTime($end);
    $invert = $start > $end;

    $dates = array();
    $dates[] = $start->format($format);
    while ($start != $end) {
        $start->modify(($invert ? '-' : '+') . '1 day');
        $dates[] = $start->format($format);
    }
    return $dates;
}

使用示例:

print_r(createRange('2010-10-01', '2010-10-05'));
/*Array
(
    [0] => 2010-10-01
    [1] => 2010-10-02
    [2] => 2010-10-03
    [3] => 2010-10-04
    [4] => 2010-10-05
)*/

print_r(createRange('2010-10-05', '2010-10-01', 'j M Y'));
/*Array
(
    [0] => 5 Oct 2010
    [1] => 4 Oct 2010
    [2] => 3 Oct 2010
    [3] => 2 Oct 2010
    [4] => 1 Oct 2010
)*/

演示


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接