Postgres function to return datetime with time zone offset - sql

Instead of always writing:
select my_column at time zone 'UTC' at time zone 'Europe/Paris' from my_table;
I would prefer doing:
select dtz(my_column, 'Europe/Paris') from my_table;
UTC is system-wide (server time zone), and could even be hardcoded in dtz().
Can someone share an efficient implementation of such a function?
Edit
select my_column at time zone 'UTC' at time zone 'Europe/Paris' from my_table;
Can be shortened a very very little bit like this:
select timezone('UTC', my_column) at time zone 'Europe/Paris' from my_table;

Its really dificult to see what you are having trouble with on this.
I don't believe the server timezone helps you here. As I understand it, it's the SQL client timezone than affects this.
You're not going to get a function more efficient than (IMMUTABLE):
RETURN arg1 AT TIME ZONE 'UTC' AT TIME ZONE arg2;
But this will always be less efficient than writing it inline in your SQL.
Ideally your timestamp should be TIMESTAMP WITH TIMEZONE instead of TIMESTAMP WITHOUT TIMEZONE since it's being used to store absolute timestamps not local timestamps. This would prevent the need for two conversions (into UTC and out of it). But that does mean you would need to add the AT TIME ZONE 'UTC' into your INSERT and UPDATE statements.

Related

Add offset value to time in SQL Server

