As part of a larger Postgres query, I generate all years between two given timestamps (timestamptz
):
select to_char(generate_series, 'YYYY') from generate_series(
'2022-06-14 11:00:00.000+00'::timestamptz,
'2023-06-14 11:00:00.000+00'::timestamptz,
'1 year' );
Which returns:
'2022'
'2023'
The issue is, if there is less than one year between both timestamps, only the first year is returned. I need a set of all involved years, regardless of the the interval between both timestamps, e.g.:
select to_char(generate_series, 'YYYY') from generate_series(
'2022-06-14 11:00:00.000+00'::timestamptz,
'2023-06-13 11:00:00.000+00'::timestamptz,
'1 year' );
Only returns:
'2022'
But I would like it to return:
'2022'
'2023'
Is there some way to achieve this?
The year of a timestamptz
(timestamp with time zone]
) value is not strictly determined. It's still the year 2024 in New York, when I wish "Happy New Year 2025" in Vienna.
Only date
or timestamp
(timestamp without time zone
) are deterministic in this regard.
To avoid corner case issues you must define the time zone for your query in one way or another.
Here is one way:
SELECT generate_series(EXTRACT('year' FROM '2022-06-14 11:00+0'::timestamptz AT TIME ZONE 'Europe/Vienna')::int
, EXTRACT('year' FROM '2023-06-13 11:00+0'::timestamptz AT TIME ZONE 'Europe/Vienna')::int) AS the_year;
Here is another:
SELECT EXTRACT('year' FROM ts)::int AS the_year
FROM generate_series(date_trunc('year', '2022-06-14 11:00+0'::timestamptz, 'Europe/Vienna')
, date_trunc('year', '2023-06-13 11:00+0'::timestamptz, 'Europe/Vienna')
, interval '1 year') ts;
date_trunc()
allows a 3rd parameter to pass the time zone since Postgres 12 - which only makes sense for timestamptz
, obviously.
Replace with your time zone, or use timestamp
values to begin with.
Assuming the second timestamp is guaranteed to be after the first, or you have to do more / define how to deal with it.
Basics: