skip to Main Content

I have a problem condensing a large list of dates.

The source code can be seen here https://onlinephp.io/c/d8277

Source code:

<?php
    function list_date($array = [])
    {
        $events = $array;

        for ($i = 0; $i < count($events); $i++)
        {
            $this_event = $events[$i];
            $this_start_date = strtotime($this_event["date"]);
            $this_end_date = strtotime($this_event["date"]);
            $this_start_month = date("M", $this_start_date);
            $this_end_month = date("M", $this_end_date);
            $this_start_year = date("Y", $this_start_date);
            $this_end_year = date("Y", $this_end_date);

            $last = ($i == count($events) - 1);
            if (!$last)
            {
                $next_event = $events[$i + 1];
                $next_start_date = strtotime($next_event["date"]);
                $next_end_date = strtotime($next_event["date"]);

                $next_start_month = date("M", $next_start_date);
                $next_end_month = date("M", $next_end_date);
                $next_start_year = date("Y", $next_start_date);
                $next_end_year = date("Y", $next_end_date);
            }

            if (($this_start_month != $this_end_month) ||
                ($this_start_year != $this_end_year))
            {
                echo date("j/m", $this_start_date);
                if ($this_start_year != $this_end_year)
                {
                    echo " ".date("Y", $this_start_date);
                }
                echo "-".date("j/m/Y", $this_end_year)." <br/>n";
            }
            else
            {
                echo date("j", $this_start_date);
                if ($this_start_date != $this_end_date)
                {
                    echo "-".date("j", $this_end_date);
                }

                $newline = false;
                if ($last ||
                    ($this_start_month != $next_end_month))
                {
                    echo " ".date("m", $this_start_date);
                    $newline = true;
                }

                if ($last ||
                    ($this_start_year != $next_end_year) ||
                    ($next_start_month != $next_end_month))
                {
                    echo " ".date("Y", $this_start_date);
                    $newline = true;
                }

                if ($newline)
                {
                    echo " <br/>n";
                }
                else
                {
                    echo ", ";
                }
            }
        }
    }
    
    $data = [
        [
            'name' => 'Event A',
            'date' => '2023-03-1'
        ],
        [
            'name' => 'Event B',
            'date' => '2023-03-2'
        ],
        [
            'name' => 'Event C',
            'date' => '2023-03-3'
        ],
        [
            'name' => 'Event D',
            'date' => '2023-03-7'
        ],
        [
            'name' => 'Event E',
            'date' => '2023-03-9'
        ],
    ];


    echo "Event Schedule " . list_date($data);

Current output: Event Schedule 1, 2, 3, 7, 9 03 2023

The output what I want: Event Schedule 1 – 3, 7, 9 / 03 / 2023

Thank you.

2

Answers


  1. Ok, so you can use DateTime class to get difference between 2 dates at ease.

    • Run a loop. If the current date is 1 day more than the previous date, they belong to the same group.

    • If the current date in loop when compared to the tracking variable we have is more than 1 day:

      • We add current tracking variables as a group to our resultant array
      • We start a new group from here by updating tracking variables with the current date.
    • In the end, we just print the output in your desired state.

    Snippet:

    <?php
    
    $start_d = null; 
    $curr_d = null;
    
    $groups = [];
    
    foreach($data as $d){
        $dd = DateTime::createFromFormat('!Y-m-d', $d['date']);
        if($start_d == null){
            $start_d = $curr_d = $dd;
        }else{
            $diff = $dd->diff($curr_d) ;
            if($diff->y === 0 && $diff->m === 0 && $diff->d === 1){
                $curr_d = $dd;   
            }else{
                $groups[] = [$start_d->format('d'), $curr_d->format('d')];
                $start_d = $curr_d = $dd;
            }
        }
    }
    
    $groups[] = [$start_d->format('d'), $curr_d->format('d')];
    
    echo implode(" , ", array_map(fn($v) => implode(" - ", array_unique($v)), $groups)), " / ", $start_d->format('m'), " / ", $start_d->format('Y');
    

    Online Demo

    Login or Signup to reply.
  2. One way to do it would be to initialize month-year-specific strings in the desired format, then as new days are encountered within the given month-year, use regex to inject/extend the substring in the middle of the formatted string.

    Code: (Demo)

    $result = [];
    $last = [];
    foreach ($array as ['date' => $date]) {
        sscanf($date, '%d-%d-%d', $y, $m, $d);
        $key = $y . $m; // group by year-month
        if (isset($last[$key])) {
            $lastPadded = sprintf('%02s', $last[$key]);
            $thisPadded = sprintf('%02s', $d);
            if (($d - 1) === $last[$key]) {
                $result[$key] = preg_replace("~-$lastPaddedb|b$lastPaddedbK~", "-$thisPadded", $result[$key]); // hyphenate
            } else {
                $result[$key] = preg_replace("~b$lastPaddedbK~", ", $thisPadded", $result[$key], 1); // comma-separate
            }
        } else {
            $result[$key] = sprintf('%02s / %02s / %d', $d, $m, $y); // push initial string for year-month
        }
        $last[$key] = $d;
    }
    echo implode(", ", $result);
    

    Some of the convolution can be trimmed down if you actually desire unpadded day and month numbers.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search