Postgresql seem to convert timestamp parameter of PreparedStatement which I set using setTimestamp.
[What I want to do]
I want to query today's data. ( 2016-06-30 00:00:00 ~ 2016-06-30 23:59:59)
But, when I got the result from DB, it was data for 2016-06-29 15:00:00 to 2016-06-30 14:59:59. ( 9 hours gap)
My local timezone : GMT+9 (KST)
DB timezone : UTC (GMT+0) ( In table, UTC time is stored as update time. I checked that. )
So 9 hours gap as I guess. When I pass UTC timestamp parameter to postgresql, it subtracted 9 hours from my timestamp parameters. I wonder why postgresql did so, and how I prevent that.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 00:00:00");
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd 23:59:59");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
sdf2.setTimeZone(TimeZone.getTimeZone("UTC"));
Timestamp startTime = Timestamp.valueOf(sdf.format(new Date(System.currentTimeMillis())));
Timestamp endTime = Timestamp.valueOf(sdf2.format(new Date(System.currentTimeMillis())));
PreparedStatement pstmt = conn.prepareStatement(
"SELECT * FROM some_table WHERE update_time BETWEEN ? AND ? "
);
pstmt.setTimestamp(1, startTime);
pstmt.setTimestamp(2, endTime);
ResultSet rs = pstmt.executeQuery();
[Table structure]
CREATE TABLE some_table
(
mem_no bigserial
,data char(2)
,update_time timestamp with time zone DEFAULT current_timestamp
,CONSTRAINT pk_some_table PRIMARY KEY (mem_no)
);
[Something strange]
Using debugging tool, I checked pstmt value. Strangely +09:00:00 was added to my parameters.
pstmt => SELECT * FROM some_table WHERE update_time BETWEEN 2016-06-30 00:00:00 +09:00:00 AND 2016-06-30 23:59:59 +09:00:00
DB : postgresql 9.3
tl;dr
To find all rows with a recorded moment occurring on a certain date, in this SQL: "SELECT * FROM tbl WHERE when !< ? AND when < ? ; " ; that uses the Half-Open approach to a span-of-time.
myPreparedStatement
.setObject(
1 ,
LocalDate // Represent a date-only value, without a time-of-day and without a time zone.
.parse( "2016-06-30" ) // Returns a `LocalDate` object.
.atStartOfDay() // Returns a `OffsetDateTime` object. JDBC 4.2 and later requires support for this class.
) ;
myPreparedStatement
.setObject(
2 ,
LocalDate
.parse( "2016-06-30" )
.plusDays( 1 ) // Add a day, to get first moment of the following day.
.atStartOfDay()
) ;
Details
There are multiple problems with your code. For more discussion, see other Answers such as this one of mine. I'll be brief here as this has been covered many times already.
Use only java.time classes, never the legacy date-time classes they supplanted as of adoption of JSR 310.
Use Half-Open approach to defining a span of time, where the beginning is inclusive while the ending is exclusive.
Be clear on whether you are tracking moments, specific points on the timeline, or vague date with time-of-day but lacking the context of a time zone or offset-from-UTC.
If tracking moments, your column must be of data type TIMESTAMP WITH TIME ZONE rather than WITHOUT.
For any given moment, the date (and time-of-day) varies around the globe by time zone.
Some dates in some time zones do not start at 00:00. Always let java.time determine start of day.
Apparently you want all the rows with a date-time occurring on the date of 2016-06-30.
LocalDate startDate = LocalDate.parse( "2016-06-30" ) ;
LocalDate stopDate = startDate.plusDays( 1 ) ;
Specify the time zone by which you want to interpret the date and get first moment of the day.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
Get the first moment of start and stop dates.
ZonedDateTime zdtStart = startDate.atStartOfDay( z ) ;
ZonedDateTime zdtStop = startDate.atStopOfDay( z ) ;
Support for the ZonedDateTime class is not required in JDBC 4.2. That class may or may not work with your JDBC driver. If not, use the OffsetDateTime class, support for which is required by JDBC 4.2.
OffsetDateTime start = zdtStart.toOffsetDateTime() ;
OffsetDateTime stop = zdtStop.toOffsetDateTime() ;
Write your SQL with placeholders ?. I suggest always including the statement terminator ;. Never use BETWEEN for date-time work as that is full-closed rather than half-open.
String sql = "SELECT * FROM tbl WHERE when !< ? AND when < ? ; " ; // Half-Open span-of-time where beginning is inclusive while the ending is exclusive.
Pass the placeholder objects.
myPreparedStatement.setObject( 1 , start ) ;
myPreparedStatement.setObject( 2 , stop ) ;
Retrieve result.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
That result will be in UTC. You can adjust to your desired time zone. Same moment, same point on the timeline, but different wall-clock time.
ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
Always specify zone/offset
My local timezone : GMT+9 (KST)
DB timezone : UTC (GMT+0) ( In table, UTC time is stored as update time. I checked that. )
Write your Java app such that you never depend on the current default time zone of either your JVM or your database server.
Notice that the code above using objects has no surprises with time zone, and explicitly specify desired/expected time zones or offsets. The surprises come with middleware and utilities that are opinionated about injecting a zone or offset into the retrieved values. Postgres itself always stores and retrieves TIMESTAMP WITH TIME ZONE values in UTC (an offset of zero hours-minutes-seconds).
Half-Open
I want to query today's data. ( 2016-06-30 00:00:00 ~ 2016-06-30 23:59:59)
No, you are missing the last second of the day there.
Instead define your spans-of-time using Half-Open approach where the beginning is inclusive while the ending is exclusive. This lets spans of time neatly abut one another without gaps and without overlaps.
So a week starts on Monday and runs up to, but does not include, the following Monday. Lunch period starts at 12:00 noon and runs up to, but does not include, when the clock strikes 13:00. A day starts at the first moment of the day (which is not always 00:00 by the way!), running up to, but not including, the first moment of the following day. Study the SQL and Java shown in this Answer to see how that works.
I solved this ^^
[My answer] (1) WHERE clause (2) String instead of Timestamp
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 00:00:00");
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd 23:59:59");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
sdf2.setTimeZone(TimeZone.getTimeZone("UTC"));
String startTime = sdf.format(new Date(System.currentTimeMillis()));
String endTime = sdf2.format(new Date(System.currentTimeMillis()));
PreparedStatement pstmt = conn.prepareStatement(
"SELECT * FROM some_table " +
"WHERE update_time " +
"BETWEEN (CAST(TO_TIMESTAMP(?,'YYYY-MM-DD HH24:MI:SS') AS TIMESTAMP) AT TIME ZONE 'UTC') " +
"AND (CAST(TO_TIMESTAMP(?,'YYYY-MM-DD HH24:MI:SS') AS TIMESTAMP) AT TIME ZONE 'UTC')");
pstmt.setString(1, startTime);
pstmt.setString(2, endTime);
ResultSet rs = pstmt.executeQuery();
Clearly, timezone offset +09 is attached automatically. So I decided to give String parameter instead of timestamp. ( JDBC or Postgresql will not change String value ) Then I tested some cases like this.
[Test]
PreparedStatement pstmt = conn.prepareStatement(
"SELECT TO_TIMESTAMP(?,'YYYY-MM-DD HH24:MI:SS') test1 " +
"CAST(TO_TIMESTAMP(?,'YYYY-MM-DD HH24:MI:SS') AS TIMESTAMP) test2 " +
"(CAST(TO_TIMESTAMP(?,'YYYY-MM-DD HH24:MI:SS') AS TIMESTAMP) AT TIME ZONE 'UTC') test3 " +
"FROM some_table " +
"WHERE update_time " +
"BETWEEN TO_TIMESTAMP(?,'YYYY-MM-DD HH24:MI:SS') " + => In test, this is not important
"AND TO_TIMESTAMP(?,'YYYY-MM-DD HH24:MI:SS') "); => In test, this is not important
pstmt.setString(1, startTime);
pstmt.setString(2, startTime);
pstmt.setString(3, startTime);
pstmt.setString(4, startTime);
pstmt.setString(5, endTime);
ResultSet rs = pstmt.executeQuery();
System.out.println(rs.getString("test1") + "/" +rs.getString("test2") + "/" + rs.getString("test3") );
[Result]
current time : 2016-07-01 15:17:40+00 ( in UTC )
startTime : "2016-07-01 00:00:00" => String
test1 : 2016-07-01 00:00:00+09 => cause subtraction ( 2017-06-30 15:00:00)
test2 : 2016-07-01 00:00:00
test3 : 2016-07-01 09:00:00+09 => Wow ^^
[test3] was what I wanted. When I changed where clause like above, I could get data for 2016-07-01 00:00:00 ~ 2016-07-01 23:59:59
Related
We are developing applications where dates are stored in GMT , and need is to display date and time according to users local clock timezone
Java 11 , when i change timezone , its requires JVM restart so its taking old timezone of during start of server.
Is there any ways to determine timezone of machine ( OS) without restarting JVM?
Code
Calendar cal = Calendar.getInstance();
System.out.println(" -- " + TimeZone.getDefault());
long milliDiff = cal.get(Calendar.ZONE_OFFSET);
// Got local offset, now loop through available timezone id(s).
String[] ids = TimeZone.getAvailableIDs();
String name = null;
for (String id : ids) {
System.out.println("ID : " + id);
TimeZone tz = TimeZone.getTimeZone(id);
System.out.println("TZ : " + tz.getRawOffset());
if (tz.getRawOffset() == milliDiff) {
// Found a match.
name = id;
break;
}
}
System.out.println("Name " + name);
Above code works OK as standalone, but when intregrated in web application ( JVM ) its KO.
java.time
You are using terrible date-time classes that were years ago supplanted by the modern java.time classes defined in JSR 310.
Avoid setting default time zone
No need to set the current default time zone of your JVM. Doing so is risky as you immediately affect all other code in all threads running in all apps in that JVM. And doing so is unreliable in that any code in any thread of any app in that JVM can change the zone at any moment behind your back.
Instant
Capture the current moment as seen in UTC (an offset from UTC of zero hours-minutes-seconds) using Instant class.
Instant now = Instant.now();
System.out.println( "now = " + now );
now.toString() = 2023-02-16T06:24:54.944850Z
ZoneId
Time zones are represented by the ZoneId class.
Set < String > zoneNames = ZoneId.getAvailableZoneIds();
for ( String zoneName : zoneNames )
{
ZoneId z = ZoneId.of( zoneName );
ZonedDateTime zdt = now.atZone( z );
System.out.println( "Now in zone " + z + " is " + zdt.toString() );
}
Now in zone Asia/Aden is 2023-02-16T09:24:54.944850+03:00[Asia/Aden]
Now in zone America/Cuiaba is 2023-02-16T02:24:54.944850-04:00[America/Cuiaba]
…
Now in zone Pacific/Guadalcanal is 2023-02-16T17:24:54.944850+11:00[Pacific/Guadalcanal]
Now in zone Europe/Athens is 2023-02-16T08:24:54.944850+02:00[Europe/Athens]
Now in zone US/Pacific is 2023-02-15T22:24:54.944850-08:00[US/Pacific]
Now in zone Europe/Monaco is 2023-02-16T07:24:54.944850+01:00[Europe/Monaco]
Current moment as seen by user
You asked:
need is to display date and time according to users local clock timezone
Capture the current default time zone of the JVM at runtime.
ZoneId z = ZoneId.systemDefault() ;
However, if critical, your app should confirm the user's intended time zone.
Adjust our Instant object to that zone. Same moment, different wall-clock time.
ZonedDateTime zdt = instant.atZone( z ) ;
Or skip over the Instant object to get ZonedDateTime directly.
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
First time post - I'm newer to java/JavaFx
I'm trying to get my code to save in 15 min incrementalist. The project has an Observable List that houses selectable appointment times.
ApptAddController.java
private final ObservableList<String> times = FXCollections.observableArrayList("8:00 AM", "9:00 AM", "10:00 AM", "11:00 AM", "12:00 PM", "1:00 PM", "2:00 PM", "3:00 PM", "4:00 PM");
ApptDB.Java
The "times" selected goes to the "saveAppt" method
public static boolean saveAppt(int id, String type, String contact, String location, String date, String time) {
//Time stamp for booking times
String tsStart = createTimeStamp(date, time, location, true);
String tsEnd = createTimeStamp(date, time, location, false);
try {
//get date for Appointment createDate
DateTimeFormatter dt = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss");
String nowString = LocalDateTime.now(Clock.systemUTC()).format(dt);
Statement statement = DBConnection.getConnection().createStatement();
//Query
String queryInsertOne = "INSERT INTO appointment(customerId, type, contact, location, start, end, createDate, lastUpdateBy, createdBy) values ('" + id + "', '" + type + "', '" + contact + "', '" + location + "','" + tsStart + "','" + tsEnd + "','" + nowString + "','" + UserDB.getCurrentUser() + "','" + UserDB.getCurrentUser() + "')";
int updateOne = statement.executeUpdate(queryInsertOne);
return true;
} catch (SQLException e) {
System.out.println("SQLException: " + e.getMessage());
}
return false;
}
The createTimeStamp method is hard coded in the following method with the "00":
public static String createTimeStamp(String date, String time, String location, boolean startMode) {
String t = time.split(":")[0];
int baseH = Integer.parseInt(t);
if(baseH < 8) {
baseH += 12;
}
if(!startMode) {
baseH += 1;
}
String baseD = String.format("%s %02d:%s", date, baseH, "00");
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd kk:mm");
LocalDateTime ldt = LocalDateTime.parse(baseD, dtf);
ZoneId zid;
zid = TimeZone.getDefault().toZoneId();
ZonedDateTime zdt = ldt.atZone(zid);
ZonedDateTime utcDate = zdt.withZoneSameInstant(ZoneId.of("UTC"));
ldt = utcDate.toLocalDateTime();
Timestamp ts = Timestamp.valueOf(ldt);
return ts.toString();
}
Id like to have the appointments be 15 min incrementals. The user will select times at random (8:00, 8:15, 8:30, 8:45). How do I get this code to detect what the user selects and puts int in the database accordingly. If I change "00" to "15" it will hard code every appt for 15 after.
Thanks for your time.
I am not able to follow your code well. So here's some general advice. I should say up front, managing appointments and schedules is a surprisingly complicated problem domain.
You seem to be focused on strings rather than appropriate data types, a common issue with new programmers. Learn to use smart objects, not dumb strings. Notice that none of the code shown below involves strings, except for presentation to users. Values are exchanged with the database as objects, without the use of strings.
Java offers an industry-leading assortment of date-time classes, in the java.time packages. So use them. LocalTime represents a time-of-day. LocalDate represents a date-only, without time-of-day, and without time zone.
For data-entry on the time of appointment, you should be collecting an hour and a minute. Work internally with 24-hour clock LocalTime objects.
List< LocalTime > hourTimes =
List.of(
LocalTime.of( 8 , 0 ) ,
LocalTime.of( 9 , 0 ) ,
LocalTime.of( 10 , 0 ) ,
LocalTime.of( 11 , 0 ) ,
LocalTime.of( 12 , 0 ) ,
LocalTime.of( 13 , 0 ) ,
LocalTime.of( 14 , 0 ) ,
LocalTime.of( 15 , 0 ) ,
LocalTime.of( 16 , 0 )
)
;
If your audience expects a 12-hour clock, present their display with a custom formatter. If your audience expects a 12-hour clock, present their display with a custom formatter.
Locale locale = Locale.US ;
DateTimeFormatter formatterHourOfDay = DateTimeFormatter.ofPattern ( "h a" ).withLocale ( locale ) ;
String output = hourTimes.get( 7 ).format( formatterHourOfDay ) ;
See similar code run live at IdeOne.com.
3 PM
Note that java.time uses immutable objects. So you can freely use the LocalTime object directly from the master list with no need to copy, no need to worry about its values being changed out from under your feet. This also means you can use java.time objects across threads, as they are designed to be thread-safe.
Keep a list of the possible minutes.
List< Integer > minutes = List.of( 0 , 15 , 30 , 45 ) ;
In you user-interface, let the use pick one of those four values, to be mated with their choice from hourTimes above.
Put these values together for your time-of-day to make a new LocalTime.
LocalTime localTime = hourTimes.get( 7 ).plusMinutes( minutes.get( 2 ) ) ; // Adding 0, 15, 30, or 45 minutes to the on-the-hour `LocalTime` object, resulting in another `LocalTime` object.
Combine the time-of-day with your intended date to get a LocalDateTime object.
LocalDate localDate = LocalDate.of( 2020 , Month.JANUARY , 23 ) ;
LocalDateTime localDateTime = LocalDateTime.of( localDate , localTime ) ;
Store that in a database column of SQL-standard type TIMESTAMP WITHOUT TIME ZONE. (that was "WITHOUT", not "WITH")
Generally best to use prepared statements in SQL rather than string-combining.
myPreparedStatement.setObject( … , localDateTime ) ;
In addition, record the intended time zone for that appointment. There is a data type in Java for this, ZoneId. But not in SQL. So record the zone name as text in your database.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
…
myPreparedStatement.setObject( … , z.toString() ) ; // Record name of zone as text.
By the way, no need to pass when the record was created or last updated. You should be configuring your database to set those value automatically on the server-side.
Separate your business logic from your user-interface. Notice that none of my code above relates to JavaFX.
Define a class just for the appointment. This class should know only the needs of an appointment, the business rules for what defines a valid appointment. This class should know nothing about the database, nor the user-interface.
I do not understand what you are trying to do with time zones in your use of ZonedDateTime. It seems you are trying to form a java.sql.Timestamp object. That is the wrong way to go for two reasons. First, that class is one of the terrible legacy date-time classes supplanted by java.time; never use it. Second, that is the wrong way to book appointments. Future appointments should be recorded as two separate parts, (a) date-with-time-of-day and (b) time zone.
Politicians around the world have shown a penchant for frequently changing the offset of the time zones under their jurisdiction. They do so with little, or even no, forewarning. So if you booked 3:45 PM for next January as a moment, as a specific point on the timeline, the time-of-day for that zone's wall-clock time might be changed by then, with 3:45 PM becoming 3:15 PM or 4:45 PM. Imagine if your software booked an appointment for the specific moment that was 3:45 PM before the zone changed its offset-from-UTC. The customers keeping their appointments will be appearing at 3 PM on the current wall-clock time while your appointment book software will show them as early (or late). To avoid this problem, do not schedule appointments as a moment, keep the date-with-time-of-day (LocalDateTime) separated from the time zone (ZoneId).
Combine the two (date-with-time-of-day & time zone) only when building out a schedule where you need specific moments.
ZoneId z = ZoneId.of( zoneNameFromDatabase ) ;
ZonedDateTime zdt = localDateTimeFromDatabase.atZone( z ) ;
If you need to see that moment in UTC, adjust by extracting a Instant object.
Instant instant = zdt.toInstant() ;
But do not store either the ZonedDateTime or Instant for the future. Store them only after the fact, when recording history. And even then, you record OffsetDateTime, as oddly the JDBC spec does not require support for ZonedDateTime or Instant. You would store that OffsetDateTime object in a column of SQL-standard type TIMESTAMP WITH TIME ZONE ("WITH", not "WITHOUT" as seen above).
OffsetDateTime odt = zdt.toOffsetDateTime() ;
myPreparedStatement.setObject( … , odt ) ; // Record history as a moment, a specific point on the timeline.
If I understand correctly, you can and should put LocalTime objects in your ObservableList. Rather than String objects. If so, do that.
Also use SQL datatype time for the times in your database (they may already be, I didn't notice this information in your question). Pass the LocalTime object to your database using a PrepatedStatement. For example:
yourPreparedStatement.setObject(5, startLocalTime);
yourPreparedStatement.setObject(6, endLocalTime);
Do similarly for date, etc., only using the appropriate types both on the Java and the SQL side.
To calculate end time as 15 minutes after start time:
LocalTime endLocalTime = startLocalTime.plusMinutes(15);
I know I am not giving you complete code, but I hope it will get you a step or two further.
I am having a problem using the ResultSet.getDate() method. I have a date field in MySQL and when I try to get the value, the date obtained is today's date instead of the date in the table specified. I don't know what is causing this error, I have searched other posts, but other errors with getDate() were different, like parsing or data mismatch errors or other kinds of errors. It could be an error due to time zone, because the values of the dates are from yesterday, but there's one row with date of two days ago and it's also returning today's date.
Here's the code:
package dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.joda.time.LocalDate;
import model.Paciente;
import teste.ConnectionFactory;
public class PacienteDao {
// a conexão com o banco de dados
private Connection connection;
public PacienteDao() {
this.connection = new ConnectionFactory().getConnection();
}
public void adiciona(Paciente paciente) {
String sql = "insert into paciente" +
" (nome_paciente,cpf_paciente,rg_paciente,data_nasc)" +
"values (?,?,?,?)";
try {
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setString(1, paciente.getNome_paciente());
stmt.setString(2, paciente.getCpf());
stmt.setString(3, paciente.getRg());
java.sql.Date data_nasc = new java.sql.Date(paciente.getData_nasc().toDate().getTime());
stmt.setDate(4, data_nasc);
stmt.execute();
stmt.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public List<Paciente> listaPacientes() {
List<Paciente> pacientes = new ArrayList<Paciente>();
try {
PreparedStatement stmt = this.connection.prepareStatement("select * from paciente");
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
Paciente paciente = new Paciente();
paciente.setId_paciente(rs.getInt("id_paciente"));
paciente.setNome_paciente(rs.getString("nome_paciente"));
paciente.setCpf(rs.getString("cpf_paciente"));
paciente.setRg(rs.getString("rg_paciente"));
LocalDate dt = new LocalDate();
dt.fromDateFields(rs.getDate("data_nasc"));
paciente.setData_nasc(dt);
pacientes.add(paciente);
}
rs.close();
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
return pacientes;
}
Here's the data that should be returned (CSV):
"1","Lucas","1111111111","12222222","2017-12-19"
"2","Lucas","1111111111","12222222","2017-12-20"
"3","Lucas","1111111111","12222222","2017-12-20"
"4","Leandro","2321","21232","2017-12-20"
Here's the data that is been returned (StackTrace):
Id: 1
Nome: Lucas
CPF: 1111111111
RG: 12222222
Data de Nascimento: 2017-12-21
Id: 2
Nome: Lucas
CPF: 1111111111
RG: 12222222
Data de Nascimento: 2017-12-21
Id: 3
Nome: Lucas
CPF: 1111111111
RG: 12222222
Data de Nascimento: 2017-12-21
Id: 4
Nome: Leandro
CPF: 2321
RG: 21232
Data de Nascimento: 2017-12-21
Like I said one of the rows has a date of two days ago, but it's showing today's date too, so I think isn't a time zone error.
PS: The name of the variables and methods are in Portuguese, because the application is in Portuguese too.
The problem is here
LocalDate dt = new LocalDate();
dt.fromDateFields(rs.getDate("data_nasc"));
The first statement creates a new LocalDate set to today. The second statement is a call to the static method fromDateFields, which should have been flagged as a warning by your IDE an/or compiler. This method returns a new LocalDate object, which you discarded, and does not modify dt. The above should be:
LocalDate dt = LocalDate.fromDateFields(rs.getDate("data_nasc"));
The Answer by Jim Garrison is correct. The much simpler and more intuitive code seen below would have prevented that particular mistake.
In addition, you are:
Ignoring the crucial issue of time zone in determining a date.
Using an older library from a project that recommends you move to their modern replacement classes.
tl;dr
Using java.time classes that replaced Joda-Time.
myResultSet().getObject( … , Instant.class ) // Extract a moment on the timeline in UTC, an `Instant` object.
.atZone( ZoneId.of( "Asia/Kolkata" ) ) // Adjust into a time zone, to determine a date, rendering a `ZonedDateTime` object.
.toLocalDate() // Extract a date-only object, a `LocalDate` without time-of-day and without a time zone.
Avoid legacy classes
You should not be using PreparedStatement::getDate(). Avoid all of the troublesome old date-time classes bundled with the earliest versions of Java, such as Date, Calendar, and the related java.sql types. These are entirely supplanted with the java.time classes and a JDBC 4.2 driver.
Likewise, the Joda-Time project is now in maintenance mode. Its team advises migration to the java.time which they inspired, defined, and implemented in JSR 310.
java.time
Use getObject and setObject methods.
LocalDate ld = myResultSet().getObject( … , LocalDate.class ) ; // For retrieving a standard SQL `DATE` column.
And…
myPrepatedStatement.setObject( … , ld ) ;
That code above is for a standard SQL DATE column which is a date-only value.
But it sounds like you have a moment stored, perhaps the MySQL type TIMESTAMP which seems to track with the standard SQL TIMESTAMP WITH TIME ZONE type. Any provided offset or time zone info is used to adjust the value into UTC upon submission to the database, in MySQL, with a resolution of microseconds.
So the equivalent type in Java is Instant, for a point in the timeline in UTC but with a finer resolution of nanoseconds.
Instant instant = myResultSet.getObject( … , Instant.class ) ;
And…
myPreparedStatement.setObject( … , instant ) ;
Remember that the Instant is always in UTC. But determining a date requires a time zone. For any given moment, the date varies by zone.
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
From there extract the date-only object that seems to be your goal.
LocalDate ld = zdt.toLocalDate() ;
I'm facing this unique issue while retrieving date column values from postgres and export as a CSV using JDK 1.7 Following is a sample output
ID, Date Created, Date Modified
816271, 8/8/2013 14:35 2/2/2015 16:47
830322 13/08/2013 11:48 AM 2/2/2015 16:48
1128312 10/2/2015 16:53 10/2/2015 16:53
1129465 12/2/2015 16:23 12/2/2015 16:23
1130482 16/02/2015 4:28 PM 15/06/2015 7:01 AM
1019527 19/08/2014 4:40 AM 23/02/2015 12:14 PM
1134334 23/02/2015 8:38 AM 4/6/2015 5:16
The problem is, I see that AM/PM being appended those date values where the DAY part is greater than 12. When I look into the database I don't see any AM/PM. In my DO, I've just declared the variable as Date.
Please let me know why this inconsistent formatting happens.
thanks
Following is how I set the date into my DO.
public void setCreatedDate(Date createdDate) {
if (createdDate == null) {
this.mCreatedDate = createdDate; return;
}
this.mCreatedDate = new Date(createdDate.getTime());
}
I'm not using any formatting code at all. Even there is one, I'm not sure why it is not applied to all record
You need to understand that a date-time value stored in a database using a date-time data type has no format. What you are seeing are String representations of that date-time value generated for the convenient viewing by humans. The String is not the date-time.
So your formatting issue with "AM/PM" relates to some code generating that string outside of Postgres. You do not show us that code, so we cannot directly resolve the Question. But you can avoid the problem in the first place if you consciously work with date-time values/objects rather than Strings.
Storing date-time in Postgres
In Postgres, you should generally be using the TIMESTAMP WITH TIME ZONE data type. This type does not actually keep the time zone. Rather it has respect for the time zone, using any passed offset or time zone information accompanying data input to adjust to UTC. The result is then stored in the database. After adjustment, Postgres discards the original offset or time zone info.
Retrieving date-time from Postgres
When retrieving data (a SELECT), you may get a date-time value or you may get a String, depending on the client app (pgAdmin, psql, SQuirreL SQL Client, and such) or your database driver (JDBC and such). If getting a String, an adjustment to some time zone may have been made on your behalf, but that String is not the date-time value. If getting a date-time value, stick with that value for your work rather than converting to strings. In JDBC, that means using java.sql.Timestamp objects, for example.
Java date-time frameworks
If using Java 8 or later technology, you should make use of the new java.time package. If not possible, use the Joda-Time library. Try to avoid java.util.Date/.Calendar & java.text.SimpleDateFormat as they are troublesome and confusing.
Example
Below is a full example of extracting a java.sql.Timestamp from Postgres 9.4, then using java.time or Joda-Time to work with the value.
Data Loss with Joda-Time & java.util.Date
Note that Joda-Time (like java.util.Date) is limited to millisecond precision of fractional seconds. Postgres resolves to microseconds. So converting from Postgres to Joda-Time/java.util.Date means likely data loss. With java.time, no problem as it resolves to nanoseconds.
Code
Written in Java 8 Update 51, using the postgresql-9.4-1201.jdbc41.jar driver with Postgres 9.4.x on Mac OS X Mountain Lion.
String message = "Example of fetching Timestamp from Postgres.";
StringBuilder sql = new StringBuilder();
sql.append( "SELECT now() " + "\n" );
sql.append( ";" );
java.sql.Timestamp ts = null;
try ( Connection conn = DatabaseHelper.instance().connectionInAutoCommitMode() ;
PreparedStatement pstmt = conn.prepareStatement( sql.toString() ); ) {
try ( ResultSet rs = pstmt.executeQuery(); ) {
// Extract data from result set
int count = 0;
while ( rs.next() ) {
count ++;
ts = rs.getTimestamp( 1 );
}
}
} catch ( SQLException ex ) {
logger.error( "SQLException during: " + message + "\n" + ex );
} catch ( Exception ex ) {
logger.error( "Exception during: " + message + "\n" + ex );
}
java.sql.Timestamp
Beware of how the old Java date-time classes implicitly apply your JVM’s current default time zone. While intended to be helpful, it creates no end of confusion. The time zone seen when running this code is America/Los_Angeles which has an offset of −07:00.
String output_SqlTimestamp = ts.toString(); // Confusingly applies your JVM’s current default time zone.
java.time
Use java.time in Java 8 and later.
// If you have Java 8 or later, use the built-in java.time package.
java.time.Instant instant = ts.toInstant();
java.time.ZoneId zoneId = ZoneId.of( "America/Montreal" );
java.time.ZonedDateTime zdt = java.time.ZonedDateTime.ofInstant( instant , zoneId );
String output_UTC = instant.toString();
String output_Montréal = zdt.toString();
System.out.println( "output_SqlTimestamp: " + output_SqlTimestamp );
System.out.println( "output_UTC: " + output_UTC );
System.out.println( "output_Montréal: " + output_Montréal );
Joda-Time
Before Java 8, use Joda-Time.
// Before Java 8, use Joda-Time. (Joda-Time was the inspiration for java.time.)
// IMPORTANT: Joda-Time, like java.util.Date, is limited to milliseconds for fraction of a second. So you may experience data loss from a Postgres date-time value with microseconds.
org.joda.time.DateTime dateTimeMontréal = new org.joda.time.DateTime( ts.getTime() , DateTimeZone.forID( "America/Montreal" ) ); // WARNING: Data lost with microseconds truncated to milliseconds.
org.joda.time.DateTime dateTimeUtc = dateTimeMontréal.withZone( DateTimeZone.UTC );
String output_Joda_dateTimeMontréal = dateTimeMontréal.toString();
String output_Joda_dateTimeUtc = dateTimeUtc.toString();
System.out.println( "output_Joda_dateTimeMontréal: " + output_Joda_dateTimeMontréal );
System.out.println( "output_Joda_dateTimeUtc: " + output_Joda_dateTimeUtc );
When run.
output_SqlTimestamp: 2015-08-24 12:46:06.979144
output_UTC: 2015-08-24T18:46:06.979144Z
output_Montréal: 2015-08-24T14:46:06.979144-04:00[America/Montreal]
output_Joda_dateTimeMontréal: 2015-08-24T14:46:06.979-04:00
output_Joda_dateTimeUtc: 2015-08-24T18:46:06.979Z
I am working on a script where I need to keep checking for individual dates for a month. So far, I can directly check for a month, but I need it for a day, as it returns multiple values for a day.
The code is as follows:
SELECT * FROM table
WHERE end_datetime BETWEEN '2015-03-01 00:00:00' and '2015-04-01 00:00:00'
I need a code in java, where I can check for individual dates (example - between '2015-03-01 00:00:00' and '2015-03-02 00:00:00'; between '2015-03-02 00:00:00' and '2015-03-03 00:00:00' and so on, till the last date is reached, which is '2015-04-01 00:00:00'.
Half-Open
A common approach in date-time work is "Half-Open" (or Half-Closed). The idea is to making the beginning moment inclusive while the ending is exclusive. So a week runs from a Monday to a Monday, an hour runs from 02:00 to 03:00, and a day runs from 2015-03-01 00:00:00 to 2015-03-02 00:00:00. In other words, a day runs from the first moment of a day and goes up to, but does not include, the first moment of the next day.
This approach avoids various problem. The main problem is trying to determine the last moment of the day with infinite fractional second resolved to whole seconds or milliseconds or microseconds or nanoseconds. Better to say "The First of March is any date-time value >= 2015-03-01 00:00:00 AND < 2015-03-02 00:00:00" with an = in the first criterion but not in the second.
This means not using BETWEEN in SQL. Use a pair of comparisons >= and <.
Example SQL for obtaining events happening on first of March this year.
SELECT *
FROM event_
WHERE ( when_ >= '2015-03-01 00:00:00' )
AND ( when_ < '2015-03-02 00:00:00' )
;
Time Zone
The example above has a serious problem. It assumes we mean "today" in UTC time zone. Or perhaps the SQL session’s current default time zone is applied and adjusts the values. Either way it is messy.
Better to specify your desired/expected time zone rather than implicitly rely on some default.
If those date-time values were meant to be midnights in Montréal Québec, then we should say so. The word "midnight" seems to create confusion in this context of computer programming. I prefer the phrase "first moment of the day".
SELECT *
FROM event_
WHERE ( when_ >= '2015-03-01 00:00:00' AT TIME ZONE 'America/Montreal' )
AND ( when_ < '2015-03-02 00:00:00' AT TIME ZONE 'America/Montreal' )
;
Month
This same logic applies to doing a month. Go from first moment of March to first moment of April.
SELECT *
FROM event_
WHERE ( when_ >= '2015-03-01 00:00:00' AT TIME ZONE 'America/Montreal' )
AND ( when_ < '2015-04-01 00:00:00' AT TIME ZONE 'America/Montreal' )
;
Java
When working in Java, we can use the new java.time package built into Java 8 and later.
ISO 8601
First, for convenience, we convert your input strings to comply with standard formats defined by ISO 8601. The java.time framework uses ISO 8601 by default when parsing and generating string representations of date-time values. So no need for us to define explicit formatters.
String inputStart = "2015-03-01 00:00:00"; // Month of March.
String inputStop = "2015-04-01 00:00:00";
String inputStartStandard = inputStart.replace( " " , "T" );
String inputStopStandard = inputStop.replace( " " , "T" );
LocalDateTime
I assume your input strings represent the first moment of days in a certain time zone. I'll arbitrarily go with Québec as an example. The actual input strings lack any such information, so first we parse them as "homeless" LocalDateTime values without any time zone.
java.time.LocalDateTime localStart = LocalDateTime.parse( inputStartStandard );
java.time.LocalDateTime localStop = LocalDateTime.parse( inputStopStandard );
Next we adjust them to Québec time zone.
java.time.ZonedId zoneId = ZoneId.of( "America/Montreal" );
java.time.ZonedDateTime zdtStart = ZonedDateTime.of( localStart , zoneId );
java.time.ZonedDateTime zdtStop = ZonedDateTime.of( localStop , zoneId );
JDBC 4.2
Some day in the future we will be able to proceed to executing SQL at this point. But today most JDBC drivers have not yet been updated to JDBC 4.2, so they cannot directly handle the new java.time types.
Convert to old java.sql.Timestamp
While waiting for future JDBC drivers, we can use the new methods added to the old java.sql.Timestamp class to conveniently convert. The conversion requires an Instant object, which is a point on the timeline without time zone information (UTC basically). Our ZonedDateTime offer a toInstant method, just what we need.
java.sql.Timestamp tsStart = new java.sql.Timestamp( zdtStart.toInstant() );
java.sql.Timestamp tsStop = new java.sql.Timestamp( zdtStop.toInstant() );
No data loss
Note that all these mentioned classes, ( java.sql.Timestamp, java.time. ZonedDateTime, and java.time.Instant ), have nanosecond resolution of their fractional seconds. So no data loss involved going between them. Converting to the old java.util.Date/.Calendar or third-party Joda-Time library support only millisecond resolution, so may involve data loss.
Be aware of your database’s date-time resolution. Many databases such as Postgres use microsecond. Any value generated in java.time with nanoseconds will be truncated when saved to a database using microseconds. For example 2015-01-02 03:04:05.123456789 changes to 2015-01-02 03:04:05.123456, only six digits of fractional second.
PreparedStatement
Build the text for a SQL statement. While not required in this case, best to make a habit of using PreparedStatement to avoid SQL Injection security risk.
StringBuilder sql = new StringBuilder();
sql.append( "SELECT * " ).append( "\n" );
sql.append( "FROM event_ " ).append( "\n" );
sql.append( "WHERE ( when_ >= ? ) " ).append( "\n" );
sql.append( "AND ( when_rc_ < ? ) " ).append( "\n" );
sql.append( "ORDER BY when_ ASC " ).append( "\n" );
sql.append( ";" )append( "\n" );
Execute SQL
Lastly, execute the SQL. Pass the start and stop timestamp objects into the PreparedStatement.
try ( Connection conn = DatabaseHelper.instance().connectionInAutoCommitMode() ;
PreparedStatement pstmt = conn.prepareStatement( sql.toString() ); ) {
pstmt.setTimestamp( 1 , tsStart );
pstmt.setTimestamp( 2 , tsStop );
try ( ResultSet rs = pstmt.executeQuery(); ) {
while ( rs.next() ) {
// …
}
}
} catch ( SQLException ex ) {
logger.error( "SQLException during: " + message + "\n" + ex );
} catch ( Exception ex ) {
logger.error( "Exception during: " + message + "\n" + ex );
}
All this example code was hand-written without running, untested.
Day at a time
The Question is not clear. If you want a day at a time, loop by adding a day to the start.
ZonedDateTime zdtNextDay = zdtStart.plusDays( 1 );
You can also get the beginning of next month similarly, with call to plusMonths( 1 ).
ZonedDateTime zdtNextMonth = zdtStart.plusMonths( 1 );
You can test in your loop to see if the "next day" is still before the "next month" by calling isBefore.
if( zdtNextDay.isBefore( zdtNextMonth ) ) {
…
}
Complete month example
Here is a complete example of looping day-by-day of current month.
We must get the first moment of the day. To do that in java.time, we must start with a local date-only (LocalDate) where "local" means "any locality" without any specific time zone. Then we assign both a time zone and a time-of-day by calling atStartOfDay to produce a ZonedDateTime object. You might think the day starts at the time 00:00:00 but no, not always. Some time zones such as Palestine start Daylight Saving Time at midnight so the day starts at 01:00.
The old date-time classes before java.time, such as java.sql.Timestamp, implement toString methods that implicitly apply the JVM’s current default time zone. Though done with good intentions, this approach proved to be confusing. We see this in the code below. This code shows that when run in the United States west coast time zone with offset of -07:00, midnight in Québec with offset of -04:00 is three hours different. So midnight in Québec is reported as 9 PM (21:00) of the previous date out on the west coast.
java.time.ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdtThisMonthStart = LocalDate.now( zoneId ).withDayOfMonth( 1 ).atStartOfDay( zoneId );
java.time.ZonedDateTime zdtNextMonthStart = zdtThisMonthStart.plusMonths( 1 );
java.time.ZonedDateTime zdtDayStart = zdtThisMonthStart; // Initialize variable to be modified in loop.
System.out.println( "JVM’s current default time zone applied implicitly by java.sql.Timestamp’s 'toString' method: " + java.util.TimeZone.getDefault( ) );
while ( zdtDayStart.isBefore( zdtNextMonthStart ) ) {
java.time.ZonedDateTime zdtNextDayStart = zdtDayStart.plusDays( 1 );
java.sql.Timestamp tsStart = java.sql.Timestamp.from( zdtDayStart.toInstant( ) );
java.sql.Timestamp tsStop = java.sql.Timestamp.from( zdtNextDayStart.toInstant( ) );
System.out.print( "In java.time, Day is: [" + zdtDayStart + "/" + zdtNextDayStart + "]. " );
System.out.println( "In java.sql.Timestamp, Day is: [" + tsStart + "/" + tsStop + "]" );
//
// … Do SQL work, such as the try-catch-catch seen above in this Answer.
//
// Prep for next loop. Increment to next day.
zdtDayStart = zdtDayStart.plusDays( 1 );
}
When run.
JVM’s current default time zone applied implicitly by java.sql.Timestamp’s 'toString' method: sun.util.calendar.ZoneInfo[id="US/Pacific",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=US/Pacific,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]
In java.time, Day is: [2015-08-01T00:00-04:00[America/Montreal]/2015-08-02T00:00-04:00[America/Montreal]]. In java.sql.Timestamp, Day is: [2015-07-31 21:00:00.0/2015-08-01 21:00:00.0]
…
In java.time, Day is: [2015-08-31T00:00-04:00[America/Montreal]/2015-09-01T00:00-04:00[America/Montreal]]. In java.sql.Timestamp, Day is: [2015-08-30 21:00:00.0/2015-08-31 21:00:00.0]
Option 1:
See if you need all data of dates then you first get all the records in single query order them by end_time. and then you can loop records.
Options 2:
You can also fetch records by date simply using '>' and '<' operator and you need to fire query in loop using prepared statement till last date is reached in loop. Keep increasing date using java Calendar in loop