PHP 人类日期范围/持续时间格式

问题描述 投票:0回答:4

PHP 制作得非常好,我想知道是否有一个函数可以满足我的需要。

对于持续超过一天的事件,人类的格式化方式很复杂。

例子...

事件一:从 2015-04-20 到 2015-04-22 可以针对人类进行格式化,如下所示:2015 年 4 月 20-22 日

活动二:2015年4月1日至2015年5月31日人类->2015年4月-5月

活动三:2015年4月30日至2015年5月2日人类->2015年4月30日至5月2日

简而言之,不需要重复的事情永远不要重复。尽量用“-”链接。

它必须进行本地化,并且格式可能会根据本地情况而改变。例如,美国人喜欢 MonthName DayNumber Year,但法国人喜欢 DayNumber MonthName Year。

我正计划对这种格式进行编程,但我想知道它是否已经存在:)

php date format
4个回答
6
投票

这有效...

function humanDateRanges($start, $end){
    $startTime=strtotime($start);
    $endTime=strtotime($end);

    if(date('Y',$startTime)!=date('Y',$endTime)){
        echo date('F j, Y',$startTime) . " to " . date('F j, Y',$endTime);
    }else{
        if((date('j',$startTime)==1)&&(date('j',$endTime)==date('t',$endTime))){
            echo date('F',$startTime) . " to " . date('F, Y',$endTime);
        }else{
            if(date('m',$startTime)!=date('m',$endTime)){
                echo date('F j',$startTime) . " to " . date('F j, Y',$endTime);
            }else{
                echo date('F j',$startTime) . " to " . date('j, Y',$endTime);
            }
        }
    }
}

humanDateRanges("2015-04-20", "2015-04-22");
//April 20 to 22, 2015
humanDateRanges("2015-04-01", "2015-05-31");
//April to May, 2015
humanDateRanges("2015-04-30", "2015-05-02");
//April 30 to May 2, 2015
humanDateRanges("2014-05-02", "2015-05-02");
//May 2, 2014 to May 2, 2015

但我确实认为在某些情况下,即使是人类也需要被告知 4 月至 5 月将从 1 号开始,到 31 号结束。


5
投票

以防万一其他人偶然发现这一点:我编写了一个小型库,试图以也适用于国际化的方式解决这个问题:

https://github.com/flack/ranger

它应该为 ltr 语言提供合理的默认值(并且添加 rtl 支持应该不会太难)


1
投票

这是一个尝试针对所有可能的语言环境的解决方案。
肯定还有进步的空间。
看看这个演示:https://www.tehplayground.com/JIrjclNy3jEoVrTJ

