1
1

More than 3 years have passed since last update.

Carbonで何週目の何曜日の日付を取得する

Last updated at Posted at 2020-10-11

日付処理を扱う上で、carbonに非常に助けられている今日この頃だが、思ったより簡単に書けない処理があり、暫定で作ったものを備忘録として残す。それもっと簡単に行けるで、という情報があれば是非教えていただければと思う。

前提

日曜始まり土曜終わりを1週として、日曜から順に0,1,2...と曜日と数字が対応している。

['sun' => 0, 'mon' => 1, 'tue' => 2, 'wed' => 3, 'thu' => 4, 'fri' => 5, 'sat' => 6,]

1週目、2週目、、、5週目とn週目とnが対応している。

['1週目' => 1, '2週目' => 2, '3週目' => 3, '4週目' => 4, '5週目' => 5, '6週目' => 6]

要件

n週目のm曜日を入力値として与えられた時に、日付を返す。


public function getDate($week_of_month, $weekday)
{
  // return '2020-10-19'
}

Carbonじゃダメなの?

Carbonにも何週目か判定するメソッドや、曜日を判定するメソッドなどなど便利なメソッドが多々あるが、何週目かの計算が、月曜始まりのものと、単純に7日づつ加算して算出するものしかなく、日曜始まりで扱いやすそうな処理が書けなかったので断念

//月曜始まりで、n週目を計算。
$carbon->weekNumberInMonth;
//7日間づつ計算
$carbon->weekOfMonth;

2020/10/12追記

@vf8974 さんより、標準関数でより簡潔にかける方法をいただいたので追記

public function getDay($params)
    {
        // DateTimeImmutableインスタンス
        $d = new \DateTimeImmutable();
        // 年(省略時は今年)
        $year = $params['year'] ?? $d->format('Y');
        // 月(省略時は今月)
        $month = $params['month'] ?? $d->format('m');
        // 対象月1日
        $firstDay = $d->setDate($year, $month, 1);
        // 対象月1日の曜日
        $firstDayOfWeek = $firstDay->format('w');
        // 対象月の最終日
        $lastDay = $firstDay->format('t');
        // 第num week曜日の日
        $day = 1 + ($params['num'] - 1) * 7 + ($params['week'] - $firstDayOfWeek) % 7;
        // $dayが当月範囲内ならYYYY-MM-DDを、範囲外ならfalseを返す
        return $day >= 1 && $day <= $lastDay ? sprintf('%04d-%02d-%02d', $year, $month, $day) : false;
    }

書いたコード

日曜始まりの簡易的なカレンダーを作成し、曜日加算するという二段構えで実装した。

     protected $month_weeks;

     public function __construct()
     {
       $this->month_weeks = $this->createWeekOfMonths($this->argument($target_month))
     }
     /**
     * @param Carbon $start_of_month
     * @return array
     */
    public function createWeekOfMonths(Carbon $start_of_month): array
    {
        //start_of_month='2020-11-01'
        $target_month_weeks = [];
        //月初の曜日取得 start_of_month_weekday=0, (ie, sunday)
        $start_of_month_weekday = $start_of_month->weekday();
        //対象月の初週の日曜日を、取得
        $target_sunday = (new Carbon())->createFromTimestamp($start_of_month->timestamp)->subDays($start_of_month_weekday);

        while ($target_sunday->lte($target_month->endOfMonth())) {
            array_push($month_weeks, (new Carbon())->createFromTimestamp($target_sunday->timestamp));
            $target_sunday->addDays(7);
        }
        return $month_weeks;
    }
//$this->createWeekOfMonths('2020-11-01');
//['2020-11-01','2020-11-08','2020-11-15','2020-11-22','2020-11-29']

こうすると配列のIndexがそのままn-1週目になっている形で取得できるので、あとはこの簡易カレンダーをもとにn週目,n曜日の条件から日付を取得する。

public function getDate($week_of_month, $weekday)
{
  $target_date = array_key_exists($week_of_month - 1, $this->month_weeks) //n週目存在チェック
                ? (new Carbon())
                    ->createFromTimestamp($this->month_weeks[$week_of_month - 1]->timestamp) //n週目の日曜日のCarbonオブジェクト取得
                    ->addDays($weekday) //m曜日のm分加算 (2020-11-01 sun(0) -> 2020-11-03 tue(2))
                : null;
  return $target_date;
}

まとめ

簡易カレンダー作成にループ使用するのが若干嫌な気がしますが、暫定版なのでひとまずこれで対応。

1
1
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1