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。
我正计划对这种格式进行编程,但我想知道它是否已经存在:)
这有效...
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 号结束。
以防万一其他人偶然发现这一点:我编写了一个小型库,试图以也适用于国际化的方式解决这个问题:
https://github.com/flack/ranger
它应该为 ltr 语言提供合理的默认值(并且添加 rtl 支持应该不会太难)
这是一个尝试针对所有可能的语言环境的解决方案。
肯定还有进步的空间。
看看这个演示: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>';
我注意到现有的答案似乎有点过度设计,所以我制作了一个更清晰/更易读的版本,使用
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");
}
当然,您可以编辑格式字符串以按照您想要的任何形式显示日期。