Home日記コラム書評HintsLinks自己紹介
 

フィンローダのあっぱれご意見番 第148回「リマインダーを作ろう (2)」

← 前のをみる | 「フィンローダのあっぱれご意見番」一覧 | 次のをみる →

ソフトウェアをインストールするときには、基本的にデフォルトの設定で、 というのが常識として無難である。 つまり、ダイアログでデフォルトの指定で済むものがあれば、そのまま Enter を押す。 話が逸れるが、昔は「リターンを押す」と言ったものだが、最近そういう表現を聞いた。 キーボードに「Return」とはどこにも書いてないのだが、言われた人は分かったのだろうか、と思ったのである。

 

※ 昔よく見かけたPC-9800シリーズというパソコンのキーボードにReturnというキーがあった。

最近ハマったのは MySQL だ。 いや、他にも多数ハマっているのだが、 それはともかく、 Fedora の MySQL はデフォルトの設定だと /usr/lib/mysql にデータベースを作成したと思う。 その通りに使っていたら問題はないのだが、 今回、ディスクのパーティションの都合で、 別ディレクトリを指定することになった。 そこで、 /etc/my.cnf を編集して、 /part2/lib/mysql を指定するように変更して、 /etc/init.d/mysqld start を実行したわけだが、画面にはしっかり [失敗] と表示されている。

細かいアレコレを紹介する余裕がないので省略するが、 結局、/usr/lib/mysql から /part2/lib/mysql にシンボリックリンクを張って、 一度実行した後に一部のファイルの owner を root から mysql に変更し…といったその場しのぎで切り抜けたのだが、 そういえばこういう逃げ技は得意なのかもしれない。

§

ふ「という訳で」

U「なにが?」

ふ「リマインダーを作るって話ではありませんでしたっけ?」

U「おおっと忘れていました。」

ふ「そのためのリマインダーなのですが、 ちなみに MySQL が出てきたのはその伏線であります。 簡単に復習しておきますと、 時間 start ~ end に予定されているスケジュールがあるとして、 ある時間 t_start から t_end の間にこのスケジュールと重なっていることを判定するには、 List 1のようなクエリーを出せばいい、 というのが前回の結論です。」

---- List 1 ----

SELECT * FROM schedule WHERE start<t_end and end>t_start ORDER BY start

U「例えば 10日15時から11日2時までの予約がある場合に、 11日に予約が重なった部分があるかどうか考えてみます。 実際は timestamp 型の場合「200410101500」のような値などで指定するのですが、 今回は考え方だけ分かればいいということで適当に書いておきますと、 ここで、スケジュールとしてDBに登録されている start は 10日15時、 end は11日2時ですから、 10日15時<12日0時 and 11日2時>11日0時、 ということで条件が成立することが分かります。」

ふ「前回は、等号が必要かというような細かい話でしたが、 今回は毎週予約という機能に対応するということになっていましたね。 ということで、いかがでしょうか?」

U「毎週ということは、 曜日情報がデータベースに登録されていると考えます。 例えば、weekday というカラムがあることにします。 水曜 12:30~15:00 というスケジュールの場合、 start は 12:30、end は 15:00、weekday は水曜、という感じでいいですか?」

ふ「timestamp 型にそのような値を突っ込むのに一難ありますが…。」

U「テーブルも週間スケジュールは通常のスケジュールとは別に持つことにして、 クエリーも曜日なしの場合とは別に発行することにしましょう。 timestamp 型のような表現では、 毎週何曜日の何時から、というような情報を表現するのが面倒だし、 開始時刻と終了時刻は、時・分・秒のみを long で持つことにします。 すると、WHERE に指定する条件は、 start<t_end and end>t_start and weekday=target_w みたいな?」

ふ「理屈としては分かりますが、毎日のスケジュールを選ぶのだから、 時刻を条件として与える必要はないのでは?」

U「あ、そうか、曜日だけチェックすれば済むのか。」 "weekday=target_w" でいいのか。」

§

ふ「さて、ここまでは前置きです。」

U「マジすか? 無茶苦茶長いやん。」

ふ「というか、実際に書いてハマったのはここからなのです。 前回の要求仕様では、毎日の予定の他に、30分前とか、3日後とか、そういうのもメールを送りたいということになっていましたから、 30分前というのをやってみましょう。」

U「今から30分後までの間にデータの開始時刻が含まれているものを選択する という仕様でいいですか?」

ふ「そうですね、今回は直前に連絡するということが目的になるので、 終了時刻は無視していいです。 実際の処理では、通知したかどうかをフラグで記憶しておくようなことも必要になりそうですが、その種の細かいことも無視していいです。」

