[pgsql-jp: 29990] Re: 24:00を超えた場合の時刻表示方法について

T.Suzuki t_suzuki @ kenwood-eng.co.jp
2003年 5月 19日 (月) 18:56:19 JST


鈴木と申します。

既に石田さんが、大変参考になるSQLを紹介されている上、
ご本人が解決されているので、邪魔でしかないかも知れませんが。。

> viewを使ってセレクトしてきた方が早いかな〜と思っていたのですが、
>
> >PostgreSQLで解決することにこだわらず、PHPなどのWEBアプリケーション
> >の表示側で解決してもいいじゃないでしょうか?
>
> phpのアプリ側で対応して、もう一度SQL文を勉強しなおします。

勤怠管理系の処理である場合、勤怠上の日付が変わるのは、
6時とか7時である為、その扱い(変換処理)にかなり苦労しました。

 ・日付を超えて、「在場時間 = 退勤時間 - 出勤時間」をSQLで行いたい
 ・「在場時間から休憩時間を減算」をSQLで行いたい

こういった処理を、(6時とか7時とか)基準時間をもとに計算したかったので、
時刻←→Int時間の変換を、PL/pgSQLで関数化してみました。

> phpのアプリ側で対応して 〜略
psqlから、ちょっとしたSQLを投げる際に、PHPのクラスでは使えなくて
思い切って作りました。

以下に、そのPL/pgSQLを紹介させて下さい。
#サーバに掲載してリンクを張りたい所ですが、場所が無い為、
#メールにて紹介させて下さい。
----------------------------------------------------
/**
 * TO_INTERVAL()
 * 時刻形式の表現を引数に受け取り、基準時間からの経過分を返します。
 * ※プロシージャ作成時に基準時刻(root_time)を変更しておく必要があります。
 *
 * @language     plpgsql
 * @date         2001/11/14
 * @author       t_suzuki
 * @param        text     ":"区切りの時刻表現された文字列
 * @return       integer  基準時間からの経過時間を分単位で表した数値
 */
CREATE or REPLACE FUNCTION to_interval(text)
RETURNS integer AS '
Declare
    ins_text alias for $1;                          --引数の別名定義
    root_time constant integer not null default 7;  --開始基準時刻
    center          integer;    --引数内の文字列で、「:」の位置
    total_time      integer;    --合計値(分)【戻り値】
    houre_time      integer;    --時間単位部分
    minute_time     integer;    --分単位部分
Begin
    --引数の文字列で「:」の位置を調べる
    center := position('':'' in ins_text);
    IF center < 1 THEN          --「:」が無い
        RETURN NULL;            --「:」が無い場合は、NULL値を返す
    END IF;
    IF character_length(ins_text) <= center+1 THEN
      --分が1桁以下の入力のため、適切な値を入れる事が出来ない為、NULLを返す
        RETURN NULL;
    END IF;
    IF center = 1 THEN             --1文字目が「:」であるとき
        --開始基準時刻が省略されていると判断し、補完する
        houre_time := root_time;
    ELSE
        --時間部分を取り出す
        houre_time := substring( ins_text from 0 for center)::int;
    END IF;
    --分の部分を取り出す
    minute_time := substring( ins_text from center+1 for 5)::int;
    --24時間表記で、0〜基準時刻のときは24を足す
    IF houre_time < root_time THEN
        houre_time := houre_time + 24;
    ELSE
        --24時間表記で、24+基準時刻の入力までは許可
        IF houre_time - root_time >= 24 THEN
            --範囲外の入力は、正式な解釈が出来ないため無効
            RETURN NULL;
        END IF;
    END IF;
    --開始時間を基点として時間に変更
    houre_time := houre_time - root_time;
    houre_time := houre_time * 60;           --時間単位を分に変換
    total_time := houre_time + minute_time;  --基点時間を計算する
    RETURN total_time;
End;
' LANGUAGE 'plpgsql';

/*-----------------------------------------------------*/
/**
 * TO_CLOCK()
 * 基準時間からの経過分を引数に受け取り、時刻形式の表現を返します。
 * 経過分の有効範囲は、 0〜1439、-1〜-1439 とします。
 * 有効範囲外の数値が引数に与えられた場合、NULLを返します。
 * この為、集計関数中で使用した場合、有効範囲外の数値の影響で集計
 *結果がNULLになる様に考慮してあります。
 * ※プロシージャ作成時に基準時刻(root_time)を変更しておく必要があります。
 *
 * @language     plpgsql
 * @date         2001/11/14
 * @author       t_suzuki
 * @param        integer  基準時間からの経過時間を分単位で表した数値
 * @return       text     ":"区切りの時刻表現された文字列
 */
CREATE or REPLACE FUNCTION to_clock(int)
RETURNS text AS '
Declare
    ins_minute alias for $1;    --引数の別名
    root_time CONSTANT integer NOT NULL DEFAULT 7;    --基準時刻
    clean_minute    integer;    --範囲チェック完了後の値
    hour_time       text;       --時間単位の表示部分
    minute_time     text;       --分単位の表示部分
    clock_text      text;       --戻り値
Begin
    /**
     * 入力範囲チェック
     *   有効範囲 0〜1439/-1〜-1439 (前後基準時間を考慮)
     *   引数がマイナスの場合、基準時刻からの逆経過時間とする
     */
    IF ins_minute >= 0 AND ins_minute < 1440 + root_time * 60 THEN
        --正の有効値をセット
        clean_minute := ins_minute;
    ELSE
        IF ins_minute < 0 AND ins_minute > -1440 - root_time * 60 THEN
            --負の有効値をセット
            clean_minute := ins_minute + 1440;
        ELSE
            --有効範囲外 NULL値を返す
            RETURN NULL;
        END IF;
    END IF;
    /**
     * 整形処理
     * "24:00"を"0:00"と出力する場合、※1の条件をコメントアウトして
     * ※2を使用する。
     */
    --時刻を計算し、TEXT 型に整形する
    --※1  24時以上をそのまま表示する条件
    IF clean_minute >= 1440 THEN
    --※2  24時以上を繰り下げる条件
--  IF clean_minute >= 1440 - root_time * 60 THEN
        hour_time := to_char(clean_minute / 60 - 24 + root_time, ''00'');
    ELSE
        hour_time := to_char(clean_minute / 60 + root_time, ''00'');
    END IF;
    --分を計算し、TEXT 型に整形する
    minute_time    := to_char(clean_minute % 60 , ''00'');
    --文字列の結合
    hour_time  := ltrim(hour_time);
    minute_time := ltrim(minute_time);
    clock_text  := hour_time || '':'' || minute_time;
    RETURN clock_text;
End;
' LANGUAGE 'plpgsql';

----------------------------------------------------
■使用方法
・7:00(基準時刻)からの経過分を返します。
# select to_interval('9:00');
 to_interval
----------
           120

# select to_clock(120);
 to_clock
--------
 09:00

日付を超えた、「在場時間 = 退勤時間 - 出勤時間」は、
以下のように問い合わせ可能です。
select to_clock(to_interval(out_time) - to_interval(in_time)) from jobday;

#目的指向が強く、汎用性はないものですが…。
 -----------------------------------------
      鈴木 徹 (SUZUKI Toru)
      KENWOOD ENGINEERING CORPORATION
      E-mail:t_suzuki @ kenwood-eng.co.jp
 -----------------------------------------




pgsql-jp メーリングリストの案内