PHP实现短月份数组按连续区间格式化输出的问题排查
PHP: Fixing Broken Consecutive Month Range Formatting
The Problem
I need to format a list of short month names into consecutive ranges: consecutive months should be displayed as "Start to End", with multiple ranges separated by commas.
Expected Behavior
- Input:
'Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct'→ Should output:'Feb to Oct' - Input:
'Feb, Mar, Apr, May, Aug, Sep, Oct'→ Should output:'Feb to May, Aug to Oct'
But my current code is producing incorrect output: for the second input, it returns 'Mar, May, Aug, Oct' instead of the expected result.
My Faulty Code
$seasons_raw = 'Feb, Mar, Apr, May, Aug, Sep, Oct'; $seasons_arr = explode(",",$seasons_raw); $seasons_numeric = array(); $seasons_ranges = array(); $group_id = 0; $last = false; foreach($seasons_arr as $season_raw){ $month = (int)date('m', strtotime($season_raw)); if($month - $last > 1){ $group_id++; } $seasons_numeric[$group_id][] = $month; $last = $month; } foreach($seasons_numeric as $season_section){ $first_obj = DateTime::createFromFormat('!m', $season_section[0]); $last_obj = DateTime::createFromFormat('!m', $season_section[count($season_section)-1]); if($first_obj == $last_obj){ $seasons_ranges[] = $first_obj->format('M'); } else{ $seasons_ranges[] = $first_obj->format('M') . ' to ' . $last_obj->format('M'); } } $seasons = implode(', ',$seasons_ranges); echo $seasons;
Why It's Breaking
Let's walk through the key issues:
- Bad initial
$lastvalue: Setting$last = falsecauses the first month comparison to fail. PHP castsfalseto0, so when processing February (month 2),2 - 0 = 2which triggers a group increment immediately. This skips group 0 entirely, and while it doesn't break the first group's content, it's a fragile setup that can lead to unexpected behavior if input changes. - Un trimmed month strings: When exploding the input, elements like
' Mar'(with leading space) can cause inconsistent parsing in edge cases, even if it works here. It's a good practice to clean up input data. - Unreliable DateTime comparison: Comparing
DateTimeobjects directly with==can have subtle edge cases (like time zone differences, though not an issue here). It's safer to compare their formatted month values explicitly.
Fixed Code
$seasons_raw = 'Feb, Mar, Apr, May, Aug, Sep, Oct'; // Trim whitespace from each month to avoid parsing issues $seasons_arr = array_map('trim', explode(",", $seasons_raw)); $seasons_numeric = array(); $seasons_ranges = array(); $group_id = 0; $last = null; // Initialize to null to skip first iteration comparison foreach($seasons_arr as $season_raw){ $month = (int)date('m', strtotime($season_raw)); // Only increment group if we have a valid previous month and the gap is >1 if($last !== null && $month - $last > 1){ $group_id++; } $seasons_numeric[$group_id][] = $month; $last = $month; } foreach($seasons_numeric as $season_section){ $first_obj = DateTime::createFromFormat('!m', $season_section[0]); $last_obj = DateTime::createFromFormat('!m', $season_section[count($season_section)-1]); // Compare formatted month values instead of DateTime objects if($first_obj->format('m') === $last_obj->format('m')){ $seasons_ranges[] = $first_obj->format('M'); } else{ $seasons_ranges[] = $first_obj->format('M') . ' to ' . $last_obj->format('M'); } } $seasons = implode(', ',$seasons_ranges); echo $seasons; // Outputs: Feb to May, Aug to Oct
Key Fixes Explained
- Trimmed input:
array_map('trim', ...)removes leading/trailing whitespace from each month string, ensuring consistent parsing withstrtotime. - Null initial
$last: By starting with$last = null, we skip the gap check for the first month, preventing an unnecessary group increment. - Explicit month comparison: Comparing formatted month strings (
'02'vs'05') is more reliable than comparingDateTimeobjects directly.
Testing the Fixed Code
- For input
'Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct', it outputs'Feb to Oct'as expected. - For input
'Feb, Mar, Apr, May, Aug, Sep, Oct', it outputs'Feb to May, Aug to Oct'correctly.
内容的提问来源于stack exchange,提问作者BENCT
相关产品推荐
相关产品推荐

