将连续逗号分隔的天数转换为带连字符的日期范围


Convert consecutive comma-separated days into hyphenated day ranges

我正在尝试将单个日期的表达式压缩为较短的表达式,包括连字符分隔的范围。

例子:


  • mon,tue,wed,thu,fri,sat
    mon-sat

  • mon,tue,wed,fri,satmon-wed,fri-sat

我的编码尝试:

function dayrange($days){
    $days = explode(",", str_replace(" ","",$days));
    return reset($days) . "-" . end($days);
}

如何缩短多天表达式,以便将连续天数合并为天数范围?

基本上,我会通过以下方式解决这个问题:

  1. 将天数转换为相应的数值
  2. 将数字数组转换为带范围的字符串
  3. 将字符串中的数字转换回星期几

我写了一些代码来做到这一点:

/**
 * Convert an array of numbers to a string containing ranges and single values
 * @param array $numbers an array of numbers
 * @return string
 */
function compressNumbers($numbers) {
    $result = array();
    sort($numbers);
    $previousValue = reset($numbers);
    $startValue = $previousValue;
    foreach ($numbers as $value) {
        if ($value > $previousValue + 1) {
            if ($startValue == $previousValue) {
                $result[] = $startValue;
            } else {        
                $result[] = $startValue . '-' . $previousValue;
            }
            $startValue = $value;
        }
        $previousValue = $value;
    }
    if ($startValue == $previousValue) {
        $result[] = $startValue;
    } else {        
        $result[] = $startValue . '-' . $previousValue;
    }
    return implode(',', $result);
}
/*
 * Creates an array with values the three letter representation for days of the 
 * week and keys the corresponding numeric representation.
 *
 * @return array
 */
function createLookupNumberToDay() {
    $date = strtotime('now');
    $lookup = array();
    for ($i = 1; $i <= 7; $i++) {
        $lookup[date('w', $date)] = date('D', $date);
        $date = strtotime('+1 day', $date);
    }
    return $lookup;
}
/*
 * Converts a string listing days separated by commas into 
 * an array with values the numeric value for the corresponding
 * day of the week.
 *
 * @param string $days
 * @return array
 */
function convertDaysToNumbers($days) {
    $result = array();
    $daysArray = explode(",", str_replace(" ","",$days));
    foreach ($daysArray as $day) {
        $result[] = date('w', strtotime($day));
    }
    return $result;
}
/*
 * Converts the numbers in a string to the corresponding 3-letter day of the
 * week abbreviation.
 *
 * @param string $string
 * @return string
 */
function convertNumbersToDays($string) {
    $lookup = createLookupNumberToDay();
    return str_replace(array_keys($lookup), $lookup, $string);
}
function convert($string) {
    return (convertNumbersToDays(compressNumbers(convertDaysToNumbers($string))));
}
echo convert('mon,tue,wed,thu,fri,sat');
echo '<br />';
echo convert('mon,tue,wed,sat');
echo '<br />';

还没有测试过这个,但应该给你一个好的开始。它也处理周包装。

function dayrange($days){
    $wdays = array("mon","tue","wed","thu","fri","sat","sun");
    $indays = explode(",", str_replace(" ","",$days)); // expand the list to an array

    $retstr = array_shift($indays); // get the first date
    $curpos = array_search($retstr, $wdays);  // current position in the wdays array
    $intv = 0;    // interval between days to avoid mon-tue like output
    foreach($indays as $d) {
       if($d == $wdays[$curpos]) {
          $curpos = ($curpos++) % 7; // this will take care of wrapping.
          $intv++;
       } else {
           $retstr.= ($intv > 1 ? "-".$d:",".$d); // use appropriate join
           $intv = 0; // reset interval
       }
    }
    if($intv > 0) {   // if anything was left deal with the end.
        $retstr.= ($intv > 1 ? "-".$d:",".$d);
    } else {
        $retstr.= ",".$d;
    }
    return ($retstr);
}
  1. 将查找定义为常量,以便轻松确定每天的顺序位置。

  2. 在逗号上分解字符串并迭代日期值。

  3. 如果结果字符串为空,则添加不带分隔符/粘附符的日期。

  4. 如果日期是连续定位的日期,则可能会删除追加的昨天子字符串(如果使用连字符附加),然后附加连字符和日期。

  5. 如果日期不是连续定位的日期,则附加逗号和日期。

代码:(演示)

define('DAYS', array_flip(['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']));
function condenseDays(string $days): string
{
    $result = '';
    foreach (explode(',', $days) as $day) {
        if (!$result) {
            $result .= $day;
        } elseif (DAYS[$day] === DAYS[$yesterday] + 1) {
            $result = str_replace("-$yesterday", '', $result) . "-$day";
        } else {
            $result .= ",$day";
        }
        $yesterday = $day;
    }
    return $result;
}
echo condenseDays('mon,tue,wed,thu,fri,sat') . "'n";
echo condenseDays('tue,thu,fri,sun') . "'n";
echo condenseDays('mon,tue,wed,fri,sat,sun') . "'n";
echo condenseDays('mon,thu,sun') . "'n";
echo condenseDays('tue,wed,fri,sat') . "'n";
echo condenseDays('mon,wed,fri,sun') . "'n";
echo condenseDays('mon,tue,thu,fri,sat,sun');

输出:

mon-sat
tue,thu-fri,sun
mon-wed,fri-sun
mon,thu,sun
tue-wed,fri-sat
mon,wed,fri,sun
mon-tue,thu-sun
<小时 />

或者,如果您更愿意使用蛮力方法,您可以将所有相邻日期的逗号替换为连字符,然后使用正则表达式删除连续多天的"胆量"。

代码:(演示)

define(
    'PAIRS',
    [
        [
            'mon,tue',
            'tue,wed',
            'wed,thu',
            'thu,fri',
            'fri,sat',
            'sat,sun'
        ],
        [
            'mon-tue',
            'tue-wed',
            'wed-thu',
            'thu-fri',
            'fri-sat',
            'sat-sun'
        ]
    ]
);
function condenseDays(string $days): string
{
    return preg_replace(
               '/-'K[^,]+-/',
               '',
               str_replace(PAIRS[0], PAIRS[1], $days)
           );
}
<小时 />

最偷偷摸摸/最不容易理解的版本,其中值得范围的逗号由相邻的字母而不是 3 个字母的日期标识。

代码:(演示)

function condenseDays(string $days): string
{
    return preg_replace(
               '/-'K[^,]+-/',
               '',
               str_replace(
                   ['n,t', 'e,w', 'd,t', 'u,f', 'i,s', 't,s'],
                   ['n-t', 'e-w', 'd-t', 'u-f', 'i-s', 't-s'],
                   $days
               )
           );
}