I have two data base in two differents machines with the same schemas, tables and data. I launch this query:
select mydate from mytable where date = '2013-10-03 14:25:00-07'::timestamp::date
the first machine return the correct rows and the second one doesn´t, both machines has the same prostgres version (9.2)
the only different between the machines is that first one works on windows and the second one on Linux (Centos)
Any suggestion?
'2013-10-03' can be interpreted as Oct, 3rd or March, 10th, depending on your datestyle setting. #Chris has more on that.
In addition to that, your query is generally incorrect. This expression is misleading:
'2013-10-03 14:25:00-07'::timestamp
timestamp in Postgres defaults to timestamp without time zone, which doesn't recognize time zone offsets. Therefore, the time zone offset -07 is discarded.
Use instead:
'2013-10-03 14:25:00-07'::timestamptz
Match the point in time:
SELECT * FROM mytable
WHERE mydate = '2013-10-03 14:25:00-07'::timestamptz
Does not depend on your local time zone setting, since the data type of the column is timestamp with time zone as you clarified in a later comment.
Match the day:
...
WHERE mydate::date = '2013-10-03 14:25:00-07'::timestamptz::date
Depends on your local time zone setting, which defines lower and upper borders of the "day".
Detailed explanation in this related answer:
Ignoring timezones altogether in Rails and PostgreSQL
The cleanest solution would be to, at the beginning of the session, just issue the following command:
SET datestyle = "ISO, YMD";
This will ensure properly handling the timestamp according to your input format.
Related
I am creating a table with timestamp column,I am stuggling with time zone settings, I want to specify the time zone on the column,as follows:
create table t1(a date, b timestamp with time zone 'America/Los_Angeles', c timestamp without time zone)
But the grammar is wrong, I would ask how to specify the time zone on the column, than
👉 You do not specify a time zone when defining the column.
CREATE TABLE t1
(
a DATE ,
b TIMESTAMP WITH TIME ZONE ,
c TIMESTAMP WITHOUT TIME ZONE
)
;
You need to read the documentation carefully. Programming by intuition tends to end badly.
The TIMESTAMP WITH TIME ZONE type in Postgres does not save a time zone. The type uses any offset or time zone info supplied with an input to adjust to an offset of zero hours-minutes-seconds from UTC. 👉 Every value in that column is set to an offset of zero. After adjusting to zero offset, the supplied time zone or offset info is discarded by Postgres.
If you care about the original time zone, you need to write that value into a separate column yourself.
In contrast, the TIMESTAMP WITHOUT TIME ZONE type lacks any concept of a time zone or offset from UTC. A column of this type stores simply a date and a time-of-day. So values in this column cannot represent a moment, cannot refer to a specific point on the timeline. If you write noon on the 23rd of last January, we have no way of knowing if you meant noon in Tokyo Japan, noon in Toulouse France, or noon in Toledo Ohio US. Those would be three different moments, several hours apart.
Some other databases share the same behavior as Postgres. But not all. The SQL standard barely touches on the subject of date-time, just mentioning the types but without much detail regarding prescribed behavior. As a consequence, date-time behavior varies widely across database engines.
I should mention that some tools have an anti-feature where they inject a default time zone, used to adjust a value stored in UTC to that zone. pgAdmin is, unfortunately, one such tool. While well-intentioned as a convenience to the user, this behavior creates the illusion of a time zone having been stored and retrieved. I would rather all tools “tell the truth”, and report retrieved values with an offset of zero. A workaround is to set the current default time zone of your database session to UTC.
All this has been covered many times already here on Stack Overflow, and also on the sister site https://dba.stackexchange.com/. Search to learn more.
I want to get the current timezone name.
What I already achieved is to get the utc_offset / the timezone abbreviation via:
SELECT * FROM pg_timezone_names WHERE abbrev = current_setting('TIMEZONE')
This gives me all Continent / Capital combinations for this timezone but not the exact timezone. For example I get:
Europe/Amsterdam
Europe/Berlin
The server is in Berlin and I want to get the timezone name of the server.
The problem I have with CET that it is always UTC+01:00 and does not account for DST iirc.
I don't think this is possible using PostgreSQL alone in the most general case. When you install PostgreSQL, you pick a time zone. I'm pretty sure the default is to use the operating system's timezone. That will usually be reflected in postgresql.conf as the value of the parameter "timezone". But the value ends up as "localtime". You can see this setting with the SQL statement.
show timezone;
But if you change the timezone in postgresql.conf to something like "Europe/Berlin", then show timezone; will return that value instead of "localtime".
So I think your solution will involve setting "timezone" in postgresql.conf to an explicit value rather than the default "localtime".
It seems to work fine in Postgresql 9.5:
SELECT current_setting('TIMEZONE');
This may or may not help you address your problem, OP, but to get the timezone of the current server relative to UTC (UT1, technically), do:
SELECT EXTRACT(TIMEZONE FROM now())/3600.0;
The above works by extracting the UT1-relative offset in minutes, and then converting it to hours using the factor of 3600 secs/hour.
Example:
SET SESSION timezone TO 'Asia/Kabul';
SELECT EXTRACT(TIMEZONE FROM now())/3600.0;
-- output: 4.5 (as of the writing of this post)
(docs).
You can access the timezone by the following script:
SELECT * FROM pg_timezone_names WHERE name = current_setting('TIMEZONE');
current_setting('TIMEZONE') will give you Continent / Capital information of settings
pg_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.
name column in a view (pg_timezone_names) is time zone name.
output will be :
name- Europe/Berlin,
abbrev - CET,
utc_offset- 01:00:00,
is_dst- false
See this answer: Source
If timezone is not specified in postgresql.conf or as a server command-line option, the server attempts to use the value of the TZ environment variable as the default time zone. If TZ is not defined or is not any of the time zone names known to PostgreSQL, the server attempts to determine the operating system's default time zone by checking the behavior of the C library function localtime(). The default time zone is selected as the closest match among PostgreSQL's known time zones. (These rules are also used to choose the default value of log_timezone, if not specified.) source
This means that if you do not define a timezone, the server attempts to determine the operating system's default time zone by checking the behavior of the C library function localtime().
If timezone is not specified in postgresql.conf or as a server command-line option, the server attempts to use the value of the TZ environment variable as the default time zone.
It seems to have the System's timezone to be set is possible indeed.
Get the OS local time zone from the shell. In psql:
=> \! date +%Z
We have an application that fetches data from a source and that source present the data with a timestamp in UTC. When our application saves that data to Postgres, it stores that timestamp in a timestamp column without time zone. The default on postgres in our shop is set to our local time, Mountain Time. So that means, I think, that postgres assumes that timestamp is mountain time. How can I query that column so that my result set thinks it's UTC and not the local time zone?
More cleary stated, I need to perform some offsets on that timestamp (moving it to, say EST) and so the math of doing that is different if the resultset thinks it's UTC than my local time
The Answer by Kouber Saparev is mostly correct, though incorrect about storing a time zone.
Wrong data type in Postgres
a timestamp in UTC. When our application saves that data to Postgres, it stores that timestamp in a timestamp column without time zone.
As noted in his Answer, you are using the wrong data type in your Postgres database. When tracking moments, you must use a column of type TIMESTAMP WITH TIME ZONE. When supplying an input during an insert or update, any accompanying info about time zone or offset-from-UTC is used to adjust into UTC. The accompanying zone/offset is then discarded. If you need to remember the original zone/offset, you will need to define a second column and store that info there yourself.
The other type in Postgres, and the SQL standard, is TIMESTAMP WITHOUT TIME ZONE. This type purposely lacks any concept of time zone or offset-from-UTC. So this type cannot represent moments, cannot store points on the timeline. It stores values that represent potential moments along a range of about 26-27 hours, the range of various time zones around the globe. Use this type only when you mean a date with time-of-day everywhere or anywhere, but not specifically somewhere. Also used when you mean appointments far enough out in the future that we run the risk of politicians changing the offset used in any of the time zones we care about.
Always specify time zone
default on postgres in our shop is set to our local time, Mountain Time
Never depend on the current default time zone of your host OS, the database server, or your tools such as the Java Virtual Machine. Always specify the desired/expected time zone in your code.
Tip: Generally best to work in UTC for data storage, data exchange, and most of your business logic. Adjust from UTC to a time zone only for presentation to the user or where business rules require.
As explained above, Postgres always stores date-time values either in UTC or with no zone/offset at all. Beware: Tools used between you and Postgres may apply a time zone to the UTC value retrieved from the database. While well-intentioned, this anti-feature creates the illusion that the time zone was stored when in fact only UTC was stored in TIMESTAMP WITH TIME ZONE or no zone/offset at all in TIMESTAMP WITHOUT TIME ZONE.
Be aware that any zone information accompanying input to a column of TIMESTAMP WITHOUT TIME ZONE is simply ignored, the date and time-of-day taken as-is and stored.
I need to perform some offsets on that timestamp (moving it to, say EST)
Generally best to use your database just for storage, query, and retrieval of data. For massaging the data like adjusting time zone, do such work in your application. For example, in Java use the industry-leading java.time classes, in .NET the Noda Time project (a port of the predecessor of java.time, the Joda-Time project).
Example code in Java using JDBC 4.2 or later.
LocalDateTime
For a value in a column of TIMESTAMP WITHOUT TIME ZONE we use the corresponding type in Java, LocalDateTime, lacking any concept of time zone or offset-from-UTC.
LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ; // Retrieve value from database.
String output = ldt.toString() ; // Generate text representing this date-with-time value in standard ISO 8601 format.
2018-01-23T01:23:45.123
If you know for certain that this date and time was meant for UTC but was incorrectly stored without any zone/offset info, you can apply a zone or offset to repair the damage.
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ); // Apply an offset-from-UTC to a `LocalDateTime` lacking such information. Determines a moment.
OffsetDateTime
For a value in a column of TIMESTAMP WITH TIME ZONE we use the corresponding type in Java, OffsetDateTime (or Instant), representing a moment in UTC.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ; // Retrieve value from database.
String output = odt.toString() ; // Generate text representing this date-with-time value in standard ISO 8601 format. A `Z` on the end indicates UTC, pronounced “Zulu”.
2018-01-23T01:23:45.123Z
ZonedDateTime
To see that OffsetDateTime value set in UTC through the lens of the wall-clock time used by the people of regions within the mid-west of North America, specify a time zone such as America/Edmonton or America/Denver.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Denver" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
See this code run live at IdeOne.com. We see the same moment but with a different wall-clock time.
2018-01-22T18:23:45.123-07:00[America/Denver]
Beware of tools & middleware injecting a time zone
Unfortunately, many tools and middleware will volunteer to apply some default time zone to a moment retrieved from the database. While well-intentioned, this creates the illusion of the zone having been a part of the stored data when in fact the time zone was added after storage, upon retrieval. This anti-feature creates much confusion. I wish all the tools were clear and truthful by reporting the moment in UTC, as it was stored.
If you use Java, with JDBC 4.2 and later, you can exchange java.time (JSR 310) (tutorial) objects with the database and avoid this time zone injection.
There are two data types handling timestamps in PostgreSQL - timestamp, and timestamptz (timestamp with time zone). The latter stores the time zone along with the timestamp itself.
If you are using just a timestamp without time zone, then there is no way for the result set to think whether the timestamp is UTC or not. It is just a timestamp. It is up to the client application to interpret it and give it some time zone meaning.
On the contrary, if you use timestamptz, then PostgreSQL knows the time zone of that timestamp, and then it can calculate time zone offsets properly for you.
db=# select now();
now
-------------------------------
2014-12-04 19:27:06.044703+02
(1 row)
db=# select timezone('est', now());
timezone
----------------------------
2014-12-04 12:27:06.044703
(1 row)
So, back on the problem posed. You need to make sure that first the data is imported properly and then - when needed, it is returned and displayed properly to the end user. You have two options:
Continue using timestamp
In that case both the writing app and the reading app need to know that all the timestamps in the database are UTC and calculate offsets accordingly.
Switch to timestamptz
Then the only thing that the apps need to know is their own time zone, they just have to declare it after connecting to PostgreSQL and leave the rest to the database.
For example, let's connect as a writing app and declare our time zone as UTC.
db=# create table x (data timestamptz);
CREATE TABLE
db=# set timezone='utc';
SET
db=# insert into x values (now());
INSERT 0 1
db=# select * from x;
data
-------------------------------
2014-12-04 20:02:08.692329+00
(1 row)
Now, let's say a reading app connects and is in the EST time zone.
db=# set timezone='est';
SET
db=# select * from x;
data
-------------------------------
2014-12-04 15:02:08.692329-05
(1 row)
Changing the client time zone setting changes the way all the timestamps are returned, but that's the case only if you use timestamptz - timestamp with time zone. If you cannot switch to this data type, then the application will have to take care of all this magic.
I am converting an Unix script with a SQL transact command to a PostgreSQL command.
I have a table with records that have a field last_update_time(xtime) and I want to select every record in the table that has been updated within a selected period.
Say, the current time it 05/01/2012 10:00:00 and the selected time is 04/01/2012 23:55:00. How do I select all the records from a table that have been updated between these dates. I have converted the 2 times to seconds in the Unix script prior to issuing the psql command, and have calculated the interval in seconds between the 2 periods.
I thought something like
SELECT A,B,C FROM table
WHERE xtime BETWEEN now() - interval '$selectedtimeParm(in secs)' AND now();
I am having trouble evaluating the Parm for the selectedtimeParm - it doesn't resolve properly.
Editor's note: I did not change the inaccurate use of the terms period, time frame, time and date for the datetime type timestamp because I discuss that in my answer.
What's wrong with:
SELECT a,b,c
FROM table
WHERE xtime BETWEEN '2012-04-01 23:55:00'::timestamp
AND now()::timestamp;
If you want to operate with a count of seconds as interval:
...
WHERE xtime BETWEEN now()::timestamp - (interval '1s') * $selectedtimeParm
AND now()::timestamp;
Note the standard ISO 8601 date format YYYY-MM-DD h24:mi:ss which is unambiguous with any locale or DateStyle setting.
The first value for the BETWEEN construct must be the smaller one. If you don't know which value is smaller use BETWEEN SYMMETRIC instead.
In your question you refer to the datetime type timestamp as "date", "time" and "period". In the title you used the term "time frames", which I changed to "timestamps". All of these terms are wrong. Freely interchanging them makes the question even harder to understand.
That, and the fact that you only tagged the question psql (the problem hardly concerns the command line terminal) might help to explain why nobody answered for days. Normally, it's a matter of minutes around here. I had a hard time understanding your question.
Understand the data types date, interval, time and timestamp - with or without time zone. Start by reading the chapter "Date/Time Types" in the manual.
Error message would have gone a long way, too.
For anyone who is looking for the fix to this. You need to remove timestamp from the where clause and use BETWEEN!
TABLENAME.COL-NAME-FOR-TIMESTAMP BETWEEN '2020-01-29 04:18:00-06' AND CURRENT_TIMESTAMP
My web app stores all timestamps in UTC without time zones. I have a shell/psql script that returns a list of recent logins, and I want that script to display login times in the server's local time zone (which may vary depending on where the server is and with daylight savings).
To get an interval representing the difference between my database server's time zone and UTC, I'm currently using this hack:
SELECT age(now() at time zone 'UTC', now());
That works, but is there a more straightforward way?
There's a server configuration parameter called "timezone" that returns a valid timezone string, but I don't think it's possible to access those parameters in a query. (I guess that's a separate question, but an answer to it would resolve the time zone issue.)
SELECT current_setting('TIMEZONE')
This can be used in a query, however, this does not give a numerical difference.
Your solution is fine.
To get # of seconds difference as an integer, I have used simply:
select extract( timezone from now() );
Get the interval based on shaunc's answer:
select (extract(timezone from now()) || 'seconds')::interval;