Eva*_*oll 12 postgresql timestamp timezone datetime date
假设我想在两个日期之间生成一系列日期。我看到该功能generate_series
仅提供
Function Argument Type Return Type Description
generate_series(start, stop, step interval) timestamp or timestamp with time zone setof timestamp or setof timestamp with time zone (same as argument type) Generate a series of values, from start to stop with a step size of step
Run Code Online (Sandbox Code Playgroud)
那么我该怎么做呢?
Eva*_*oll 19
您可以generate_series
为此使用,但一定要明确地将参数转换为“timestamp without time zone”,否则它们将默认为“timestamp with timezone”。generate_series
两个输入的PostgreSQL 重载。
timestamp with timezone
你可以在这里看到缺点。
SET timezone = 'America/Santiago';
SELECT generate_series(date '2016-08-15', date '2016-08-15', '1 day');
SELECT generate_series(date '2016-08-14', date '2016-08-15', '1 day');
Run Code Online (Sandbox Code Playgroud)
以上两者都返回相同的天数。你可以在这里再次看到它。
SET timezone = 'America/Sao_Paulo';
SELECT generate_series(date '2016-10-16', date '2016-10-17', '1 day');
SELECT generate_series(date '2016-10-17', date '2016-10-17', '1 day');
Run Code Online (Sandbox Code Playgroud)
上面显示了一天的两个范围。
这种行为的原因是这些时区在午夜有他们的“夏令时边界,而不是在几个小时内更合理的时间”
那么“做对了”是什么样子的,
SELECT generate_series(
timestamp without time zone '2016-10-16',
timestamp without time zone '2016-10-17',
'1 day'
);
Run Code Online (Sandbox Code Playgroud)
现在你可以投射到日期..
SELECT d::date
FROM generate_series(
timestamp without time zone '2016-10-16',
timestamp without time zone '2016-10-17',
'1 day'
) AS gs(d);
Run Code Online (Sandbox Code Playgroud)
这个问题和答案的灵感来自与 RhodiumToad 在 IRC (irc://irc.freenode.net/#postgresql) 上的对话。他改变了我这个问题并提供了解决方案。
generate_series(date,date,interval)
玩玩,我发现我也许可以省去timestamp without time zone
通过重载显式转换为generate_series(date,date,interval)
这是我的功能,
CREATE FUNCTION generate_series( t1 date, t2 date, i interval )
RETURNS setof date
AS $$
SELECT d::date
FROM generate_series(
t1::timestamp without time zone,
t2::timestamp without time zone,
i
)
AS gs(d)
$$
LANGUAGE sql
IMMUTABLE;
Run Code Online (Sandbox Code Playgroud)
现在我可以重新运行上面的测试用例,它不再可疑。这两个都返回相同的东西,
SET timezone = 'America/Santiago';
SELECT d::date
FROM generate_series(date '2016-08-15', date '2016-08-15', '1 day')
AS gs(d);
SELECT d::date
FROM generate_series(
timestamp without time zone '2016-08-15',
timestamp without time zone '2016-08-15',
'1 day'
)
AS gs(d);
Run Code Online (Sandbox Code Playgroud)
和这两个一样,
SELECT d::date
FROM generate_series(date '2016-08-14', date '2016-08-15', '1 day')
AS gs(d);
SELECT d::date
FROM generate_series(
timestamp without time zone '2016-08-14',
timestamp without time zone '2016-08-15',
'1 day'
)
AS gs(d);
Run Code Online (Sandbox Code Playgroud)
generate_series(date,date,int)
另一种选择是创建一个新函数,generate_series(date,date,int)
但是由于这里提到的原因,您不能同时拥有这两个函数。所以选择其中之一,
generate_series(date,date,interval)
generate_series(date,date,int)
Run Code Online (Sandbox Code Playgroud)
如果你想要第二个选项,试试这个:
CREATE FUNCTION generate_series( t1 date, t2 date, i int )
RETURNS setof date
AS $$
SELECT d::date
FROM generate_series(
t1::timestamp without time zone,
t2::timestamp without time zone,
i * interval '1 day'
)
AS gs(d)
$$
LANGUAGE sql
IMMUTABLE;
Run Code Online (Sandbox Code Playgroud)
随着irc的审查,这些想法存在一些问题,
< johto>
generate_series(date, date, unknown)
今天已经开始工作了。当您不使用 int 版本(例如generate_series(date, date, '1 day')
)彻底打破它时,您将返回类型从 timestamptz 更改为 date。(date, date, interval)
会打破更少的情况,但你仍然会改变输出类型。((date, date, '1 hour')
目前“工作正常”应该发生什么也不清楚 )