PHP How to get first and last day of a week based on date range

Viewed 71

I have a function that can return first day and last day of a week based on date range given but apparently I got some problems in the function as it will return 8 days per week in an array and how can I set the end date of a week to Sunday as now the end date is Monday.

PHP:

    private function rangeWeek(string $start,string $end): array
    {
        $start     = new DateTime($start);
        $end       = new DateTime($end);
        $interval  = new DateInterval('P1D');
        $period    = new DatePeriod($start, $interval, $end);
        $weeks     = [];
        $oldWeek   = null;
        $weekStart = null;
        foreach ($period as $date) {
            $week = $date->format('W');
            if ($week !== $oldWeek) {
                if (null === $weekStart) {
                    $oldWeek   = $week;
                    $weekStart = $date->format('Y-m-d');
                } else {
                    $weeks[]   = ['start' => $weekStart, 'end' => $date->format('Y-m-d'), 'week' => $week];
                    $weekStart = null;
                }
                continue;
            }
        }

        $weeks[] = ['start' => $weekStart, 'end' => $end->format('Y-m-d'), 'week' => $week];
        return $weeks;
    }

$startDate = '2022-08-08'; //Monday
$endDate   = '2022-08-23'; //Tuesday
$weeks     = rangeWeek($startDate, $endDate);
print_r($weeks);

Output:

Array
(
    [0] => Array //This array return 8 days per week
        (
            [start] => 2022-08-08 //Monday
            [end] => 2022-08-15   //Monday (should be end on Sunday)
            [week] => 33
        )

    [1] => Array
        (
            [start] => 2022-08-16 //Tuesday (Should be start on Monday)
            [end] => 2022-08-22  //Monday (should be end on Sunday)
            [week] => 34
        )

    [2] => Array
        (
            [start] => null
            [end] => 2022-08-23
            [week] => 34
        )

)

Expected Output:

Array
(
    [0] => Array
        (
            [start] => 2022-08-08 //Monday
            [end] => 2022-08-14   //Sunday
            [week] => 33
        )

    [1] => Array
        (
            [start] => 2022-08-15 //Monday
            [end] => 2022-08-21  //Sunday
            [week] => 34
        )

    [2] => Array
        (
            [start] => 2022-08-22 //Monday
            [end] => 2022-08-23   //Tuesday
            [week] => 35 
        )

)
4 Answers

Without DateInterval, the modify method of DateTime can be used to go from the current day to the next Sunday and then to the following Monday.

function rangeWeek(string $start,string $end): array{
  $dtStart = date_create($start);
  $dtEnd = date_create($end);
  $weeks = [];
  while($dtStart <= $dtEnd){
    $weeks[] = [
      'start' => $dtStart->format('Y-m-d'),
      'end' => min($dtEnd,$dtStart->modify('Sunday this week'))->format('Y-m-d'),
      'week' => $dtStart->format('W')
    ];
  $dtStart->modify('next Monday');
  }
  return $weeks;
}

$startDate = '2022-08-08'; //Monday
$endDate   = '2022-08-23'; //Tuesday
$weeks     = rangeWeek($startDate, $endDate);
var_dump($weeks);

Demo: https://3v4l.org/ouK2q

Using min replaces the if queries to test if the next Sunday is after the end date. min can be used since DateTime objects can be directly compared.

I'm adding a simple post here with a PHP method that has helped me. This method calculates the beginning and ending of a week given the year and week number

To add to the confusion, PHP's methods themselves seem confused about what the first and last day of the week are.

For example:

$new_date = new DateTime;
// returns Monday, Jan 29 2018
$new_date->setISODate(2018, 5);

// returns Sunday, Feb 4 2018
$new_date->modify('sunday this week');

// returns Sunday, Jan 28 2018
$new_date->setISODate(2018, 5 ,0);

The method below is the one I've found returns the most helpful results:

function get_first_and_last_day_of_week( $year_number, $week_number ) {
    // we need to specify 'today' otherwise datetime constructor uses 'now' which includes current time
    $today = new DateTime( 'today' );

    return (object) [
        'first_day' => clone $today->setISODate( $year_number, $week_number, 0 ),
        'last_day'  => clone $today->setISODate( $year_number, $week_number, 6 )
    ];
}

I written a sample code.

I using monday this week to get start of week and sunday this week to get end of week.

<?php

    function rangeWeek(string $start,string $end)
    {
        $start     = new DateTime($start);
        $end       = new DateTime($end);
        $interval  = new DateInterval('P1D');
        $period    = new DatePeriod($start, $interval, $end);
        $weeks     = [];
        $oldWeek   = null;
        $weekStart = null;
        foreach ($period as $date) {
            $week = $date->format('W');
            
            if(!isset($weeks[$week]))
            {
                 $weekStart = date("Y-m-d", strtotime('monday this week', strtotime($date->format('Y-m-d'))));
                $weekEnd = date("Y-m-d", strtotime('sunday this week', strtotime($date->format('Y-m-d'))));
                 $weeks[$week]   = ['start' => $weekStart, 'end' => $weekEnd, 'week' => $week];
            }
        }
        return $weeks;
    }

$startDate = '2022-08-08'; //Monday
$endDate   = '2022-08-23'; //Tuesday
$weeks     = rangeWeek($startDate, $endDate);
print_r($weeks);

You don't need to keep track of the week number but rather just Mondays and Sundays as a pair like below:

Snippet:

<?php


function rangeWeek(string $start,string $end): array{
    $start     = new DateTime($start);
    $end       = new DateTime($end);
    $interval  = new DateInterval('P1D');
    $period    = new DatePeriod($start, $interval, $end);
    $weeks     = [];
    $monday    = null;
    foreach ($period as $currentDay) {
       if($currentDay->format('D') == 'Mon'){
         $monday = $currentDay;
       }else if($currentDay->format('D') == 'Sun'){
          $weeks[] = [
            'start' => is_null($monday) ? null : $monday->format('Y-m-d'),
            'end' => $currentDay->format('Y-m-d'),
            'week' => $currentDay->format('W')
          ];
          $monday = null;
       }
    }
    
    return array_merge($weeks, [[
        'start' => is_null($monday) ? null : $monday->format('Y-m-d'),
        'end' => $end->format('Y-m-d'),
        'week' => $end->format('W')
      ]]);
}

$startDate = '2022-08-08'; //Monday
$endDate   = '2022-08-23'; //Tuesday
$weeks     = rangeWeek($startDate, $endDate);
print_r($weeks);

Online Demo

Related