I felt like this should be easy but I'm not finding answer. The query below will give me the current date/time in California and represent it as UTC with a timezone offset (currently -08:00):
select getdate() at time zone 'Pacific Standard Time'
I simply want to apply the offset and get a timestamp showing the current local time and then drop the offset. I feel like there should be a way to achieve this without having to pick through it with string and date functions. For example, I want to go from
2021-11-24 18:03:41.190 -08:00
to
2021-11-24 10:03:41.190
Is there a succinct way to do this?
When you use AT TIME ZONE on a value that isn't a datetimeoffset it's assumed that the value is at the time zone you are converting it to.
Instead, therefore, you could nest the AT TIME ZONE clauses. If you did do this though, I would also suggest using SYSUTCDATE rather than GETDATE (which returns the local time to the host, which could be any timezone).
SELECT SYSUTCDATETIME() AT TIME ZONE 'UTC' AT TIME ZONE 'Pacific Standard Time';
Though really, just use SYSDATETIMEOFFSET to start with; then you just need one AT TIME ZONE as the RDBMS already knows what timezone the value is for.
SELECT SYSDATETIMEOFFSET() AT TIME ZONE 'Pacific Standard Time';
db<>fiddle
Declare #myDateTime datetime = '2021-11-24 21:35:25.984'; --UTC time
Select cast(#myDateTime At Time Zone 'UTC' At Time Zone 'Pacific Standard Time' As datetime);
Or - you can just apply the desired offset and convert back to datetime
Select cast(switchoffset(#myDateTime, '-08:00') As datetime)
The problem with using switchoffset is that it isn't DST aware and would need to be changed to use a different offset value. If you change the date above to October - the PST time would be 14:35:25.983 but the second calculation still returns 13:35:25.983

Postgres: meaning of `timestamp without time zone at time zone`

I am having some trouble understanding how to deal in Postgres with time zone semantic:
Consider this table
TABLE MyTable (
MyDate TIMESTAMP NOT NULL,
// other columns
);
And this query
SELECT *
FROM MyTable
WHERE // conditions
AND tstzrange(#{start} ::timestamp with time zone, #{end} ::timestamp with time zone] #> MyDate::timestamp without time zone at time zone 'CET'
I understand that Postgres only stores the epoch value i.e. no time zone info is ever stored, so I understand the need to specify timezone for start and end as they are formatted strings that Postgres needs to calculate the epoch for.
What I don't really understand is:
MyDate::timestamp without time zone at time zone 'CET'
Postgres knows the epoch for MyDate since it's their values are stored, why the need to "convert" to a time zone ?
What are we actually saying here and can this be simplified ?
Postgres never stores timezone information in timestamp or timestamptz. In the timestamptz case the timestamp being stored is rotated to a UTC value using either the timezone information in the presented timestamp or the value of the setting TimeZone and then stored. In the timestamp case that is not done. On output a timestamptz is rotated back from UTC to whatever the TimeZone setting is or what via at time zone <some_tz>. In the timestamp case the retrieved value is assumed to be the setting of TimeZone unless you override with at time zone <some_tz>. Best practices is that you use timestamptz.

Is there an SQL function to convert the time zone to local time (LDT)?

I am quite new to SQL, and trying to pull a data table from the database (flight) using the following command:
select
flight.FLT_NBR,
flight.LEG_NBR,
flight.LEG_TAIL_NBR,
flight.LEG_IATA_ORIG_CD as FLT_SCHD_ORIG_ARPT_CD,
flight.LEG_IATA_DEST_CD as FLT_SCHD_DEST_ARPT_CD,
flight.SCHD_ARR_TMSTP as Scheduled_Arrival,
flight.ACTL_ARR_TMSTP AS Actual_Arrival,
flight.SCHD_DPRT_TMSTP as Scheduled_Departure,
flight.ACTL_DPRT_TMSTP AS Actual_Departure,
from home/tulips/FT_FLIGHT_LEG flight
Now the problem is there are multiple country origin and destination with different times. How do I incorporate same time zone for all the countries? I tried using the command as time zone 'UTC' such as below but it didn't work... May be I am adding it in a wrong place?
select
flight.FLT_NBR,
flight.LEG_NBR,
flight.LEG_TAIL_NBR,
flight.LEG_IATA_ORIG_CD as FLT_SCHD_ORIG_ARPT_CD,
flight.LEG_IATA_DEST_CD as FLT_SCHD_DEST_ARPT_CD,
flight.SCHD_ARR_TMSTP as Scheduled_Arrival as time zone 'UTC',
flight.ACTL_ARR_TMSTP AS Actual_Arrival as time zone 'UTC',
flight.SCHD_DPRT_TMSTP as Scheduled_Departure as time zone 'UTC',
flight.ACTL_DPRT_TMSTP AS Actual_Departure as time zone 'UTC',
from home/tulips/FT_FLIGHT_LEG flight
Please help me a way to have one time zone for all the Scheduled_Arrival,Actual_Arrival,Scheduled_Departure and Actual_Departure
The expression you want is at time zone, not as time zone.
In order to use it, you need to know what time zone the original datetime value represents. For example, I have a SQL server in Sydney Australia, so getdate() will return my local date and time. However, to convert it to UTC I must first inform SQL of the fact that the value starts off in AUS Eastern Standard Time, and then ask it to convert it to UTC, by chaining at time zone expressions together. Like this:
select getdate() at time zone 'AUS Eastern Standard Time' at time zone 'UTC'
If you don't know the time zone of the original datetime value, there is no way for SQL to know how to change it to a different time zone's value.

Get postgres to show all TIMESTAMP WITH TIME ZONE columns in UTC

When I do a SELECT statement that outputs a TIMESTAMP WITH TIME ZONE column, it doesn't include the time zone in the output. It just outputs the date and time without the timezone, after converting it to my local timezone.
I have 2 questions:
How to get all TIMESTAMP WITH TIME ZONE columns to always display their time zones in the output, rather than the default of omitting it?
How to get all TIMESTAMP WITH TIME ZONE columns to output with a default time zone of UTC? I know there's ways to do this on a per-query basis, but I'd like to do it in every case with needing to modify any queries. So a server setting maybe?
for one column:
SELECT colname AT TIME ZONE 'UTC' FROM tablename;
for all
SET TIMEZONE TO 'UTC';
see also ALTER ... SET ...

Insert time with timezone daylight savings

I would like to insert time data type in postgresql that includes the timezone and is aware of daylight savings time. This is what I have done:
CREATE TABLE mytable(
...
start_time time(0) with time zone,
end_time time(0) with time zone
)
INSERT INTO mytable(start_time, end_time)
VALUES(TIME '08:00:00 MST7MDT', TIME '18:00:00 MST7MDT')
I get the following error:
invalid input syntax for type time: "08:00:00 MST7MDT"
It works if I use 'MST' instead of 'MST7MDT', but I need it to be aware of DST. I also tried using 'America/Edmonton' as the timezone, but I got the same error.
What is the proper way to insert a time value (not timestamp) with timezone and DST?
EDIT:
I would actually like to use the 'America/Edmonton' syntax
The proper way is not to use time with time zone (note the space between time and zone) at all, since it is broken by design. It is in the SQL standard, so Postgres supports the type - but advises not to use it. More in this related answer:
Accounting for DST in Postgres, when selecting scheduled items
Since you are having problems with DST, timetz (short name) is a particularly bad choice. It is ill-equipped to deal with DST. It's impossible to tell whether 8:00:00 is in winter or summer time.
Use timestamp with time zone (timstamptz) instead. You can always discard the date part. Simply use start_time::time to get the local time from a timestamptz. Or use AT TIME ZONE to transpose to your time zone.
Generally, to take DST into account automatically, use a time zone name instead of a time zone abbreviation. More explanation in this related question & answer:
Time zone names with identical properties yield different result when applied to timestamp
In your particular case, you could probably use America/Los_Angeles (example with timestamptz):
INSERT INTO mytable(start_time, end_time)
VALUES
('1970-01-01 08:00:00 America/Los_Angeles'
, '1970-01-01 18:00:00 America/Los_Angeles')
I found this by checking:
SELECT * FROM pg_timezone_names
WHERE utc_offset = '-07:00'
AND is_dst;
Basics about time zone handling:
Ignoring time zones altogether in Rails and PostgreSQL
How about this?
INSERT INTO mytable(start_time, end_time)
VALUES('08:00:00'::time at time zone 'MST7MDT', '18:00:00'::time at time zone 'MST7MDT')