U「すると、こんな感じですか。t_start は今、t_end は今から30分後として、 "start>t_start and start<=t_end "」

ふ「実際は、start>=t_start にしても構いません。 5分おきのチェックで30分以内にひっかかったらメールを出して、 一旦出したらフラグ立てるとかしておいて二度と出さない、というような処理ですから、 こちらの条件がシビアになることはまずないと考えられるのです。」

U「これは割と簡単でしたね。」

ふ「好事魔多しとも言いますが、それはおいといて、 毎週指定のスケジュールはどうやって判定しましょうか?」

U「それも簡単ですね、 今の曜日を w_now とします。誌面の都合もあるので条件部分だけ書くことにしますと、 "start>t_start and start<=t_end and weekday=w_now" で判定できると。」

ふ「それ本当に大丈夫ですか?」

U「ヤバいとは思ったのですが、23:45とかにチェックしたらどうなるのだ、 と言いたい訳でしょ?」

ふ「まあレアケースなんだろうけど、 今から30分以内に曜日が変わったらイヤですよね。」

U「しょうがないな、厳密にやってみましょう。今日と明日で曜日が変わるので、 別々に考えることにします。つまり、23:45~翌日00:15 の間にスケジュールが登録されているというのは、 本日の23:45~24:00でヒットするか、 翌日の00:00~00:15でヒットするか、という2つのケースで判定すればいいと。 指定期間が2日にまたがる場合はこういうクエリーになる。 "(start>t_start and weekday=w_start) or (start<=t_end and weekday=w_end)"」

ふ「しょうがないというのは言い得て妙というか、 仕様ですと開き直る手は実際にありますが、今回はそれはナシということで。」

U「実はこの条件判定は、こういうこと。 "(start>t_start and start<24:00 and weekday=w_start) or (start >= 00:00 and start<=t_end and weekday=w_end)" ですが、start<24:00 とか start>=00:00 は必ず成り立つので、省略したのです。」

ふ「またがらない場合は?」

U「 "(start>t_start and start<t_end and weekday=w_start)" ですか?」

ふ「毎週水曜 00:00 から、というスケジュールが火曜日23:30のチェックでメールを出せることを確認してください。」

U「 00:00>23:30 and 00:00<00:00 and WED=TUE… ってことは、全然ダメですか? ってまてよ、23:30のチェックというのは、日付をまたぐ方で処理しないといけないのか。 (00:00>23:30 and WED=TUE) or (00:00<=00:00 and WED=WED) 、これだとokですね゜」

ふ「23:30から30分後というのは既に翌日と考えるわけですね、 それでokのようですね。 というか、実際はそのような判定もプログラムに任せることができて、 クエリーを生成するプログラムは、例えば List のような感じですか。」

---- List 2 (毎週のスケジュールを調べるクエリーを生成する処理: Java) ----

    Calendar t_start = new GregorianCalendar(); // 現在時刻
    Calendar t_end = (Calendar) t_start.clone(); // 複製
    t_end.add(Calendar.MINUTE, 30); // 30分後

    int w_start = t_start.get(Calendar.DAY_OF_WEEK);
    int w_end = t_end.get(Calendar.DAY_OF_WEEK);

    String where;
    if (w_start == w_end) {
        where = "(start>" + t_start
            + " and start<" + t_end
            + " and weekday=" + w_start + ")";
    } else {
        // 2日にまたがる
        where = "(start>" + t_start
            + " and weekday=" + w_start
            + ") or (start<=" + t_end
            + " and weekday=" + w_end + ")";
    }

    String query = "SELECT * FROM weekly_schedule WHERE "
        + where + " ORDER BY start";

U「何でまた Java がというか、Xoops で処理するのなら PHP で書けばいいのでは?」

ふ「まあそれも道理なのですが、 Java で書いておけば、Java を知らない人でも意外と勘で読んで分かるものですが、 PHP で書くとどうも分からなくなりそうなので。」

U「それはそういう書き方をするからなのでは?」

  

ふ「まあそうも言えますが。 とりあえず宿題ということで、毎月何日という指定に対して今回と同様の30分前メールを出すためのクエリーを考えてみてください。余裕がある人は、まあ冗談ですが、〆切3日後に出す方法も。なお、実際にこの種のプログラムを作る場合は、スケジュールを変更する機能への対応にも注意が必要です。」

 

※ 指定した数日後にメールを出す機能は、 意外と重宝するのではないかと思う。

(C MAGAZINE 2004年10月号掲載)
内容は雑誌に掲載されたものと異なることがあります。

修正情報:
2006-03-01 裏ページに転載。

(C) Phinloda 2004-2006, All rights reserved.