多个日期的回声持续时间(例如 8 月 25 日至 27 日,9 月 3 日)

Echo durations from multiple dates (ex. Aug 25-27, Sep 3)

我正在处理可能发生在一天(例如 5/20)或多天(例如 8/25、8/26、8/27、9/3)的事件。

例如,假设在 8 月 25 日、8 月 26 日、8 月 27 日和 9 月 3 日举行了为期 4 天的活动,我想附和一下:

Aug 25-27, Sep 3

我想要代码:

仅使用 date() 格式的单日活动很容易做到这一点,但是否可以在必要时使用多个日期智能地生成像这样的格式?

我创建了一个函数,它应该根据 DateTime 对象的数组输出所需的字符串。我放置了一些内联注释以指示函数中给定时间发生的事情。

function produceDateString(array $dates): string
{
    // sort the dates
    sort($dates);

    // create an array of arrays that contain ranges of consecutive days
    $ranges = [];
    $currentRange = [];
    foreach ($dates as $date) {
        if(empty($currentRange) || consecutive(end($currentRange), $date)) {
            $currentRange[] = $date;
        } else {
            $ranges[] = $currentRange;
            $currentRange = [$date];
        }
    }
    $ranges[] = $currentRange;

    // create the output string
    $output = '';
    $previous = null;
    foreach ($ranges as $range) {
        // add a comma between each range
        if (!empty($output)) {
            $output .= ', ';
        }

        // the long format should be used on the first occurrence of the loop
        // or when the month of first date in the range doesn't match 
        // the month of the last date in the previous range
        $format = $previous === null || end($previous)->format('m') !== reset($range)->format('m')
            ? 'M. j'
            : 'j';

        // the output differes when there are 1 or multiple dates in a range
        if (count($range) > 1) {
            // the output differs when the end and start are in the sane month
            $output .= sameMonth(reset($range), end($range))
                ? reset($range)->format($format).'-'.end($range)->format('j')
                : reset($range)->format('M. j').'-'.end($range)->format('M. j');
        } else {
            $output .= reset($range)->format($format);
        }

        $previous = $range;
    }

    return $output;
}

function consecutive(DateTime $t1, DateTime $t2): bool
{
    $t1->setTime(0, 0, 0, 0);
    $t2->setTime(0, 0, 0, 0);
    return(abs($t2->getTimestamp() - $t1->getTimestamp()) < 87000);
}

function sameMonth(DateTime $t1, DateTime $t2): bool
{
    return $t1->format('Y-m') === $t2->format('Y-m');
}

我做了一个小 3v4l 来向您展示它是如何工作的。如果您对它的工作原理有任何疑问,请不要犹豫。