Suppose I have this table structure:
Table "test"
Column | Type | Modifiers
----------------+-----------------------------+-----------
uuid | uuid | not null
created | timestamp without time zone | not null
How would I select records after a certain date? But also factor in a specific timezone?
Since created is timestamp without zone, we can assume it's UTC
Example query:
select uuid from test where created >= '2017-07-20'
This would return all events that happend on, or after 2017-07-20 00:00:00.000 UTC How would I query for events that happend after say, 2017-07-20 00:00:00.000 GMT+2 ? Without having to add hours to my argument in created > arg
select uuid
from test
where created > '2017-07-20'::timestamp with time zone at time zone '+02';
I think one approach would be to compare the number of seconds since the epoch between your UTC timestamp in the table and some other input:
select uuid
from test
where
extract(epoch from created) >
extract(epoch from timestamp with time zone
'2017-07-19 23:59:59.999'::timestamp with time zone at time zone 'GMT')
The above syntax is verbose, and there may be a shorter way of writing this query. As a general rule, when you find yourself jumping through hoops to answer simple queries, it might mean you need to change your design. If you were storing your timestamps in UTC and running queries from an application, you would probably be passing in local timestamps already converted to UTC, which would make things much simpler.
Related
I have a table like this.
create table public.test123
(id int not null primary key, dt timestamp null default clock_timestamp());
I then insert into it from 2 different client sessions,
the first session is from PG Admin, the second one is from DBeaver.
1) In the first session when I run show timezone; it returns UTC.
So this session's timezone is UTC.
So from this session I do an insert as follows.
insert into public.test123(id) values (1);
2) In the second session I have timezone America/New_York and I do this.
insert into public.test123(id) values (2);
I run the two inserts just a few seconds apart from each other.
But I get two very different values in the DB e.g.
id|dt |
--|-------------------|
1|2020-06-05 14:38:18|
2|2020-06-05 10:38:26|
I always thought that in such scenario the clock_timestamp() call is executed on the server, and the timezone of the client session should not matter. And I expected to get two values which are a few seconds apart, and not 4 hours apart.
What am I missing? Could anyone explain in some details please.
And also... is there any way to get a timestamp independent of the client session's timezone?
How can I create a column with default timestamp values
which are really independent on the client session's timezone?
tl;dr: Switch to timestamptz.
I'll refer to the timestamp types by their short names: timestamp without time zone is timestamp and timestamp with time zone is timestamptz.
Both timestamp and timestamptz store an absolute point in time. timestamp with time zone also records the time zone it was entered in.
For example, 2020-06-05 20:22:48Z and 2020-06-05 13:22:48-0700 both represent the same moment in time, but the first is in Universal Coordinated Time and the other is 7 hours offset (Pacific Daylight Time).
timestamptz will store 2020-06-05 13:22:48-0700 as 2020-06-05 20:22:48, the absolute point in time, but also remember that it was entered with 7 hours offset. When you retrieve it you'll get 2020-06-05 13:22:48-0700 which is the same time as 2020-06-05 20:22:48Z.
timestamp will ignore the time zone completely. It stores 2020-06-05 13:22:48-0700 as 2020-06-05 13:22:48, no time zone, no conversion, and that's it.
On to the specific problem: dt timestamp null default clock_timestamp()
clock_timestamp() returns timestamptz, but you're storing it in a timestamp. Postgres will cast one to the other by simply dropping the time zone. It will not convert it. If the time zone is in UTC and it's 2020-06-05 20:22:48Z it will store 2020-06-05 20:22:48. If the time zone is in America/New York and it's 2020-06-05 16:22:48-0400 it will store 2020-06-05 16:22:48.
And also... is there any way to get a timestamp independent of the client session's timezone?
They are all independent of the client's time zone, it's the type conversion that's a problem. Switch to timestamptz.
Time zones are complicated, but here's two strategies to deal with them when moving timestamps in and out of the database.
Use timestamp and always work in UTC.
Use timestamptz and always include a time zone.
The former is simpler, and many frameworks take this approach. But I prefer the latter because, as complex and infuriating as they are, people like the time to be vaguely in sync with the Sun.
Yes, timestamp with time zone, which is an absolute timestamp, would be the correct data type in your case.
If you use timestamp without time zone, the result of clock_timestamp(), which is a timestamp with time zone, is converted (cast implicitly) to a timestamp without time zone according to the current setting of timezone.
I have this simple query
MyLog
| summarize avg(executionTimeInMS_d) by bin(TimeGenerated, 5min)
I'd like the summary to be in my local time zone, not UTC. This does not work :
MyLog
| summarize avg(executionTimeInMS_d) by bin(TimeGenerated-5, 5min)
Can this be done?
datetime values are in UTC.
if you know the timezone offset (at the time you run the query), you can subtract/add it to your datetime values as explained here: https://learn.microsoft.com/en-us/azure/kusto/query/datetime-timespan-arithmetic
for example: print now() - 7h
There is now a "Display time zone" setting in the App Insights query page. This will convert the timestamp to the selected timezone. It will also show the timezone in the timestamp column heading.
This only seems to work if you output the timestamp directly. If you apply any operations it reverts to UTC.
Best to convert using the datetime_utc_to_local() function. This way you can dynamically handle daylight savings within the query and don't need to depend on the UI.
AzureDiagnostics
| extend CentralTime = datetime_utc_to_local(TimeGenerated, "US/Central")
You can do this by subtracting/adding the time different from UTC. For example, to convert to EST. I subtracted 5h from TimeGenerated which is in UTC.
AppServiceConsoleLogs
| extend EasternTime = TimeGenerated - 5h
| sort by EasternTime desc
| project Level, EasternTime, ResultDescription
I've fiddeling around with timestamps in PostgreSQL, reading a interesting Blog about this behavior, but have no clue to solve following problem:
How to select a 'timestamp with time zone' column with AT TIME ZONE from another column, including the offset to UTC in one step?
I've a sample table in PostgreSQL 10:
CREATE TABLE public.test_tz
(
id serial,
in_zone_timestamp timestamp without time zone,
in_zone character varying COLLATE pg_catalog."default",
CONSTRAINT test_tz_pkey PRIMARY KEY (id)
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
PostgreSQL's setting is
timezone=localtime
and the server runs at 'Europe/Berlin'
$ date --> Fri Mar 9 12:15:23 CET 2018
The timestamp column should store with this messy syntax:
INSERT INTO public.test_tz ( in_zone_timestamp, in_zone) VALUES
('2017-02-01 00:00:00' AT TIME ZONE current_setting('TIMEZONE') AT TIME ZONE 'Asia/Singapore', 'Asia/Singapore' );
If someone knows a better way, please don't hold back! The other solution is to use '2017-02-01 00:00:00-08' as the value, but I don't know the offset value.
I want to store the local timestamp like: 'The user hit a key at 2017-02-01 00:00:00 in Singapore'.
If I ask the database: Which time it was here in Europe, when the user in Singapore is hitting a key, I got:
SELECT in_zone_timestamp FROM public.test_tz;
--> '2017-01-31 17:00:00+01'
This seems OK, because Singapore has a offset of +8 hours.
If I want to know, which time it was in Singapore, I use:
SELECT in_zone_timestamp AT TIME ZONE in_zone FROM public.test_tz;
--> '2017-02-01 00:00:00'
That's OK too, but however, it doesn't return the offset to 'UTC', so I can't see that this timestamp is not in my local time!
I try some combinations of AT TIME ZONE or converting to timestamptz, but the results are not what I want. I expected a result like:
--> '2017-02-01 00:00:00+08'
At here, I only see one solution, to manually concat/convert/manipulate the result and add the offset by hand, but is this the only way?
Sorry if I explain this question a little bit too comlicated and hope someone can follow my thoughts.
Thanks in advance
The only way to have PostgreSQL convert a timestamp to a string like 2017-02-01 00:00:00+08 automatically is to change the session time zone:
SET timezone = 'Asia/Singapore';
You can use SET LOCAL to change the setting only for the duration of a transaction.
If you don't want that, you can get the offset with a query like this:
SELECT TIMESTAMP '2000-01-01 00:00:00' AT TIME ZONE 'UTC'
- TIMESTAMP '2000-01-01 00:00:00' AT TIME ZONE 'Asia/Singapore';
?column?
----------
08:00:00
(1 row)
Thanks for clarify, but in my opinion that PostgreSQL has a few shortcomings on this topic.
A 'not so perfect' solution for this problem is really to manually concat the parts needed.
SELECT (test_tz.in_zone_timestamp AT TIME ZONE in_zone) || (SELECT abbrev FROM pg_timezone_names WHERE name = in_zone) FROM public.test_tz;
--> 2017-02-01 00:00:00+08
This provides an acceptable solution, with small imperfections.
In some cases there is the timezone short text in the abbrev-column, like 'CET' or 'PDT' and not the +/- numeric value. This produces results (eg. for 'Europe/Berlin') like:
--> 2017-02-01 02:00:00CET
A better value gives the column 'utc_offset' of pg-timezone-names, but this requires a more complex text manipulation, I don't want at this point.
Second solution can be the manipulation of the output by application and formatting the text to whatever you need.
Hope that helps others to solve such problem without searching the internet for a not available support from the database.
Bye
I'm hurting my head again this :
On postgresql, I would like to get the local time for a given timezone.
So, at 15:45 GMT, I want 16:45 for +01:00, but I can't get the good anwser :
SQL Fiddle
Query 1:
select current_timestamp at time zone 'GMT' as time_local_gmt
Results:
| time_local_gmt |
|-----------------------------|
| 2018-01-26T15:45:10.871659Z |
This is OK.
Query 2:
select current_timestamp at time zone '+01:00' as time_local_paris
Results:
| time_local_paris |
|-----------------------------|
| 2018-01-26T14:45:10.871659Z |
This is totally wrong, seem like it's -01:00 instead of +01:00
Edit :
See the valid answer here : https://stackoverflow.com/a/48707297/5546267
This worked for me.
select current_timestamp at time zone 'UTC+1';
Gave me the following result.
2018-01-26T17:00:58.773039Z
There is also a list of timezone names.
Here is an excerpt from the PostgreSQL 9.6 documentation regarding timezone names.
The view pg_timezone_names provides a list of time zone names that are recognized by SET TIMEZONE, along with their associated abbreviations, UTC offsets, and daylight-savings status.
Basically, the following query will give you the current time in Paris.
SELECT current_timestamp AT TIME ZONE 'Europe/Paris';
Good Luck!
For completeness (even if #Avi Abrami's answer should be what you're searching for) let's take a look at the datetime operators in the docs.
One can use the INTERVAL keyword to add hours to the stored value:
SELECT current_timestamp AT TIME ZONE INTERVAL '+02:00' AS plus_two;
Which then results in
2018-01-26T17:45:10.871659Z
(when GMT time is 2018-01-26T15:45:10.871659Z)
Section 9.9.3 AT_TIME_ZONE mentions my use of INTERVAL without any preceeding operator:
In these expressions, the desired time zone zone can be specified either as a text string (e.g., 'PST') or as an interval (e.g., INTERVAL '-08:00'). In the text case, a time zone name can be specified in any of the ways described in Section 8.5.3.
The documentation says:
Another issue to keep in mind is that in POSIX time zone names, positive offsets are used for locations west of Greenwich. Everywhere else, PostgreSQL follows the ISO-8601 convention that positive timezone offsets are east of Greenwich.
I guess that is your problem.
Ok, finally found how to !
SELECT
current_timestamp
AT TIME ZONE 'GMT'
AT TIME ZONE '+01:00'
AS time_local_paris_right;
The timestamp is UTC without TZ by default, you force it as a GMT one, and then the second AT convert it with the right offset to give you the local time for the specified time zone.
SQL Fiddle
PostgreSQL 9.6 Schema Setup:
Query 2:
select current_timestamp at time zone 'GMT' as time_local_gmt
Results:
| time_local_gmt |
|-----------------------------|
| 2018-02-09T13:44:56.824107Z |
Query 3:
select current_timestamp at time zone '+01:00' as time_local_paris_wrong
Results:
| time_local_paris_wrong |
|-----------------------------|
| 2018-02-09T12:44:56.824107Z |
Query 4:
select current_timestamp at time zone 'GMT' at time zone '+01:00' as time_local_paris_right
Results:
| time_local_paris_right |
|-----------------------------|
| 2018-02-09T14:44:56.824107Z |
I have an application, using an Oracle 11g (11.2.0.2.0 64 bit) db.
I have a lot of entries in a Person table. To access data I'm using different application (same data).
In this example I'm using birth_time field of my person table.
Some application queries data with birth_time directly, some other with to_char to reformat it, and some other with UTC function.
The problem is this: with same data, same query, result are different.
In this screenshot you can see the result with Oracle Sql developer (3.2.20.09)
All the timestamp are inserted with midnight timestamp, and in fact the to_char(..) and birth_time result are at midnight. UTC hours are returned with one hour less (Correct according to my timezone!) but some entry (here one for example, the last one) is TWO HOURS less (only few on thousand are Three)!!
The same query executed with sql*plus return the correct result with one hour of difference for all the entries!
Does anyone have a suggestion to approach this problem?
The issue is born because one of our application made with adobe flex seems to execute queries with UTC time, and the problems appears when you look at data with this component.
ps.:
"BIRTH_TIME" is TIMESTAMP (6)
Would it be possible for you to change the query used? If so, you could use the AT TIME ZONE expression to tell Oracle that this date is in UTC time zone:
SELECT SYS_EXTRACT_UTC(CAST(TRUNC(SYSDATE) AS TIMESTAMP)) AS val FROM dual;
Output:
VAL
----------------------------
13/11/20 23:00:00,000000000
Now, using AT TIME ZONE 'UTC' gets you the date you need:
SELECT SYS_EXTRACT_UTC(
CAST(
TRUNC(SYSDATE) AS TIMESTAMP)
AT TIME ZONE 'UTC') AS val FROM dual;
Output:
VAL
----------------------------
13/11/21 00:00:00,000000000