function format_dateRange($from, $to, $options=[]){
    if (!is_numeric($from)) $from = strtotime($from);
    if (!is_numeric($to)) $to = strtotime($to);

    $fromArr = explode('.',date('Y.n.j', $from));
    $toArr   = explode('.',date('Y.n.j', $to));

    $sameYear  = $fromArr[0] === $toArr[0];
    $sameMonth = $sameYear && $fromArr[1] === $toArr[1];
    $sameDay   = $sameMonth && $fromArr[2] === $toArr[2];


    $locale = $options['locale'] ?? setlocale(LC_ALL, 0);
    $dateType = $options['dateType'] ?? \IntlDateFormatter::LONG;
    $timeType = $options['timeType'] ?? \IntlDateFormatter::SHORT;
    $ignoreTime = $timeType === 'none' || ($timeType === 'optional' && !$sameDay);

    if ($timeType === 'optional') {
        $timeType = $sameDay ? \IntlDateFormatter::SHORT : \IntlDateFormatter::NONE;
    }

    $startFormatter = new \IntlDateFormatter($locale,$dateType,$timeType,null);
    $pattern = $startFormatter->getPattern();

    $timeSeparator = $options['timeSeparator'] ?? ' – '; 
    $daySeparator  = $options['daySeparator'] ?? ' – ';
    $dateSeparator = $options['dateSeparator'] ?? ' – ';

    if ($timeType === \IntlDateFormatter::NONE) {
        if ($sameMonth) { // include day-range
            preg_match("/(?<!['])dd?\.?/", $pattern, $matches);
            $partPattern = trim($matches[0]);
            $endFormatter = new \IntlDateFormatter($locale,$dateType,$timeType,null);
            $endFormatter->setPattern($partPattern);
            $endPart = $endFormatter->getPattern();
            $preg = "/".preg_quote($partPattern)."(?!e)/"; 
            $pattern = preg_replace($preg, $partPattern."'".$daySeparator.($endFormatter->format($to))."'", $pattern); 
        } else if ($sameYear) { // include day-month-range
            preg_match('/(d+.+M+|M+.+d+(?!\'))/', $pattern, $matches);
            $partPattern = $matches[0];
            $endFormatter = new \IntlDateFormatter($locale,$dateType,$timeType,null);
            $endFormatter->setPattern($partPattern);
            $endPart = $endFormatter->getPattern();
            $pattern = str_replace($partPattern, $partPattern."'".$daySeparator.($endFormatter->format($to))."'", $pattern);
        }
        // else just the pattern?
    } else {
        if ($sameDay) { // incluce time-range
            $endFormatter = new \IntlDateFormatter($locale,\IntlDateFormatter::NONE,$timeType,null);
            $endPart = $endFormatter->getPattern();
            $pattern = str_replace($endPart, $endPart."'".$timeSeparator.($endFormatter->format($to))."'", $pattern);
        } else { // append end-date
            $endFormatter = new \IntlDateFormatter($locale,$dateType,$timeType,null);
            $pattern .= "'".$dateSeparator.$endFormatter->format($to)."'";
        }
    }

    $startFormatter->setPattern($pattern);
    $str = $startFormatter->format($from);
    return $str;
}

$locales = ['af-ZA','am-ET','ar-AE','ar-BH','ar-DZ','ar-EG','ar-IQ','ar-JO','ar-KW','ar-LB','ar-LY','ar-MA','arn-CL','ar-OM','ar-QA','ar-SA','ar-SY','ar-TN','ar-YE','as-IN','az-Cyrl-AZ','az-Latn-AZ','ba-RU','be-BY','bg-BG','bn-BD','bn-IN','bo-CN','br-FR','bs-Cyrl-BA','bs-Latn-BA','ca-ES','co-FR','cs-CZ','cy-GB','da-DK','de-AT','de-CH','de-DE','de-LI','de-LU','dsb-DE','dv-MV','el-GR','en-029','en-AU','en-BZ','en-CA','en-GB','en-IE','en-IN','en-JM','en-MY','en-NZ','en-PH','en-SG','en-TT','en-US','en-ZA','en-ZW','es-AR','es-BO','es-CL','es-CO','es-CR','es-DO','es-EC','es-ES','es-GT','es-HN','es-MX','es-NI','es-PA','es-PE','es-PR','es-PY','es-SV','es-US','es-UY','es-VE','et-EE','eu-ES','fa-IR','fi-FI','fil-PH','fo-FO','fr-BE','fr-CA','fr-CH','fr-FR','fr-LU','fr-MC','fy-NL','ga-IE','gd-GB','gl-ES','gsw-FR','gu-IN','ha-Latn-NG','he-IL','hi-IN','hr-BA','hr-HR','hsb-DE','hu-HU','hy-AM','id-ID','ig-NG','ii-CN','is-IS','it-CH','it-IT','iu-Cans-CA','iu-Latn-CA','ja-JP','ka-GE','kk-KZ','kl-GL','km-KH','kn-IN','kok-IN','ko-KR','ky-KG','lb-LU','lo-LA','lt-LT','lv-LV','mi-NZ','mk-MK','ml-IN','mn-MN','mn-Mong-CN','moh-CA','mr-IN','ms-BN','ms-MY','mt-MT','nb-NO','ne-NP','nl-BE','nl-NL','nn-NO','nso-ZA','oc-FR','or-IN','pa-IN','pl-PL','prs-AF','ps-AF','pt-BR','pt-PT','qut-GT','quz-BO','quz-EC','quz-PE','rm-CH','ro-RO','ru-RU','rw-RW','sah-RU','sa-IN','se-FI','se-NO','se-SE','si-LK','sk-SK','sl-SI','sma-NO','sma-SE','smj-NO','smj-SE','smn-FI','sms-FI','sq-AL','sr-Cyrl-BA','sr-Cyrl-CS','sr-Cyrl-ME','sr-Cyrl-RS','sr-Latn-BA','sr-Latn-CS','sr-Latn-ME','sr-Latn-RS','sv-FI','sv-SE','sw-KE','syr-SY','ta-IN','te-IN','tg-Cyrl-TJ','th-TH','tk-TM','tn-ZA','tr-TR','tt-RU','tzm-Latn-DZ','ug-CN','uk-UA','ur-PK','uz-Cyrl-UZ','uz-Latn-UZ','vi-VN','wo-SN','xh-ZA','yo-NG','zh-CN','zh-HK','zh-MO','zh-SG','zh-TW','zu-ZA'];
$variants = [];
foreach ($locales as $locale) {
    $variant = format_dateRange(time(), time()+60*5, ['locale' => $locale, 'timeType' => 'optional']);
    $variants[$variant] = [
        'locale' => $locale,
        'sameday' => $variant,
        'samemonth' => format_dateRange(time()-60*60*13, time()+60*60*13, ['locale' => $locale, 'timeType' => 'optional']),
        'sameyear' => format_dateRange(time()-60*60*24*16, time()+60*60*24*16, ['locale' => $locale, 'timeType' => 'optional']),
    ];
}

