如何将两个日期缩小到一个日期范围内?

How can I shrink two dates in to one date range?

我需要显示如下日期范围:

我以 PHP 日期对象的形式提供了开始日期和结束日期。有没有一种方法可以解析那些 PHP 以根据需要输出?


编辑:我认为你们误解了我要做什么。

我有一个输出开始日期和结束日期的 WordPress 事件插件。如果事件的开始日期为 2015 年 5 月 4 日,结束日期为 2015 年 5 月 7 日,我需要显示:

<h4>Event Title</h4>
<p>When: <time>May 4-7, 2015</time></p>

我不需要显示给定日期之间的每个日期;这很容易让我自己弄清楚。


编辑 2: 好吧,只是为了确保我完全清楚:

我正在使用 Tri.be 事件插件来允许用户创建事件日历。在这个 WordPress 站点的首页上,我需要显示即将发生的事件的列表。每个事件应该这样输出:

这个事件应该这样输出:

<article>
    <h4><a href="/path/to/event/">Systematic Implementation of Advance Care Planning in a Diverse City</a></h4>
    <p>
        <a href="/path/to/event/">
            Aging in America Conference - Chicago, IL<br />
            <time datetime="2015-03-23">March 23-27, 2015</time>
        </a>
    </p>
</article>

我遇到的问题是 <time> 元素。下面的答案似乎表明我想以这种格式输出时间:

<time datetime="2015-03-23">March 23, 2015 - March 27, 2015</time>

这是不正确的。如果是这样的话,我可以很容易地自己输出这个。正确的格式是:

<time datetime="2015-03-23">March 23-27, 2015</time>

因此,问题是:"How can I shrink two dates in to one date range?"

这将是最简单的解决方案:

$dtStart = new DateTime('2015-05-05');
$dtEnd   = new DateTime('2015-05-07');

while ($dtStart <= $dtEnd) {
    echo $dtStart->format('Y-m-d') . "\n";
    $dtStart->modify('+1 day');
}

// Output:
// 2015-05-05
// 2015-05-06
// 2015-05-07

$dtStart = new DateTime('2015-12-30');
$dtEnd   = new DateTime('2016-01-01');

while ($dtStart <= $dtEnd) {
    echo $dtStart->format('Y-m-d') . "\n";
    $dtStart->modify('+1 day');
}

// Output:
// 2015-12-30
// 2015-12-31
// 2016-01-01

编辑 2:

我已经用一些日期格式更新了函数。

输出将是:

Event title

When: Feb 28, 2010 - Mar 2, 2010

Event title

When: Mar 4, 2010 - Mar 5, 2010

Event title

When: Dec 31, 2010 - Jan 5, 2011

编辑:

我找到了这个(通过搜索)--> Check for consecutive dates within a set and return as range

下面的代码不是我的。这是来自 @Darragh

的旧 post

I have made some minor changes to the code to fit the OP's question.

    // assuming a chronologically
    // ordered array of DateTime objects 

    $dates = array(         
          new DateTime('2010-02-28'), 
          new DateTime('2010-03-01'), 
          new DateTime('2010-03-02'), 
          new DateTime('2010-03-04'), 
          new DateTime('2010-03-05'), 
          new DateTime('2010-12-31'),
          new DateTime('2011-01-01'), 
          new DateTime('2011-01-02'), 
          new DateTime('2011-01-03'), 
          new DateTime('2011-01-04'), 
          new DateTime('2011-01-05'),
    );

    // process the array

    $lastDate = null;
    $ranges = array();
    $currentRange = array();

    foreach ($dates as $date) {    

        if (null === $lastDate) {
            $currentRange[] = $date;
        } else {

            // get the DateInterval object
            $interval = $date->diff($lastDate);

            // DateInterval has properties for 
            // days, weeks. months etc. You should 
            // implement some more robust conditions here to 
            // make sure all you're not getting false matches
            // for diffs like a month and a day, a year and 
            // a day and so on...

            if ($interval->days === 1) {
                // add this date to the current range
                $currentRange[] = $date;    
            } else {
                // store the old range and start anew
                $ranges[] = $currentRange;
                $currentRange = array($date);
            }
        }

        // end of iteration... 
        // this date is now the last date     
        $lastDate = $date;
    }

    // messy... 
    $ranges[] = $currentRange;

    // print dates

        foreach ($ranges as $range) {

    // there'll always be one array element, so 
    // shift that off and create a string from the date object 
    $startDate = array_shift($range);
    $str = "<h4>Event title</h4>";
    $str .= "<p>When: ";
    $str .= "<time>"; 
    $str .= sprintf('%s', $startDate->format('M j, Y'));
    // if there are still elements in $range
    // then this is a range. pop off the last 
    // element, do the same as above and concatenate
    if (count($range)) {
        $endDate = array_pop($range);
        $str .= sprintf(' - %s', $endDate->format('M j, Y'));
        $str .= "</time></p>";
    }

    echo "<p>$str</p>";
}

