【问题标题】:PHP Human date range/duration formatPHP 人类日期范围/持续时间格式
【发布时间】:2015-07-10 15:29:00
【问题描述】:

PHP 做得很好,我想知道是否有我需要的函数。

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

例子...

事件一:从 2015-04-20 到 2015-04-22 可以像这样为人类格式化:2015 年 4 月 20-22 日

事件二:2015-04-01 至 2015-05-31 人类 -> 2015 年 4 月至 5 月

事件三:从 2015-04-30 到 2015-05-02 人类 -> 2015 年 4 月 30 日到 5 月 2 日

简而言之,不要重复不需要重复的内容。尽量用“-”链接。

它必须本地化,并且格式可能会因本地而异。例如,美国人喜欢 MonthName DayNumber Year,但法国人喜欢 DayNumber MonthName Year。

我正计划编写这样的格式,但我想知道它是否已经存在:)

【问题讨论】:

  • 我不认为你可以不做一点工作就做这样的事情。不过,原生 DateTime 类将为您完成大部分读取日期和格式化的工作!
  • 日期格式实际上看起来很随机。人们不一定知道“2015 年 4 月至 5 月”是指 4 月 1 日至 5 月 31 日。为什么不更具体一些,对所有间隔使用相同的格式。
  • 你想!人类填补了你所知道的空白。他们很清楚该活动将在四月和五月举行。人类也喜欢简短直观的格式。像 2015-04-01 到 2015-05-31 这样的格式比 2015 年 4 月到 2015 年 4 月更难理解和评估。恕我直言

标签: php date format


【解决方案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>';

【讨论】:

    【解决方案2】:

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

    https://github.com/flack/ranger

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

    【讨论】:

      【解决方案3】:

      这行得通...

      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 日结束。

      【讨论】:

      • +1 太棒了!您可以改为使用“n”进行月份比较。我还需要想办法将其本地化:)
      • 要本地化它,用 strftime() 替换所有 date() 函数,格式选项与 %Y 的年份略有不同,而不仅仅是 Y,但它是相同的,并且您可以在函数顶部设置语言环境。只需将它作为另一个变量传递并设置它。您可能还应该返回结果,而不是回应它们,我认为这样会更有用,这只是一个快速的尝试。 us2.php.net/manual/en/function.strftime.php
      猜你喜欢
      • 2015-01-22
      • 1970-01-01
      • 1970-01-01
      • 2018-03-03
      • 1970-01-01
      • 2018-04-07
      • 2021-12-11
      • 2015-07-13
      • 2018-09-15
      相关资源
      最近更新 更多