スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

PL/pgSQLで指定された曜日の日付を取得する

ある日付を与えて、その日を含めてその日から指定の曜日の一番近い日を求める関数です。
指定する曜日は0が日曜日で6が土曜日になる数字で、可変長引数で必要な曜日をすべて渡せます。

最初作った時は、日付を見て曜日を確認して入ってなければ、一日足してを繰り返していました。
そうすると、最長で7回ループするので、スピードがもったいないなぁと思い、今のような形になっています。
すこしギミックを効かせすぎたせいか、ソースが長くなっています。

配列の値に含まれるかどうかを判定するのに、ANYを使っています。
値 = ANY(配列)
とすると、配列のどれか一つの要素に一致していたら、trueを返します。

曜日を求めるにはdate_part('dow', 時間)を使います。dowって何だろう?


CREATE OR REPLACE FUNCTION uv_get_date_by_week(
p_ts timestamp with time zone
,p_week_ary VARIADIC int[] DEFAULT null
) RETURNS timestamp with time zone AS $$
DECLARE
w_dow int := date_part('dow', p_ts);
w_next int;
w_min int;
w_count int;
w_week int;
BEGIN
IF p_ts IS NULL OR p_week_ary IS NULL THEN
RETURN null;
END IF;
IF w_dow = ANY(p_week_ary) THEN
-- 曜日が一致したら返す
RETURN p_ts;
ELSE
w_count := array_length(p_week_ary, 1);
w_next := 7;
w_min := p_week_ary[1];
FOR i IN 1..w_count LOOP
w_week := p_week_ary[i];
IF w_dow + 1 = w_week THEN
-- 一つずれを発見したら翌日を返す
RETURN uv_add_time(p_ts, 1, 'day');
END IF;
-- 現在の曜日よりも大きい最小のものを検索
IF w_next > w_week AND w_dow < w_week THEN
w_next := w_week;
END IF;
-- 最小の曜日を検索
IF w_min > w_week THEN
w_min := m_min;
END IF;
END LOOP;
IF 7 <> w_next THEN
-- 現在の曜日よりも大きい場合
RETURN uv_add_time(p_ts, w_next - w_dow, 'day');
ELSE
-- 現在の曜日よりも小さい場合
RETURN uv_add_time(p_ts, 7 + w_min - w_dow, 'day');
END IF;
END IF;
END;
$$ LANGUAGE plpgsql;


使用例
select uv_get_date_by_week('2010-02-01', 0)
→"2010-02-07 00:00:00+09"

select uv_get_date_by_week('2010-02-01', 1)
→"2010-02-01 00:00:00+09"

select uv_get_date_by_week('2010-02-01', 1, 2, 3, 4, 5)
→"2010-02-01 00:00:00+09"


一行入魂サイトにまとめがあります。
スポンサーサイト

COMMENTS

COMMENT FORM

TRACKBACK


この記事にトラックバックする(FC2ブログユーザー)

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。