旧答案(基于原始问题)。

$start_date = new DateTime( '2015-05-01' );
$end_date = new DateTime( '2015-05-04' );
$end_date = $end_date->modify( '+1 day' ); 

$interval = new DateInterval('P1D');
$daterange = new DatePeriod($start_date, $interval ,$end_date);

foreach($daterange as $date){
    echo $date->format("M d, Y") . "<br>";
}

// Output
// May 01, 2015
// May 02, 2015
// May 03, 2015
// May 04, 2015
<article>
    <h4><a href="/path/to/event/">Systematic Implementation of Advance Care Planning in a Diverse City</a></h4>
    <p>
        <a href="/path/to/event/">
            Aging in America Conference - Chicago, IL<br />
            <time datetime="2015-03-23/2015-03-27">March 23&ndash;27 2015</time>
        </a>
    </p>
</article>

编辑

使用上述 PHP 代码的完整示例。

Output

Mar 23, 2015–Mar 27, 2015

    // assuming a chronologically
    // ordered array of DateTime objects 

    $dates = array(         
          new DateTime('2015-03-23'), 
          new DateTime('2015-03-24'), 
          new DateTime('2015-03-25'), 
          new DateTime('2015-03-26'), 
          new DateTime('2015-03-27')          
    );

    // process the array

    $lastDate = null;
    $ranges = array();
    $currentRange = array();

    foreach ($dates as $date) {    

        if (null === $lastDate) {
            $currentRange[] = $date;
        } else {

            // get the DateInterval object
            $interval = $date->diff($lastDate);

            // DateInterval has properties for 
            // days, weeks. months etc. You should 
            // implement some more robust conditions here to 
            // make sure all you're not getting false matches
            // for diffs like a month and a day, a year and 
            // a day and so on...

            if ($interval->days === 1) {
                // add this date to the current range
                $currentRange[] = $date;    
            } else {
                // store the old range and start anew
                $ranges[] = $currentRange;
                $currentRange = array($date);
            }
        }

        // end of iteration... 
        // this date is now the last date     
        $lastDate = $date;
    }

    // messy... 
    $ranges[] = $currentRange;

    // print dates

    foreach ($ranges as $range) {

    // there'll always be one array element, so 
    // shift that off and create a string from the date object 
    $startDate = array_shift($range);
    $str = "<h4>Event title</h4>";
    $str .= "<p>When: ";
    $str .= "<time>"; 
    $str .= sprintf('%s', $startDate->format('M j, Y'));
    // if there are still elements in $range
    // then this is a range. pop off the last 
    // element, do the same as above and concatenate
    if (count($range)) {
        $endDate = array_pop($range);
        $str .= sprintf(' - %s', $endDate->format('M j, Y'));
        $str .= "</time></p>";
    }

}

echo "<time datetime=".$startDate->format('M j, Y')."/".$endDate->format('M j, Y').">".$startDate->format('M j, Y')."&ndash;".$endDate->format('M j, Y')."</time>";

//OUTPUT
// Mar 23, 2015–Mar 27, 2015