どのサービスでも、一定期間内にどれだけのログインがあったか等の集計をするバッチはあったりするかと思います。
今回初めて集計バッチの実装をしたのですが、その中で集計期間の範囲を決める
ロジックに対して指摘を受けたので、今後同じミスをしないためにも書き留めておきます。
要件
-
バッチ実行日は毎週火曜日11時とする。
-
期間は実行日から見て1週間前の日曜日から先週の土曜日とする。
- 例 実行日が8月24日(火)の場合、8月15日(日) ~ 8月21日(土)になる。
Carbon
PHPを使用してる人なら日付操作にCarbonを使ってる人は多いのではないかと思います。
CarbonとはPHPのDateTimeクラスをオーバーラップした
日付操作ライブラリのことです。→ Carbon 公式
指摘を受けた実装例
要件を満たす実装として下記の様な方法で実装してました。
$count_to_start_day = now()->dayOfWeek - Carbon::SUNDAY;
$period_start = now()->subWeek()->subDays($count_to_start_day)->startOfDay();
$peirod_end = $period_start->copy()->addDays(Carbon::SATURDAY)->endOfDay();
上から見ていくと、バッチ実行日は火曜日になるので$count_to_start_dayには2の数字が入るかと思います。
>>> Carbon::setTestNow(new Carbon('2021-08-24 10:00:00'))
=> null
>>> now()
//2021-08-24日は火曜日//
=> Illuminate\Support\Carbon @1629766800 {#3336
date: 2021-08-24 10:00:00.0 Asia/Tokyo (+09:00),
}
>>> $count_to_start_day = now()->dayOfWeek - Carbon::SUNDAY;
=> 2
また、$period_startには用件通り2週間前の日曜日の日付を取得できてるかと思います。
>>> $period_start = now()->subWeek()->subDays($count_to_start_day)->startOfDay();
//2021-08-15日は日曜日//
=> Illuminate\Support\Carbon @1628953200 {#3348
date: 2021-08-15 00:00:00.0 Asia/Tokyo (+09:00),
}
>>>
よって$period_startから見て6日後の8月21日の日付を取得できるかと思います。
>>> $peirod_end = $period_start->copy()->addDays(Carbon::SATURDAY)->endOfDay();
2021-08-21日は土曜日
=> Illuminate\Support\Carbon @1629557999 {#3352
date: 2021-08-21 23:59:59.999999 Asia/Tokyo (+09:00),
}
>>>
この様に、上記で述べた要件は満たされてるかと思います。
ただこの場合は、起点となっているのが日曜日(0)となっているので問題がなかっただけで
要件変更で集計期間を、月曜日から日曜日になった時に以下のようになってしまいます。
>>> $count_to_start_day = now()->dayOfWeek - Carbon::MONDAY
=> 1
>>> $period_start = now()->subWeek()->subDays($count_to_start_day)->startOfDay();
=> Illuminate\Support\Carbon @1629039600 {#3335
date: 2021-08-16 00:00:00.0 Asia/Tokyo (+09:00),
}
>>> $period_end = $period_start->copy()->addDays(Carbon::SUNDAY)->endOfDay();
//Carbon::SUNDAYは0になるので日付は変わらない//
=> Illuminate\Support\Carbon @1629125999 {#3355
date: 2021-08-16 23:59:59.999999 Asia/Tokyo (+09:00),
}
>>>
この様に集計開始期間と集計終了時間が同じになってしまいます。
そこで$period_endの取得方法を単純にaddWeek()->subDay()で6日後を取得する様にすれば、要件が変わっても対応できます!
>>> $count_to_tart_day = now()->dayOfWeek - Carbon::SUNDAY;
=> 2
>>> $period_start = now()->subWeek()->subDays($count_to_start_day)->startOfDay();
=> Illuminate\Support\Carbon @1629039600 {#3356
date: 2021-08-15 00:00:00.0 Asia/Tokyo (+09:00),
}
>>> $period_end = $period_start->copy()->addWeek()->subDay()->endOfDay();
=> Illuminate\Support\Carbon @1629644399 {#3355
date: 2021-08-21 23:59:59.999999 Asia/Tokyo (+09:00),
}
>>>
これだと要件が月曜日から日曜日に変更されても対応できますね!
>>> $count_to_start_day = now()->dayOfWeek - Carbon::MONDAY;
=> 1
>>> $period_start = now()->subWeek()->subDays($count_to_start_day)->startOfDay();
=> Illuminate\Support\Carbon @1629039600 {#3339
date: 2021-08-16 00:00:00.0 Asia/Tokyo (+09:00),
}
>>> $period_end = $periof_start->copy()->addWeek()->subDay()->endOfDay();
>>> $period_end = $period_start->copy()->addWeek()->subDay()->endOfDay();
=> Illuminate\Support\Carbon @1629644399 {#3344
date: 2021-08-22 23:59:59.999999 Asia/Tokyo (+09:00),
}
>>>
今振り返ってみれば何故こんなにややこしい実装をしたのだろうと思います。。。
皆さんも日付操作には気をつけてください!