echo '<table>';
foreach ($variants as $variant => $row) {
    echo 
    '<tr>'.
        '<td>'.$row['locale'].
        '<td>'.$row['sameday'].
        '<td>'.$row['samemonth'].
        '<td>'.$row['sameyear'];
}
echo '</table>';

0
投票

我注意到现有的答案似乎有点过度设计,所以我制作了一个更清晰/更易读的版本,使用

DateInterval
类来大大简化问题:

function format_date_range3(\DateTime $from_date, \DateTime $to_date) {
    $diff = $from_date->diff($to_date);

    if ($diff->y) return "{$from_date->format("F j, Y")} - {$to_date->format("F j, Y")}";
    if ($diff->m) return "{$from_date->format("F j"   )} - {$to_date->format("F j, Y")}";
    if ($diff->d) return "{$from_date->format("F j"   )} - {$to_date->format(  "j, Y")}";
    return $from_date->format("F j, Y");
}

由于我们只关心日期部分(例如年、月或日)是否不同,因此我们只需要测试它的值是否为 Truthy(非 0;不同)或 Falsey(0;相同)。


这是我在项目中使用的带注释的版本,它接受各种日期格式并包括一定程度的类型安全:

/** 
 * Creates a human-readable string to represent 
 * the date range from `$from_date` to `$to_date`
 **/
function format_date_range(DateTimeInterface|string $from_date, DateTimeInterface|string $to_date) : string {
    // Convert inputs to DateTime objects, and sort by earliest date
    if (is_string($from_date)) $from_date = date_create($from_date);
    if (is_string($to_date  )) $to_date   = date_create($to_date  );
    if ($to_date < $from_date) [$from_date, $to_date] = [$to_date, $from_date];
    
    // Get the date different and return the proper string format
    $diff = $from_date->diff($to_date);
    if ($diff->y) return "{$from_date->format("F j, Y")} - {$to_date->format("F j, Y")}";
    if ($diff->m) return "{$from_date->format("F j"   )} - {$to_date->format("F j, Y")}";
    if ($diff->d) return "{$from_date->format("F j"   )} - {$to_date->format(  "j, Y")}";
    return $from_date->format("F j, Y");
}

当然,您可以编辑格式字符串以按照您想要的任何形式显示日期。

© www.soinside.com 2019 - 2024. All rights reserved.