例:
INTERVAL '5 minute'
の場合、必要な出力は2018-05-17 22:50:00です。INTERVAL '10 minute'
の場合、必要な出力は2018-05-17 22:50:00です。INTERVAL '1 hour'
の場合、必要な出力は2018-05-17 23:00:00です。INTERVAL '1 day'
の場合、必要な出力は2018-05-18 00:00:00です。データ型timestamp
を想定しています。 date
またはtimestamptz
の詳細は一部異なります。
any時間間隔の一般的な解決策は、Epoch値と整数除算に基づいて切り捨てることができます。すべての例をカバーします。
あなたのタスクの特別な困難: floor (より一般的です)ではなく、 ceiling が必要です。コーナーケースのバグを回避するために、下限と上限に注意してください。床の値をexact増やしたくない場合。 (または私はそう思います。)
組み込みの一般的な時間間隔 date_trunc()
(例では_1 hour
_および_1 day
_のように)ショートカットを使用します。 daysの定義は、timestamptz
を使用する(timestamp
を使用しない)セッションのタイムゾーン設定に依存します。
自然な代替手段は ceil()
です。テストでは少し遅いですが、よりクリーンです。
_-- short demo WITH t(ts) AS (SELECT timestamp '2018-05-17 22:45:30') -- your input timestamp SELECT t2.* FROM (SELECT *, ts - interval '1 microsecond' AS ts1 FROM t) t1 -- subtract min time interval 1 µs , LATERAL ( VALUES ('input timestamp' , ts) , ('5 min' , to_timestamp(trunc(extract(Epoch FROM ts1))::int / 300 * 300 + 300) AT TIME ZONE 'UTC') , ('10 min', to_timestamp(ceil (extract(Epoch FROM ts)/ 600) * 600) AT TIME ZONE 'UTC') -- based on unaltered ts! , ('hour' , date_trunc('hour', ts1) + interval '1 hour') , ('day' , date_trunc('day' , ts1) + interval '1 day') ) t2(interval, ceil_ts);
_間隔| ceil_ts :-------------- | :------------------ 入力タイムスタンプ| 2018-05-17 22:45:30 5分| 2018-05-17 22:50:00 10分| 2018-05-17 22:50:00 時間| 2018-05-17 23:00:00 日| 2018-05-18 00:00:00
'5 min'計算の「トリック」は、 1 µs の最小時間間隔を差し引いてから切り捨ててから、 ceilingを効果的に取得するためのそれぞれの時間間隔 EXTRACT()
は、タイムスタンプの秒数、小数桁がマイクロ秒までの_double precision
_数値を返します。 trunc()
が必要です。これは、integer
への単純なキャストがround、truncateする必要があります。
このようにして、上限に該当するタイムスタンプの増分を回避します。ただし、最小の時間間隔は現在のPostgresバージョンの実装の詳細であるため、少し汚れています。 非常にしかし、変更される可能性はほとんどありません。関連:
'10 min'の計算はceil()
の方が簡単です。1µsを減算して境界をシフトする必要はありません。クリーナー。しかし、ceil()
は、私のテストでは少し高価です。
_WITH t(id, ts) AS ( VALUES (1, timestamp '2018-05-17 22:45:30') -- your input timestamps here , (2, timestamp '2018-05-20 00:00:00') , (3, timestamp '2018-05-20 00:00:00.000001') ) SELECT * FROM (SELECT *, ts - interval '1 microsecond' AS ts1 FROM t) t1 -- subtract min time interval 1 µs , LATERAL ( VALUES ('input timestamp' , ts) , ('5 min' , to_timestamp(trunc(extract(Epoch FROM ts1))::int / 300 * 300 + 300) AT TIME ZONE 'UTC') , ('10 min' , to_timestamp(ceil (extract(Epoch FROM ts)/ 600) * 600) AT TIME ZONE 'UTC') -- based on unaltered ts! , ('hour' , date_trunc('hour', ts1) + interval '1 hour') , ('day' , date_trunc('day' , ts1) + interval '1 day') , ('alt_day', ts1::date + 1) ) t2(interval, ceil_ts) ORDER BY id;
_id | ts | ts1 |インターバル| ceil_ts -:| :------------------------- | :------------------------- | :-------------- | :------------------------- 1 | 2018-05-17 22:45:30 | 2018-05-17 22:45:29.999999 |入力タイムスタンプ| 2018-05-17 22:45:30 1 | 2018-05-17 22:45:30 | 2018-05-17 22:45:29.999999 | 5分| 2018-05-17 22:50:00 1 | 2018-05-17 22:45:30 | 2018-05-17 22:45:29.999999 | 10分| 2018-05-17 22:50:00 1 | 2018-05-17 22:45:30 | 2018-05-17 22:45:29.999999 |時間| 2018-05-17 23:00:00 1 | 2018-05-17 22:45:30 | 2018-05-17 22:45:29.999999 |日| 2018-05-18 00:00:00 1 | 2018-05-17 22:45:30 | 2018-05-17 22:45:29.999999 | alt_day | 2018-05-18 00:00:00 2 | 2018-05-20 00:00:00 | 2018-05-19 23:59:59.999999 |入力タイムスタンプ| 2018-05-20 00:00:00 2 | 2018-05-20 00:00:00 | 2018-05-19 23:59:59.999999 | 5分| 2018-05-20 00:00:00 2 | 2018-05-20 00:00:00 | 2018-05-19 23:59:59.999999 | 10分| 2018-05-20 00:00:00 2 | 2018-05-20 00:00:00 | 2018-05-19 23:59:59.999999 |時間| 2018-05-20 00:00:00 2 | 2018-05-20 00:00:00 | 2018-05-19 23:59:59.999999 |日| 2018-05-20 00:00:00 2 | 2018-05-20 00:00:00 | 2018-05-19 23:59:59.999999 | alt_day | 2018-05-20 00:00:00 3 | 2018-05-20 00:00:00.000001 | 2018-05-20 00:00:00 |入力タイムスタンプ| 2018-05-20 00:00:00.000001 3 | 2018-05-20 00:00:00.000001 | 2018-05-20 00:00:00 | 5分| 2018-05-20 00:05:00 3 | 2018-05-20 00:00:00.000001 | 2018-05-20 00:00:00 | 10分| 2018-05-20 00:10:00 3 | 2018-05-20 00:00:00.000001 | 2018-05-20 00:00:00 |時間| 2018-05-20 01:00:00 3 | 2018-05-20 00:00:00.000001 | 2018-05-20 00:00:00 |日| 2018-05-21 00:00:00 3 | 2018-05-20 00:00:00.000001 | 2018-05-20 00:00:00 | alt_day | 2018-05-21 00:00:00
db <> fiddle ここ
丸一日の代替ショートカットを追加しました:_ts1::date + 1
_。 date
へのキャストは丸一日に切り捨てられ、integer
1を追加して1日を追加できます。
後でtimestamptz
を使用することを開示したので、式から_AT TIME ZONE
_を削除できます。
私のテストでは、関数STABLE
を宣言すると、関数のインライン化が可能になったため、最高のパフォーマンスが得られました。私はIMMUTABLE
が最良であると期待していましたが、その宣言は、内部にインライン化できるものについてよりうるさいです。関連:
私のテストでは少し速くなりました:
_CREATE OR REPLACE FUNCTION f_tstz_interval_ceiling2(_tstz timestamptz, _int_seconds int)
RETURNS timestamptz AS
$func$
SELECT to_timestamp(trunc(extract(Epoch FROM ($1 - interval '1 microsecond')))::int / $2 * $2 + $2)
$func$ LANGUAGE sql STABLE;
_
クリーナーIMO:
_CREATE OR REPLACE FUNCTION f_tstz_interval_ceiling1(_tstz timestamptz, _int_seconds int)
RETURNS timestamptz AS
$func$
SELECT to_timestamp(ceil(extract(Epoch FROM $1) / $2) * $2)
$func$ LANGUAGE sql STABLE;
_
コール:
_SELECT f_tstz_interval_ceiling1(my_tstz, 600); -- 600 = seconds in 10 min
_
便宜上、interval
を_$2
_として取る別の方法で各関数をオーバーロードできます。
_CREATE OR REPLACE FUNCTION f_tstz_interval_ceiling1(_tstz timestamptz, _interval interval)
RETURNS timestamptz LANGUAGE sql STABLE AS
'SELECT f_tstz_interval_ceiling1($1, extract(Epoch FROM $2)::int)';
_
秒を抽出して最初のバージョンを呼び出すだけです。次に、以下を呼び出すこともできます。
_SELECT f_tstz_interval_ceiling1(my_tstz, interval '10 min');
_
3つのケースそれぞれについて、例を挙げます。
_select now(), date_trunc('hour', now()), date_trunc('hour', now()) + (10 * round(extract(minute from now())/10) || ' minute')::interval;
now | date_trunc | ?column?
-------------------------------+------------------------+------------------------
2018-05-17 19:21:44.797717-05 | 2018-05-17 19:00:00-05 | 2018-05-17 19:20:00-05
(1 row)
select now(), date_trunc('day', now()), date_trunc('day', now()) + (1 * round(extract(hour from now())/1) || ' hour')::interval;
now | date_trunc | ?column?
-------------------------------+------------------------+------------------------
2018-05-17 19:22:34.508226-05 | 2018-05-17 00:00:00-05 | 2018-05-17 19:00:00-05
(1 row)
select now(), date_trunc('month', now()), date_trunc('month', now()) + (1 * round(extract(day from now())/1) || ' day')::interval;
now | date_trunc | ?column?
-------------------------------+------------------------+------------------------
2018-05-17 19:23:56.562104-05 | 2018-05-01 00:00:00-05 | 2018-05-18 00:00:00-05
(1 row)
_
だから基本的に
_date_trunc('X', now()) + (Y * round(extract(Z from now())/Y) || ' Z')::interval
_
あなたは交換する必要があります:
そしてもちろんnow()
をあなたが扱っているタイムスタンプに置き換えてください。
これは、特定のタイムスタンプに対して、指定した間隔値とステップに基づいてこれを作成する関数で抽象化できます。
必要な切り捨てを正確に理解しているとは限らないため、round
をceil
に置き換える必要がある場合があります。
現在、サーバー年のタイムスタンプストレージのデフォルトの方法であるINTEGER_DATETIMES
(ビルドオプション)を想定しています。
timestamp
(またはtimestamp with timezone
)からbigint
へのキャストを作成し、それらを2000-01-01 00:00
からのマイクロ秒の整数として操作して、timestamp
(with timezone
)
CREATE CAST (bigint AS timestamp) WITHOUT FUNCTION;
CREATE CAST (timestamp AS bigint) WITHOUT FUNCTION;
/
のような整数演算を使用して、タイムスタンプの 内部表現 を操作できるようになりました。
次の2週間に転送します。
-- 1209600000000 microseconds is two weeks.
select (((now()::timestamp::bigint-1)/1209600000000+1) * (1209600000000))::timestamp;
そう
create or replace function timestamp_ceil(timestamp,interval)
returns timestamp
language plpgsql
as $$
declare
i bigint = 1000000*extract( Epoch from $2);
ts bigint = $1::bigint;
begin
return (((ts-1)/i + (ts>0)::int )*i)::timestamp;
end $$;
免責事項、これは一定サイズの間隔でのみ機能し、「月」や「年」のような不規則なサイズの間隔は「30日」と「365.25日」に折りたたまれます。
負の数値はゼロに丸められるため、(ts>0)::int
の代わりに1
が必要であるため、増分は必要ありません。 (booleanからintへのキャストは、結果として0または1になります)