多个日期的回声持续时间(例如 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
我想要代码:
- 处理单日事件(例如 10 月 3 日)
- 组连续天数(例如 1 月 25-27 日)
- 用逗号分隔非连续的日期(例如 Sep 19, 22)
- 处理跨越多个月的日期范围(例如 2 月 28 日至 3 月 2 日)
- 必要时处理多个范围(例如 4 月 2-4 日,6 月 10-13 日)
- 避免冗余日期信息(例如 12 月 1 日至 3 日,12 月 8 日)
仅使用 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 来向您展示它是如何工作的。如果您对它的工作原理有任何疑问,请不要犹豫。
我正在处理可能发生在一天(例如 5/20)或多天(例如 8/25、8/26、8/27、9/3)的事件。
例如,假设在 8 月 25 日、8 月 26 日、8 月 27 日和 9 月 3 日举行了为期 4 天的活动,我想附和一下:
Aug 25-27, Sep 3
我想要代码:
- 处理单日事件(例如 10 月 3 日)
- 组连续天数(例如 1 月 25-27 日)
- 用逗号分隔非连续的日期(例如 Sep 19, 22)
- 处理跨越多个月的日期范围(例如 2 月 28 日至 3 月 2 日)
- 必要时处理多个范围(例如 4 月 2-4 日,6 月 10-13 日)
- 避免冗余日期信息(例如 12 月 1 日至 3 日,12 月 8 日)
仅使用 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 来向您展示它是如何工作的。如果您对它的工作原理有任何疑问,请不要犹豫。