How to pass string with ' ' (timestamp) in prepared statement? - postgresql

I am trying to execute the following query
INSERT INTO hotspot(timestamp) VALUES
(timestamp with time zone '2012-10-25 14:00:00 +05:00' at time zone 'EET');
and i want to pass the timestamp as a variable.
My timestamp column is type of timestamp with time zone.
Do you have any idea how this can be done?
When i do... (Java, Postgresql)
String stm= "INSERT INTO hotspot(timestamp) VALUES(timestamp with time zone ? at time zone 'EET')";
pst = con.prepareStatement(stm);
pst.setString(1, "2012-08-24 14:00:00 +05:00");
pst.executeUpdate();
I get a syntax error at or near "$1"
Is there anyway i can overcome this error??
Thank you in advance!!
Update: I tried to use the setTimestamp by the following way...
Calendar c=Calendar.getInstance(TimeZone.getTimeZone("GMT+05:00"));
String stm= "INSERT INTO hotspot(timestamp) VALUES(?)";
pst = con.prepareStatement(stm);
pst.setTimestamp(1,Timestamp.valueOf("2012-01-05 14:00:00"), c );
pst.executeUpdate();
I suppose that the correct value in the DB should be (regarding that my local time zone is EET (+02))
2012-01-05 11:00:00 +02
but using pgadmin i check the value and i get
2012-01-05 14:00:00 +02
Any suggestions?

Consider using the setTimestamp() method instead of setString() method. Check this link in order to understand how to use PreparedStatement.
Edit: As I explained in the comment, check the API reference for setTimestamp() with three parameters:
Sets the designated parameter to the given java.sql.Timestamp value,
using the given Calendar object. The driver uses the Calendar object
to construct an SQL TIMESTAMP value, which the driver then sends to
the database. With a Calendar object, the driver can calculate the
timestamp taking into account a custom timezone. If no Calendar object
is specified, the driver uses the default timezone, which is that of
the virtual machine running the application.

