2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ISOに頼り切って52週を集計するコピペコード

Posted at

image.png

「52週間前」って何月何日?

1年の数字の変化を追うのに、12か月だとばっくりし過ぎていて365日だとギザギザすぎ。
そこで「週単位」にすると、曜日の影響もなくなってスムーズ。
52個の数値の変化がグラフにしたとき見た目にちょうどいい。
そこで、52週分の集計をしようと思ったけど、

  • 週単位って、月曜始まり? 日曜始まり?
  • 52週前の月曜日って何日?
  • どうやって計算するの?

そんなこと、もう悩まなくて大丈夫。偉い人が考えていて「国際規格」にまとめてくれていて、各種プログラミング言語でも標準で使えるようになっています。

この記事は、その国際規格に合わせたコードを、PHP + MySQL + Laravel という環境で、コピペで使えるようにまとめたものです。

ISO-8601 による 週番号

そもそも、馴染みがありませんが、1年を52週に割って、1月から番号を振ったものを「週番号」と言うそうです(そう言われればスケジュール帳には週番号が書いてあります)。
その週番号のカウント方法はいろいろあるのですが、いろいろあるとめんどくさいので、ISOがビシッと定めてくれています。

ISO_8601#年と週と曜日 (wikipedia)

ずばり、

  • 月曜始まり
  • 1月4日を含む週が第1週(第1週には4日以上含む)

これを MySQL、PHP、Laravelでどう扱うか…。

MySQL

MySQL YEARWEEK() Function

YEARWEEK関数の「モード3」です。

3 - First day of week is Monday and the first week has more than 3 days

とありますが、要するにこれが ISO-8601 ということ。
こうなります。

SELECT YEARWEEK( created_at, 3 ) AS week

PHP

date には W を与えると週番号になります(単にWをそのまま出力したい場合は \W とエスケープする必要があります)。
strtotime では、年と週番号を W を挟んで 'yyyyWww' という形式で与えます。たとえば '2018W35'

date('Y/m/d'); // 今日が 2018/08/29 だとすると…

date('Y\WW',strtotime("-0 weeks")); // 2018W35(1つ目のWはエスケープしてそのまま出力)
date('Y/m/d D',strtotime("2018W35"));  // 2018/08/27 Mon

date('Y\WW',strtotime("-52 weeks")); // 2017W35
date('Y/m/d D',strtotime("2017W35"));  // 2017/08/28 Mon

-0 weeks だと今日を含む週で、まだ7日経過していないません。
通常は集計対象から外して、集計期間を -52週~-1週 を対象とします。

Laravel

MyOrdersList という Eloquent モデルがあったとして…

// 集計したい週数
$weeks = 52; 
// 開始日時をタイムスタンプにしておく
$start_time = strtotime(date('Y\WW',strtotime("-{$weeks} weeks")));

$result = MyOrdersList::select(
    DB::raw('YEARWEEK(created_at,3) AS week'), 
    DB::raw('sum(*) AS price')
)->whereRaw("created_at >= '".date('Y-m-d 00:00:00',$start_time)."'")
->groupBy('week')
->pluck('price','week')
->toArray();

// 歯抜けなしデータにしながらキーをY-m-d形式に
$filled_result = [];
for( $i=$weeks; $i>=1; $i-- ){
    $week = date('YW',strtotime("-$i weeks"));
    $date = date('Y-m-d',strtotime("-$i weeks"));
    $filled_result[$date] = isset($result[$week]) ? $result[$week] : 0;
}
return $filled_result;
return
=> [
     "2017-08-30" => 351,
     "2017-09-06" => 0,
     "2017-09-13" => 524,
     "2017-09-20" => 434,

     ......

     "2018-08-01" => 144,
     "2018-08-08" => 264,
     "2018-08-15" => 0,
     "2018-08-22" => 491,
   ]

感想

また誰得な記事が出来上がってしまいましたが…。ユーザーが一人でもいればいっか(自分を含めて)、と思うことにしています。

個人的には週始まりは月曜日派ですが、カレンダーは日曜始まりが多いことないですか?(iPhoneのカレンダーアプリも日曜始まり。これは確か言語と地域の設定による)きっとこれが、僕がカレンダーを全然めくらない理由に違いない。

2
1
0

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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?