Federico Cristina is quite right that setTimestamp is the correct way to do this.
The reason for the syntax error is that you can't specify the type with a leading type-specifier when passing a parameter. The INTEGER '4' style is only valid for literals, not parameters.
Your code will be PREPAREd then EXECUTEd at the protocol level. Here's what happens if I PREPARE it:
regress=> PREPARE blah(timestamptz) AS INSERT INTO hotspot(timestamp) VALUES(timestamp with time zone $1 at time zone 'EET');
ERROR: syntax error at or near "$1"
LINE 1: ...otspot(timestamp) VALUES(timestamp with time zone $1 at time...
Since the data type is specified in the query parameter you can omit it, writing instead:
String stm= "INSERT INTO hotspot(\"timestamp\") VALUES(? at time zone 'EET')";
Note that I've double-quoted "timestamp" as well, because it's a reserved word. It'll work without quotes in some contexts but not others. Choosing a data type name or reserved word as a column name is generally a bad idea.

Related

Converting a string contain date and time using to_date function

How to use to_date function in oracle-sqldeveloper to convert a string
May 1 2019 12:00 to date datatype? Does Date in SQL store time too,
or it only stores date? I tried using the to_date function with some
format but it always removes the time part.
If the time is not possible in Date datatype what could be a good alternative?
You can convert your date to a string with (assuming 24-hour values, which seems likely as you don't have an AM/PM marker):
to_date('May 1 2019 12:00', 'Mon DD YYYY HH24:MI', 'nls_date_language=English')
The format elements are in the documentation. I've included the optional third argument to to_date() because your month name has to be interpreted in English, regardless of your session settings.
it always removes the time part
Oracle dates always have both date and time parts, even if the time is set to midnight. You're probably seeing the result of that query as '01-MAY-19'.
Dates don't have any intrinsic human-readable format; Oracle uses its own internal representation, which you generally don't need to worry about.
In most clients and IDEs the session NLS_DATE_FORMAT setting is used to display native dates as strings. For historic reasons that still defaults to DD-MON-YY, despite Y2K, during database creation. it can be changed at database level, and sessions will then inherit that. But each session can override it, e.g. by issuing:
alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'
You can also explicitly convert a date value back to a string, and specify which format elements you want to include, via a to_char() call. Only do that when displaying a value - if you're storing dates or passing them around to functions, always do that as the proper date data type, not as strings. (If you have to pass them outside the database as strings, e.g. to a remote API, you'd usually want to use an ISO-8601 format).
db<>fiddle showing the default output, explicitly formatted as a string (again, for display only - do not store or manipulate dates as string), and with the session format modified.
In SQL Developer you can also go to Tools->Preferences->Database->NLS and change the 'Date format' there - that setting will then apply when you create new sessions, without having to issue alter session each time.

How to truncate date in postgres?

I am using below condition to truncate date in postgres
to_date(to_char(trunc(appointment_date),'YYYYMMDD')||appointment_end_time,''YYYYMMDDHH24:MI:SS')AS tq
How I can use this in postgres ?
Strange data typing, sometimes requires strange, looking at least, queries. Try (see fiddle)
date_trunc('day',appointment_date)
+ substr(appoinment_end,12)::interval
As your to_char() call uses the format 'HH24:MI:SS' for the "time" column, you can cast that column directly to a time value, e.g. using the :: operator: appointment_end_time::time.
To build a new timestamp from the date part of the appointment_date and the time value, just add them:
appointment_date::date + appointment_end_time::time
So first the timestamp is converted to a date (that does not have a time), and then the time value is added to that, which yields a timestamp.
Note that to_date() returns a date so your code would remove the just added time part again. You would need to use to_timestamp() if you really want a timestamp as the result.
To answer the question's title "how to truncate date in Postgres?" (which in reality refers to a timestamp not a date): you can either cast it to a date (see above) or you can use date_trunc() (not trunc()) with a unit to which it should be truncated. However, date_trunc returns a timestamp not a date value, so you couldn't add a time to the result.

Clojure: creating a date-time object for the JDBC

I created my PostgreSQL table with a column:
updated_at timestamp(0) with time zone
I'm using clojure.java-time but I can't create a string (or should be an object?) to set the current date-time. I tried with:
(time/format "yyyy-MM-dd HH:ss" (time/local-date-time))
and with that string I tried:
(db/update-answer! {:updated_at "2019-12-25 14:08", :id 102, :answer "Foo"}
but the JDBC tells me that the field "updated_at" doesn't have a valid type. As far as I understand the JDBC is still using the old java API for handle the dates and time zones and not the new in JDK 8. So, in summary I don't know how to create a string or an object valid for Postgresql time-stamp column using clojure.java-time.
Someone advises me about extending the JDBC protocol, I'm reading about it but for now I'm just looking for a way to create a valid object and finish this update.
UPDATED
After followed this page I could give format to Instant with:
(ns zentaur.hiccup.helpers-view
(:require [clojure.tools.logging :as log]
[java-time :as jt])
(:import [java.time ZoneId]))
(defn format-date [date]
(log/info (str ">>> DATE >>>>> " date "und type >>> " (type date)))
(jt/format "yyyy-MM-dd HH:mm" (.atZone date (ZoneId/systemDefault))))
(Java syntax, not Clojure)
tl;dr
Use objects, not Strings. Use java.time classes in Java, never the legacy date-time classes.
myPreparedStatement
.setObject(
… ,
OffsetDateTime.now()
)
Even better would be a trigger to do this automatically.
in summary I don't know how to create … an object valid for Postgresql time-stamp column using clojure.java-time.
Call OffsetDateTime.now() to get a java.time.OffsetDateTime object holding the current moment.
Use a database column of type TIMESTAMP WITH TIME ZONE rather than TIMESTAMP (short for TIMESTAMP WITHOUT TIME ZONE).
Moments
set the current date-time
If you want to track moments, specific points on the timeline, you are using the wrong data type.
You are using TIMESTAMP which is short for TIMESTAMP WITHOUT TIME ZONE. This type lacks any concept of time zone or offset-from-UTC. So this type cannot track moments. I suggest you avoid the short version of this type name in the future, to make your SQL clear. See Postgres doc.
You should be defining your column as TIMESTAMP WITH TIME ZONE. Postgres handles this type by always saving the moment as seen in UTC. Any provided time zone or offset in the input is used to adjust to UTC. Likewise, when retrieved, your value is always in UTC, an offset of zero hours-minutes-seconds.
Beware of middleware and tools that dynamically apply some time zone to the retrieved value. This clouds matters, creating the illusion that the value was stored in that time zone. The value was actually stored in UTC, always in UTC in Postgres.
Smart objects, not dumb strings
but I can't create a string (or should be an object?) to set the current date-time.
Don’t.
You should be exchanging objects between Java and Postgres, rather than mere strings. This avoids the time-zone injection problem mentioned above.
As of JDBC 4.2, you can exchange modern java.time objects with the database. Never use the legacy types such as Calendar, GregorianCalendar, java.util.Date, java.sql.Date, java.sql.Timestamp, and so on. These classes were terribly flawed, designed by people who did not understand date-time handling. They were supplanted as of the adoption of JSR 310.
Capture the current moment as an OffsetDateTime. Your JDBC driver might support Instant & ZonedDateTime but those types are optional, as seen in graphic table above. In contrast, JDBC requires support for OffsetDateTime.
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;
myPreparedStatement.setObject( … , odt ) ;
The explicit use of ZoneOffset.UTC in code above is not strictly required. If omitted, your JVM’s current default offset will be applied implicitly. Your JDBC driver and/or Postgres will adjust to UTC as discussed earlier above. My own preference for the sake of debugging/logging is to specify UTC so I can see the value as it will eventually be stored in the database.
And retrieval.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
Default value
If you are simply recording the moment whenever a row is updated, no need to do that in your SQL. I suggest you write a trigger to be called whenever a row in that table is updated. Then you are guaranteed the value will be written no matter the mechanism by which the row was updated. And less to worry about when writing your SQL statements.

indexing timestamptz for specific timezone

My console is PST.
Database server and times stored are GMT.
I'm having to run queries like so:
SELECT x,y,z
FROM tbl_msg
WHERE (msg_datetime AT TIME ZONE 'BST') BETWEEN '2016-11-21'::date and '2016-11-22'::date;
Indexing 101 says that performing this operation on msg_datetime will now avoid the index and this is what I'm seeing.
So I need advice with an indexing solution for this.
Can I index this timezone? or alter this query so that it queries these times in BST, converted to GMT?
You should have msg_datetime column of type timestamp with time zone (or shorter alias timestamptz) with normal index.
Then, to get data for these 2 days, you should:
set timezone 'Europe/London'; -- once, on connection start
SELECT x,y,z
FROM tbl_msg
WHERE
msg_datetime>='2016-11-21 00:00:00'
and
msg_datetime<'2016-11-23 00:00:00';
You should not use ordinary timestamp, as it stores literal date and hour without information about which timezone it actually meant. A timestamp with time zone type will automatically convert your client's configured time to internal representation (which is in UTC) and back. You can also express timestamptz from non-default timezone using for example '2016-11-23 00:00:00 Asia/Tokyo'.
Also you should not use BST - because you'd need to use GMT on winter and remember when to use which. You should use 'Europe/London' or other "city" timezones (list), which are right both in summer and in winter.

How can i insert timestamp with timezone in postgresql with prepared statement?

I am trying to insert to a timestamp with timezone field of my DB a string which includes date, time and timezone using prepared statement.
The problem is that Timestamp.valueof function does not take into consideration the time zone that the string inludes so it causes an error.
The accepted format is yyyy-[m]m-[d]d hh:mm:ss[.f...] which does not mention timezone.
That is the exact code that causes the error:
pst.setTimestamp(2,Timestamp.valueOf("2012-08-24 14:00:00 +02:00"))
Is there any way that i can overcome it??
Thanks in advance!
The basic problem is that a java.sql.Timestamp does not contain timezone information. I think it is always assumed to be "local timezone".
On solution I can think of is to not use a parameter in a PreparedStatement, but a timezone literal in SQL:
update foo
set ts_col = timestamp with time zone '2012-08-24 14:00:00 +02:00'`;
Another possible solution could be to pass a properly formatted String to a PrepareStatement that uses to_timestamp():
String sql = "update foo set ts_col = to_timestamp(?, 'yyyy-mm-dd hh24:mi:ss')";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, "2012-08-24 14:00:00 +02:00");
I believe that you could use one more field in your database, which would include the time zone. And calculate the time manually after you get these two fields