How to fetch Records for every month? - java

I am using JDBC with Java to fetch account IDs records like below:
select s.account_id, s.status, o.phonenumber
from table1 s, table2 o
where s.valid_from='1-DEC-16'
and o.phonenumber IS NOT NULL
and s.validTo=sysdate
and s.status='active'
and and s.account_id = o.account_id;
In result set I am getting all accountids for active records. Is there any way I can execute this query every month and output account IDs at the end of every month? In that case how should I give valid_from and valid_to dates automatically?

Use smart objects, not dumb strings, when you exchanging data with your database.
With JDBC 4.2 or later, you can directly exchange modern java.time objects with your database.
For a date-only value, use LocalDate. To get the first day of month, use YearMonth.
ZoneId z = ZoneId.of( “Africa/Tunis” ) ;
YearMonth ym = YearMonth.now( z ) ;
LocalDate firstOfThisMonth = ym.atDay( 1 ) ;
LocalDate firstOfNextMonth = ym.plusMonths( 1 ).atDay( 1 ) ;
Use SQL something like:
… WHERE happened >= ? AND happened < ?
Pass a value for that placeholder.
myPreparedStatement.setObject( 1 , firstOfMonth ) ;
myPreparedStatement.setObject( 2 , firstOfNextMonth ) ;
This approach is known as Half-Open, where the beginning is inclusive while the ending is exclusive. Usually the best way to define a span of time. Note that for this approach we do not use the SQL operator BETWEEN as it is fully closed.
Retrieval.
LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;

Related

How do I convert a SQL date to String date in different format?

I have a SQL query result that is returning a date in this format ("yyyy-MM-dd hh:mm:ss"). My requirement is to convert it to a string (MM/yyyy) and put it in a Map for further comparisons. How Do I proceed to do that. So far this is what I have
Map<String, String> postingDateAmountMap = new HashMap<>();
postingDateAmountMap.put(rs.getString(3), rs.getString(2));
rs.getString(2) is returning date as 2021-03-01 00:00:00 whereas I need it as 03/2021 in the map.
NOTE: getString(3) and getString(2) are the ColumnIndex
Thanks in Advance.
tl;dr
Map< String , YearMonth > map = … ;
map.put(
rs.getString( 3 ) , // key
YearMonth.from(
myResultSet.getObject( 2 , LocalDateTime.class )
) // value
);
Use objects, not text
Retrieve smart objects from your database rather than dumb text.
LocalDateTime
If your column is of a type akin to the SQL-standard type TIMESTAMP WITHOUT TIME ZONE, use java.time.LocalDateTime.
Be aware that these types do not represent a moment as they purposely lack the context of a time zone or offset-from-UTC.
JDBC 4.2
JDBC 4.2 and later requires a JDBC driver to support the LocalDateTime type.
Call the ResultSet#getObject method rather than getString.
LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ;
YearMonth
Change your map to a key of String and a value of YearMonth. The YearMonth class represents, well, a year and a month. No day-of-month. Just what you are aiming for in your Question.
Map< String , YearMonth > map = new HashMap<>();
String k = rs.getString(3) ; // Named `k` for key of map entry.
LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ;
YearMonth v = YearMonth.from( ldt ) ; // Named `v` for value of map entry.
map.put( k , v );
Generating text
Later, when you want to present that YearMonth to the user as text in format of MM/YYYY, use DateTimeFormatter to generate text.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "MM/uuuu" ) ;
String output = myYearMonth.format( f ) ;
hi RishiAutomation you can do it by the method split in one line as follow
postingDateAmountMap.put(rs.getString(3), rs.getString(2).split("-")[1] + "/" + rs.getString(2).split("-")[0]);
or in two lines as follow
String[] newDate = rs.getString(2).split("-");
postingDateAmountMap.put(rs.getString(3), newDate[1] + "/" + newDate[0]);

Last date of next month in Java

How to get the last date of next month in Java?
Background: I have project, user only interested in the orders should be completed by the end of next month. So I need to get the last date of next month and compare to the order end date, if the order end date smaller than the last date of next month, that means this order should be selected out.
My solution is like this, but not sure is it the best one:
public static boolean shouldCompleteByNextMonth(final Date endDate) {
final LocalDate now = LocalDate.now(); // Get current local date.
final LocalDate nextMonth = now.plusMonths(1); // Get next month.
final int daysInNextMonth = nextMonth.lengthOfMonth(); // Get the length of next month
final LocalDate lastLocalDateOfNextMonth = nextMonth.plusDays(daysInNextMonth - now.getDayOfMonth()); // Get the last Date of next month
// default time zone
final ZoneId defaultZoneId = ZoneId.systemDefault();
// convert last locale date to a Date
final Date lastDateOfNextMonth = Date.from(lastLocalDateOfNextMonth.atStartOfDay(defaultZoneId).toInstant());
// Compare with the given endDate, if last date of next month is after it, return true, else, return false.
return lastDateOfNextMonth.after(endDate);
}
The TemporalAdjusters class contains some static TemporalAdjusters, amongst them lastDayOfMonth(), so you can do
LocalDate.now()
.plusMonth(1)
.with(TemporalAdjusters.lastDayOfMonth());
It seems easier to calculate the first day of the month after that and then substracting one day...
LocalDate targetDate = LocalDate.now()
.withDayOfMonth(1)
.plusMonths(2)
.minusDays(1);
tl;dr
YearMonth
.now(
ZoneId.of( "Africa/Tunis" )
)
.plusMonths( 1 )
.atEndOfMonth()
Returns a LocalDate.
.toString(): 2019-12-31
But… better to use Half-Open approach rather than last day of month.
YearMonth
The YearMonth class represents a month as a whole.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
YearMonth nextMonth = YearMonth.now( z ).plusMonths( 1 ) ;
Notice the use of time zone, ZoneId. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight on November 30th in Tokyo Japan is a new month, while in Toledo Ohio US it is still “last month”. So if at runtime the current moment is on the first or last day of the month, the time zone is required for accuracy in determining the current month.
Ask for a LocalDate for the last day of month.
LocalDate lastDayOfMonth = nextMonth.atEndOfMonth() ;
Half-Open
The approach commonly taken in defying a span of time is the Half-Open approach. The beginning is inclusive while the ending is exclusive.
This means a day begins with the first moment of the day (usually 00:00, but not always), and runs up to, but does not include, the first moment of the next day.
In your case, a month begi s on the first and runs up to, but does include, the first of the next month.
The Half-Open approach allows for spans of time that nearly abut one another without gaps.
Your search criteria logic should be find orders where the order is equal to or greater than the first of the month AND less than the first of the following month. In Java that would be >= && <.
A simpler form of that logic is find orders where the order date is not before the first of month AND *is before** the first of following month.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
YearMonth nextMonth = YearMonth.now( z ).plusMonths( 1 ) ;
LocalDate start = nextMonth.atDay( 1 ) ;
LocalDate stop = nextMonth.plusMonths( 1 ).atDay( 1 ) ;
LocalDateRange
Add the ThreeTen-Extra library to your project to benefit from LocalDateRange. This class represents a span of time as a pair of LocalDate objects. It offers handy methods for comparisons such as abuts and contains.
LocalDateRange nextMonthRange =
LocalDateRange.of(
nextMonth.atDay( 1 ) ,
nextMonth.plusMonths( 1 ).atDay( 1 )
)
;
Database
You mention a database in your Question.
Here is a rough-draft of example JDBC code for querying for orders through the next month. That is, orders whose due-date is before the first day of the month after next month.
Notice how we add two months to the current month, to get the month after next.
The key part of the SQL is: WHERE due_date_ < ? for a Half-Open query of dates running up to, but not including, the passed date (the first of month after next month).
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
LocalDate firstOfMonthAfterNextMonth = YearMonth.now( z ).plusMonths( 2 ).atDay( 1 ) ;
String sql = "SELECT order_number_ FROM order_ WHERE due_date_ < ? ; " ;
try(
Connection conn = myDataSource.getConnection() ;
PreparedStatement ps = conn.prepareStatement( sql ) ;
)
{
ps.setObject( 1 ; firstOfMonthAfterNextMonth ) ;
try(
ResultSet rs = ps.executeQuery() ;
)
{
while ( rs.next() ) {
…
}
}
} catch ( SQLException e ) {
e.printStackTrace();
…
} catch ( SQLTimeoutException e ) {
e.printStackTrace();
…
}

How does JDBC/Postgres compare a timezone-less java.util.Date with a Timestamp?

We have a Postgres table that has two TIMESTAMP WITHOUT TIME ZONE columns, prc_sta_dt and prc_end_dt. We check to see whether a java.util.Date falls in between the start and end dates.
Here is some of the Java code, which is simplified but gets the point across.
// This format expects a String such as 2018-12-03T10:00:00
// With a date and a time, but no time zone
String timestamp = "2018-12-03T10:00:00";
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date searchDate = formatter.parse(timestamp);
// Here's the Postgres query
String query = "select promotion_cd from promotions " +
"where prc_sta_dt <= :srch_dt and prc_end_dt >= :srch_dt";
Map<String, Object> map = new HashMap<String, Object>();
map.put("srch_dt", searchDate);
List<Promotion> promotions = jdbcTemplate.query(query, map, promotionMapper);
In our Postgres table, we have promotions that start at 9am on 12/3/2018 and end at 3pm on the same day. The prc_sta_dt and prc_end_dt colums in our database for these rows are 2018-12-03 09:00:00.0 and 2018-12-03 15:00:00.0
Question: When JDBC/Postgres accepts our searchDate and compares it to these timestamps, will it accept the given search date of 10am (2018-12-03T10:00:00) or will it treat this time as being under the time zone that the server is running on, and then convert that to UTC?
For example, if the server is running in Chicago, then will it interpret 10 am as 10am CST and then convert that to 4pm UTC before doing the comparison in the database? If so then we're out of luck!
I doubt this would happen, but I just want to make sure so there are no surprises.
Wrong data type, Date is not a date
A java.util.Date object represents a moment in UTC, a specific point on the timeline. So it represents the combination of a date, a time-of-day, and an offset-from-UTC of zero (for UTC itself). Among the many poor design choices in this terrible class is its misleading name that has confused countless Java programmers.
TIMESTAMP WITHOUT TIME ZONE
If you care about moments, then your database column should not be of type TIMESTAMP WITHOUT TIME ZONE. That data type represents a date and a time-of-day without any concept of time zone or offset-from-UTC. As such, by definition, that type cannot represent a moment, is not a point on the timeline. This type should only be used when you mean a date-with-time anywhere or everywhere.
Examples:
“Christmas starts after stroke of midnight at beginning of December 25, 2018” where Christmas in Kiribati comes first, India later, and Africa even later.
“Company-wide memo: Each of our factories in Delhi, Düsseldorf, and Detroit will close one hour early at 16:00 on January 21st” where 4 PM at each factory is three different moments, each several hours apart.
TIMESTAMP WITH TIME ZONE
When tracking specific a specific moment, a single point on the timeline, use a column of type TIMESTAMP WITH TIME ZONE. In Postgres, such values are stored in UTC. Any time zone or offset info submitted with an input is used to adjust into UTC, then the zone/offset info is discarded.
BEWARE: Some tools may have the well-intentioned but unfortunate anti-feature of injecting a time zone after retrieving the value in UTC, thereby misrepresenting what was actually stored.
Comparing a moment to values of TIMESTAMP WITHOUT TIME ZONE
As for comparing a moment to values in your column of type TIMESTAMP WITHOUT TIME ZONE, doing so would generally not make sense.
But if you are clear-headed and educated about date-time handling, and making this comparison is sensible in your business logic, let's forge on.
Wrong classes
You are using lousy, terrible, awful date-time classes (Date, SimpleDateFormat, etc.) that were supplanted years ago by the java.time classes. Do yourself a favor: Stop using the legacy date-time classes. Use only java.time.
If given a moment as a java.util.Date, use the new methods added to the old classes to convert. In particular, java.util.Date is replaced by Instant.
Instant instant = myJavaUtilDate.toInstant() ; // Convert from legacy class to modern class.
Specify the time zone in which you want to adjust your Instant moment in UTC for comparison. For example, if your database was built by someone who did not understand proper date-time handling, and has been using the TIMESTAMP WITHOUT TIME ZONE column to store date-with-time values that were taken from the wall-clock time of Québec, then use the time zone America/Montreal.
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/Montreal" ) ;
Apply that zone to our Instant to get a ZonedDateTime object.
ZonedDateTime zdt = instant.atZone( z ) ;
Our resulting ZonedDateTime object represents the same moment as the Instant object, same point on the timeline, but viewed with a different wall-clock time.
To hammer a square-peg into a round-hole, let's convert that ZonedDateTime object to a LocalDateTime object, thereby stripping away the time zone information and leaving only a date-with-time-of-day value.
LocalDateTime ldt = zdt.toLocalDateTime() ;
Half-Open
where prc_sta_dt <= :srch_dt and prc_end_dt >= :srch_dt
That logic is prone to failure. Generally, the best practice in date-time handling when defining a span-of-time to use Half-Open, where the beginning is inclusive while the ending is exclusive.
So use this:
WHERE instant >= start_col AND instant < stop_col ;
For a PreparedStatement, we would have placeholders.
WHERE ? >= start_col AND ? < stop_col ;
On the Java side, as of JDBC 4.2 we can directly exchange java.time objects with the database via getObject and setObject methods.
You might be able to pass an Instant depending on your JDBC driver. Support for Instant is not required by the JDBC spec. So try it, or read the doc for your driver.
myPreparedStatement.setObject( 1 , instant ) ;
myPreparedStatement.setObject( 2 , instant ) ;
If Instant is not supported, convert from Instant to an OffsetDateTime set to UTC. Support for OffsetDateTime is required by the spec.
myPreparedStatement.setObject( 1 , instant.atOffset( ZoneOffset.UTC ) ) ;
myPreparedStatement.setObject( 2 , instant.atOffset( ZoneOffset.UTC ) ) ;
Retrieval.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
Always specify time zone
For example, if the server is running in Chicago, then will it interpret 10 am as 10am CST and then convert that to 4pm UTC before doing the comparison in the database?
A programmer should never depend on the time zone (or locale, by the way) currently set as the default on the host OS or JVM. Both are out of your control. And both can change at any moment during runtime!
Always specify the time zone by passing the optional argument to various date-time methods. Making those optional was a design flaw in java.time in my opinion, as programmers all too often ignore the issue of time zone, at their own peril. But that is one of very few design flaws in an amazingly useful and elegant framework.
Notice in our code above we specified the desired/expected time zone. The current default time zone of our host OS, our Postgres database connection, and our JVM will not alter the behavior of our code.
Current moment
If you want the current moment use any of these:
Instant.now()Always in UTC, by definition.
OffsetDateTime.now( someZoneOffset )Current moment as seen in the wall-clock time of a particular offset-from-UTC.
ZonedDateTime.now( someZoneId )Current moment as seen in the wall-clock time used by the people living in a particular region.
Java 7 and ThreeTen-Backport
If you are using Java 7, then you have no java.time classes built-in. Fortunately, the inventor of JSR 310 and java.time, Stephen Colebourne, also led the ThreeTen-Backport project to produce a library providing most of the java.time functionality to Java 6 & 7.
Here is a complete example app in a single .java file showing the use of back-port in Java 7 with the H2 Database Engine.
In Java 7, JDBC 4.2 is not available, so we cannot directly use the modern classes. We fall back to using java.sql.Timestamp which actually represents a moment in UTC, but which H2 stores into a column of TIMESTAMP WITHOUT TIME ZONE taking the date and the time-of-day as-is (using the wall-clock time of UTC) while ignoring the UTC aspect. I have not tried this in Postgres, but I expect you will see the same behavior.
package com.basilbourque.example;
import java.sql.*;
import org.threeten.bp.*;
public class App {
static final public String databaseConnectionString = "jdbc:h2:mem:localdatetime_example;DB_CLOSE_DELAY=-1"; // The `DB_CLOSE_DELAY=-1` keeps the in-memory database around for multiple connections.
public static void main ( String[] args ) {
App app = new App();
app.doIt();
}
private void doIt () {
System.out.println( "Bonjour tout le monde!" );
// java.sql.Timestamp ts = DateTimeUtils.toSqlTimestamp( ZonedDateTime.of( 2018 , 1 , 23 , 12 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() );
// System.out.println( ts );
this.makeDatabase();
java.util.Date d = new java.util.Date(); // Capture the current moment using terrible old date-time class that is now legacy, supplanted years ago by the class `java.time.Instant`.
this.fetchRowsContainingMoment( d );
}
private void makeDatabase () {
try {
Class.forName( "org.h2.Driver" );
} catch ( ClassNotFoundException e ) {
e.printStackTrace();
}
try (
Connection conn = DriverManager.getConnection( databaseConnectionString ) ; // The `mem` means “In-Memory”, as in “Not persisted to disk”, good for a demo.
Statement stmt = conn.createStatement() ;
) {
String sql = "CREATE TABLE event_ ( \n" +
" pkey_ IDENTITY NOT NULL PRIMARY KEY , \n" +
" name_ VARCHAR NOT NULL , \n" +
" start_ TIMESTAMP WITHOUT TIME ZONE NOT NULL , \n" +
" stop_ TIMESTAMP WITHOUT TIME ZONE NOT NULL \n" +
");";
stmt.execute( sql );
// Insert row.
sql = "INSERT INTO event_ ( name_ , start_ , stop_ ) VALUES ( ? , ? , ? ) ;";
try (
PreparedStatement preparedStatement = conn.prepareStatement( sql ) ;
) {
preparedStatement.setObject( 1 , "Alpha" );
// We have to “fake it until we make it”, using a `java.sql.Timestamp` with its value in UTC while pretending it is not in a zone or offset.
// The legacy date-time classes lack a way to represent a date with time-of-day without any time zone or offset-from-UTC.
// The legacy classes have no counterpart to `TIMESTAMP WITHOUT TIME ZONE` in SQL, and have no counterpart to `java.time.LocalDateTime` in Java.
preparedStatement.setObject( 2 , DateTimeUtils.toSqlTimestamp( ZonedDateTime.of( 2018 , 1 , 23 , 12 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() ) );
preparedStatement.setObject( 3 , DateTimeUtils.toSqlTimestamp( ZonedDateTime.of( 2018 , 2 , 23 , 12 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() ) );
preparedStatement.executeUpdate();
preparedStatement.setString( 1 , "Beta" );
preparedStatement.setObject( 2 , DateTimeUtils.toSqlTimestamp( ZonedDateTime.of( 2018 , 4 , 23 , 14 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() ) );
preparedStatement.setObject( 3 , DateTimeUtils.toSqlTimestamp( ZonedDateTime.of( 2018 , 5 , 23 , 14 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() ) );
preparedStatement.executeUpdate();
preparedStatement.setString( 1 , "Gamma" );
preparedStatement.setObject( 2 , DateTimeUtils.toSqlTimestamp( ZonedDateTime.of( 2018 , 11 , 23 , 16 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() ) );
preparedStatement.setObject( 3 , DateTimeUtils.toSqlTimestamp( ZonedDateTime.of( 2018 , 12 , 23 , 16 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() ) );
preparedStatement.executeUpdate();
}
} catch ( SQLException e ) {
e.printStackTrace();
}
}
private void fetchRowsContainingMoment ( java.util.Date moment ) {
// Immediately convert the legacy class `java.util.Date` to a modern `java.time.Instant`.
Instant instant = DateTimeUtils.toInstant( moment );
System.out.println( "instant.toString(): " + instant );
String sql = "SELECT * FROM event_ WHERE ? >= start_ AND ? < stop_ ORDER BY start_ ;";
try (
Connection conn = DriverManager.getConnection( databaseConnectionString ) ;
PreparedStatement pstmt = conn.prepareStatement( sql ) ;
) {
java.sql.Timestamp ts = DateTimeUtils.toSqlTimestamp( instant );
pstmt.setTimestamp( 1 , ts );
pstmt.setTimestamp( 2 , ts );
try ( ResultSet rs = pstmt.executeQuery() ; ) {
while ( rs.next() ) {
//Retrieve by column name
Integer pkey = rs.getInt( "pkey_" );
String name = rs.getString( "name_" );
java.sql.Timestamp start = rs.getTimestamp( "start_" );
java.sql.Timestamp stop = rs.getTimestamp( "stop_" );
// Instantiate a `Course` object for this data.
System.out.println( "Event pkey: " + pkey + " | name: " + name + " | start: " + start + " | stop: " + stop );
}
}
} catch ( SQLException e ) {
e.printStackTrace();
}
}
}
When run.
instant.toString(): 2018-12-04T05:06:02.573Z
Event pkey: 3 | name: Gamma | start: 2018-11-23 16:30:00.0 | stop: 2018-12-23 16:30:00.0
Java 8 without ThreeTen-Backport
And here is that same example, conceptually, but in Java 8 or later where we can use the java.time classes built-in without the ThreeTen-Backport library.
package com.basilbourque.example;
import java.sql.*;
import java.time.*;
public class App {
static final public String databaseConnectionString = "jdbc:h2:mem:localdatetime_example;DB_CLOSE_DELAY=-1"; // The `DB_CLOSE_DELAY=-1` keeps the in-memory database around for multiple connections.
public static void main ( String[] args ) {
App app = new App();
app.doIt();
}
private void doIt ( ) {
System.out.println( "Bonjour tout le monde!" );
this.makeDatabase();
java.util.Date d = new java.util.Date(); // Capture the current moment using terrible old date-time class that is now legacy, supplanted years ago by the class `java.time.Instant`.
this.fetchRowsContainingMoment( d );
}
private void makeDatabase ( ) {
try {
Class.forName( "org.h2.Driver" );
} catch ( ClassNotFoundException e ) {
e.printStackTrace();
}
try (
Connection conn = DriverManager.getConnection( databaseConnectionString ) ; // The `mem` means “In-Memory”, as in “Not persisted to disk”, good for a demo.
Statement stmt = conn.createStatement() ;
) {
String sql = "CREATE TABLE event_ ( \n" +
" pkey_ IDENTITY NOT NULL PRIMARY KEY , \n" +
" name_ VARCHAR NOT NULL , \n" +
" start_ TIMESTAMP WITHOUT TIME ZONE NOT NULL , \n" +
" stop_ TIMESTAMP WITHOUT TIME ZONE NOT NULL \n" +
");";
stmt.execute( sql );
// Insert row.
sql = "INSERT INTO event_ ( name_ , start_ , stop_ ) VALUES ( ? , ? , ? ) ;";
try (
PreparedStatement preparedStatement = conn.prepareStatement( sql ) ;
) {
preparedStatement.setObject( 1 , "Alpha" );
// We have to “fake it until we make it”, using a `java.sql.Timestamp` with its value in UTC while pretending it is not in a zone or offset.
// The legacy date-time classes lack a way to represent a date with time-of-day without any time zone or offset-from-UTC.
// The legacy classes have no counterpart to `TIMESTAMP WITHOUT TIME ZONE` in SQL, and have no counterpart to `java.time.LocalDateTime` in Java.
preparedStatement.setObject( 2 , ZonedDateTime.of( 2018 , 1 , 23 , 12 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() );
;
preparedStatement.setObject( 3 , ZonedDateTime.of( 2018 , 2 , 23 , 12 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() );
preparedStatement.executeUpdate();
preparedStatement.setString( 1 , "Beta" );
preparedStatement.setObject( 2 , ZonedDateTime.of( 2018 , 4 , 23 , 14 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() );
preparedStatement.setObject( 3 , ZonedDateTime.of( 2018 , 5 , 23 , 14 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() );
preparedStatement.executeUpdate();
preparedStatement.setString( 1 , "Gamma" );
preparedStatement.setObject( 2 , ZonedDateTime.of( 2018 , 11 , 23 , 16 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() );
preparedStatement.setObject( 3 , ZonedDateTime.of( 2018 , 12 , 23 , 16 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() );
preparedStatement.executeUpdate();
}
} catch ( SQLException e ) {
e.printStackTrace();
}
}
private void fetchRowsContainingMoment ( java.util.Date moment ) {
// Immediately convert the legacy class `java.util.Date` to a modern `java.time.Instant`.
Instant instant = moment.toInstant();
System.out.println( "instant.toString(): " + instant );
String sql = "SELECT * FROM event_ WHERE ? >= start_ AND ? < stop_ ORDER BY start_ ;";
try (
Connection conn = DriverManager.getConnection( databaseConnectionString ) ;
PreparedStatement pstmt = conn.prepareStatement( sql ) ;
) {
pstmt.setObject( 1 , instant );
pstmt.setObject( 2 , instant );
try ( ResultSet rs = pstmt.executeQuery() ; ) {
while ( rs.next() ) {
//Retrieve by column name
Integer pkey = rs.getInt( "pkey_" );
String name = rs.getString( "name_" );
Instant start = rs.getObject( "start_" , OffsetDateTime.class ).toInstant();
Instant stop = rs.getObject( "stop_" , OffsetDateTime.class ).toInstant();
// Instantiate a `Course` object for this data.
System.out.println( "Event pkey: " + pkey + " | name: " + name + " | start: " + start + " | stop: " + stop );
}
}
} catch ( SQLException e ) {
e.printStackTrace();
}
}
}
When run.
instant.toString(): 2018-12-04T05:10:54.635Z
Event pkey: 3 | name: Gamma | start: 2018-11-24T00:30:00Z | stop: 2018-12-24T00:30:00Z
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Java compareTo Dates cannot be equal

I am trying to compare 2 dates, the first date is from the MySQL database, and the second is from the current date.
as you can see below, there are different dates in the database
But the problem is that I got 3 if statements, which should tell my program if the Database date is before, after or equal the current date. The Before and After statements should work, but it can see the date 2018-06-12 should be equal to the current date so it ends in the "before statement".
Hope you can see what I have done wrong.
private static void Resetter() throws ParseException, SQLException {
String host = "****";
String username = "root";
String mysqlpassword = "";
//Querys
String query = "select * from accounts";
String queryy = "update accounts set daily_search_count = 0 where id = ?";
Connection con = DriverManager.getConnection(host, username, mysqlpassword);
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(query);
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
dateFormat.setTimeZone( TimeZone.getTimeZone( "UTC" ));
Date currentDate = new Date();
while(rs.next()){
System.out.println(dateFormat.format(currentDate));
if (rs.getDate(5).compareTo(currentDate) > 0) {
// System.out.println("Database-date is after currentDate");
} else if (rs.getDate(5).compareTo(currentDate) < 0) {
// System.out.println("Database-date is before currentDate");
PreparedStatement updatexdd = con.prepareStatement(queryy);
updatexdd.setInt(1, rs.getInt(1));
int updatexdd_done = updatexdd.executeUpdate();
} else if (rs.getDate(5).compareTo(currentDate) == 0) {
// System.out.println("Database-date is equal to currentDate");
} else {
System.out.println("Any other");
}
}
}
tl;dr
LocalDate today = LocalDate.now( ZoneOffset.UTC ) ;
Instant instant = myResultSet.getObject( … , Instant.class ) ; // Retrieve a `TIMESTAMP WITH TIME ZONE` value in database as an `Instant` for a date with time-of-day in UTC with a resolution as fine as nanoseconds.
LocalDate ld = instant.atOffset( ZoneOffset.UTC ).toLocalDate() ; // Extract a date-only value without time-of-day and without time zone.
if ( ld.isBefore( today ) ) { … } // Compare `LocalDate` objects.
else if ( ld.isEqual( today ) ) { … }
else if ( ld.isAfter( today ) ) { … }
else { … handle error }
java.time
You are using, and misusing, troublesome old date-time classes.
As others pointed out:
A SQL-standard DATE type holds only a date without a time-of-day and without a timezone
The legacy java.util.Date class is misnamed, holding both a date and a time-of-day in UTC.
The legacy java.sql.Date class pretends to hold only a date but actually has a time-of-day because this class inherits from the one above, while the documentation tells us to ignore that fact in our usage. (Yes, this is confusing, and is a bad design, a clumsy hack.)
Never use the java.util.Date, java.util.Calendar, java.sql.Timestamp, java.sql.Date, and related classes. Instead, use only the sane java.time classes. They lead the industry in clean-design and depth of understanding of date-time handling gleaned from the experience of their predecessor, the Joda-Time project.
For date-only value, stored in SQL-standard database type of DATE, use java.time.LocalDate.
LocalDate ld = myResultSet.get( … , LocalDate.class ) ; // Retrieving from database.
myPreparedStatement.setObject( … , ld ) ; // Storing in database.
For a date with time-of-day in UTC value, stored in a SQL-standard database type of TIMESTAMP WITH TIME ZONE, use java.time.Instant. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant instant = myResultSet.get( … , Instant.class ) ; // Retrieving from database.
myPreparedStatement.setObject( … , instant ) ; // Storing in database.
For comparing in Java, use the isEqual, isBefore, isAfter, equals, or compare methods.
Boolean overdue = someLocalDate.isAfter( otherLocalDate ) ;
Time zone
Time zone is crucial in determining a date and a time-of-day from a moment (Instant/TIMESTAMP WITH TIME ZONE).
After retrieving your TIMESTAMP WITH TIME ZONE value from the database as an Instant, adjust into the time zone or offset-from-UTC whose wall-clock time you want to use in perceiving a date & time-of-day. For a time zone, apply a ZoneId to get a ZonedDateTime object. For an offset-from-UTC, apply a ZoneOffset to get a OffsetDateTime object. In either case, extract a date-only value by calling toLocalDate to get a LocalDate object.
In your case, you apparently want to perceive the date as UTC. So apply the constant, ZoneOffset.UTC to get an OffsetDateTime
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
LocalDate ld = odt.toLocalDate() ; // Extract a date-only value without time-of-day and without time zone.
We want to compare with current date in UTC.
LocalDate today = LocalDate.now( ZoneOffset.UTC ) ; // Specify the offset/zone by which you want to perceive the current date.
Compare.
if ( ld.isBefore( today ) ) { … }
else if ( ld.isEqual( today ) ) { … }
else if ( ld.isAfter( today ) ) { … }
else { … handle error }
ISO 8601
Avoid unnecessarily using custom formats such as "yyyy/MM/dd". Use standard ISO 8601 formats whenever possible.
For a date-only value, that would be YYYY-MM-DD.
String output = LocalDate.now().toString() ; // Ex: 2018-01-23
Example with H2
Here is a full example of writing, querying, and reading LocalDate objects from a database column of SQL-standard DATE type.
Using the H2 Database Engine, as I am not a MySQL user. Creating an in-memory database rather than writing to storage. I assume the code would be nearly the same for MySQL.
try (
Connection conn = DriverManager.getConnection( "jdbc:h2:mem:trashme" )
) {
String sql = "CREATE TABLE " + "tbl_" + " (\n" +
" uuid_ UUID DEFAULT random_uuid() , \n" + // Every table should have a primary key.
" when_ DATE \n" + // Three columns per the Question.
" );";
try (
Statement stmt = conn.createStatement() ;
) {
stmt.execute( sql );
}
sql = "INSERT INTO tbl_ ( when_ ) VALUES ( ? ) ;";
LocalDate start = LocalDate.of( 2018 , Month.JANUARY , 23 );
LocalDate ld = start; // Keep the `start` object for use later.
try (
PreparedStatement ps = conn.prepareStatement( sql )
) {
for ( int i = 1 ; i <= 10 ; i++ ) {
ps.setObject( 1 , ld );
ps.executeUpdate();
// Prepare for next loop.
int randomNumber = ThreadLocalRandom.current().nextInt( 1 , 5 + 1 ); // Pass minimum & ( maximum + 1 ).
ld = ld.plusDays( randomNumber ); // Add a few days, an arbitrary number.
}
}
// Dump all rows, to verify our populating of table.
System.out.println( "Dumping all rows: uuid_ & when_ columns." );
sql = "SELECT uuid_ , when_ FROM tbl_ ; ";
int rowCount = 0;
try (
Statement stmt = conn.createStatement() ;
ResultSet rs = stmt.executeQuery( sql ) ;
) {
while ( rs.next() ) {
rowCount++;
UUID uuid = rs.getObject( 1 , UUID.class );
LocalDate localDate = rs.getObject( 2 , LocalDate.class );
System.out.println( uuid + " " + localDate );
}
}
System.out.println( "Done dumping " + rowCount + " rows." );
// Dump all rows, to verify our populating of table.
System.out.println( "Dumping rows where `when_` is after " + start + ": uuid_ & when_ columns." );
sql = "SELECT uuid_ , when_ FROM tbl_ WHERE when_ > ? ; ";
rowCount = 0; // Reset count.
final PreparedStatement ps = conn.prepareStatement( sql );
ps.setObject( 1 , start );
try (
ps ;
ResultSet rs = ps.executeQuery() ;
) {
while ( rs.next() ) {
rowCount++;
UUID uuid = rs.getObject( 1 , UUID.class );
LocalDate localDate = rs.getObject( 2 , LocalDate.class );
System.out.println( uuid + " " + localDate );
}
}
System.out.println( "Done dumping " + rowCount + " rows." );
} catch ( SQLException eArg ) {
eArg.printStackTrace();
}
When run.
Dumping all rows: uuid_ & when_ columns.
e9c75998-cd67-4ef9-9dce-6c1eed170387 2018-01-23
741c1452-e224-4e5e-95bc-904d8db58b39 2018-01-27
413de43c-a1be-40b6-9ccf-a9b7d9ba873c 2018-01-31
e2aa148f-48b6-4be6-a0fe-f2881b6b5a63 2018-02-03
f498003c-2d8b-446e-ac55-6d7568ce61c3 2018-02-06
c41606d7-8c05-4bba-9f8e-2a0d1f1bb31a 2018-02-09
3df3abe3-1865-4632-99c4-6cd74883c1ee 2018-02-10
914153fe-34f2-4e4f-a91b-944314994839 2018-02-13
96436bdf-80ee-4afe-b55d-f240140ace6a 2018-02-16
82b43f7b-077d-45c1-8c4f-f5b30dfdd44a 2018-02-19
Done dumping 10 rows.
Dumping rows where `when_` is after 2018-01-23: uuid_ & when_ columns.
741c1452-e224-4e5e-95bc-904d8db58b39 2018-01-27
413de43c-a1be-40b6-9ccf-a9b7d9ba873c 2018-01-31
e2aa148f-48b6-4be6-a0fe-f2881b6b5a63 2018-02-03
f498003c-2d8b-446e-ac55-6d7568ce61c3 2018-02-06
c41606d7-8c05-4bba-9f8e-2a0d1f1bb31a 2018-02-09
3df3abe3-1865-4632-99c4-6cd74883c1ee 2018-02-10
914153fe-34f2-4e4f-a91b-944314994839 2018-02-13
96436bdf-80ee-4afe-b55d-f240140ace6a 2018-02-16
82b43f7b-077d-45c1-8c4f-f5b30dfdd44a 2018-02-19
Done dumping 9 rows.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
ResultSet#getDate returns a date with the time part removed. Instantiating a new Date object does contain the time, so you'll have to remove it yourself. E.g.:
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date currentDate = cal.getTime();
It all depends on the Java version that you are using. If you are using Java 6 or Java 7 then this approach will be the easiest:
As you are dealing with java.sql.Date anyways, you can use this quick approach to get Date without time part and compare java.sql.Date to java.sql.Date:
Date currentDate = new java.sql.Date(System.currentTimeMillis());
You can also use java.util.Date#before and java.util.Date#after methods for the better code readability:
while(rs.next()){
System.out.println(dateFormat.format(currentDate));
if (rs.getDate(5).after(currentDate)) {
// System.out.println("Database-date is after currentDate");
} else if (rs.getDate(5).before(currentDate)) {
// System.out.println("Database-date is before currentDate");
PreparedStatement updatexdd = con.prepareStatement(queryy);
updatexdd.setInt(1, rs.getInt(1));
int updatexdd_done = updatexdd.executeUpdate();
} else {
// System.out.println("Database-date is equal to currentDate");
}
}
If you are using Java 8 then you can use new Java time API.

How to check Before and After Date Times in Java for dates that are UTC in MySQL but for Multiple Time Zones?

Have a table setup in MySQL that holds data for an event (e.g. a music show or karaoke) at a particular place of business.
So my table looks like this:
CREATE TABLE `event_schedule` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`description` varchar(30) DEFAULT NULL,
`event_id` varchar(30) NOT NULL DEFAULT,
`event_date` datetime DEFAULT NULL,
`event_date_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
)
Database table contains rows like this:
| 1 | karoake | aab3223 | 2017-08-15 00:00:00 | 2017-08-15 16:00:00 |
| 2 | comedy | cce8465 | 2017-08-25 00:00:00 | 2017-08-25 19:00:00 |
The event_date is in UTC Time format as well is the MySQL server that it resides in.
select now()
Will give time in UTC...
Have logic setup which uses a Spring JDBC to check if a particular row exists based on curdate() like this:
#Repository
public class EventDao {
private static JdbcTemplate jdbcTemplate = null;
#Autowired
public EventDao(#Qualifier("aDataSource") DataSource dataSource) {
EventDao.jdbcTemplateObject = new JdbcTemplate(dataSource);
}
public static String getEventId() {
SqlRowSet eventRowSet = null;
String eventId = "";
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
dateInUTC = formatter.format(new Date());
eventRowSet = jdbcTemplate.queryForRowSet(
"select * from event_schedule where (event_date=CURDATE() or event_date=?)",
new Object[] { dateInUTC });
if (eventRowSet != null && eventRowSet.next()) {
eventId = rst.getString("event_id");
}
return eventId;
}
}
A class used to check if the event is under way:
public class EventProcessor {
public static String getEventStatus() {
String eventId = getEventId();
if (eventId != null) {
return "Event Started";
}
else {
return "Event not Started"
}
// TODO: Need to incorporate for two hours after its over.
// "Event Ended"
}
}
This system seemed to be working when the requirements dependent on just the event_date in UTC was simply:
2017-08-15 00:00:00
New Use Case(s) / Question(s):
If a client app connects to this web service and the situation is:
event starts 2017-08-15 19:00:00 (lets say this is 7:00 p.m PST) how can I set it up do this:
Have the service return "Event Not Started" from 2017-08-14 00:00 to 2017-08-15 18:59:59 ?
Meaning, from the midnight of the same date until one minute before it starts?
Have the service return "Event Ended" two hours after the event ends?
A lot of my client apps that connect to this web service might be in the West Coast (PST), Central (CST), or Eastern (EST).
What is the best way to do a time conversion for them (since I am using UTC Datetimes in the MySQL Database and the MySQL Database is in UTC itself)?
This has been addressed many times already from different angles on Stack Overflow. So search for more info.
Briefly…
I don't use Spring, so here is the basic JDBC to guide you.
Use only java.time classes. Avoid the troublesome old legacy date-time classes such as Date, Calendar, and the java.sql classes.
Store your date-times in UTC if they are past moments in history, or specific moments coming up soon. Query in UTC. Present in user’s expected time zone.
If scheduling more that several weeks out, you may want to use a date-time value without a zone or offset, as politicians frequently re-define time zones with little advance notice. I'll ignore that here.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as PST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Los_Angeles" ) ;
LocalDate startDate = LocalDate.of( 2017 , 8 , 15 ) ;
LocalTime startTime = LocalTime.of( 19 , 0 ) ;
ZonedDateTime start = ZonedDateTime.of( startDate , startTime , z ) ;
Duration d = Duration.ofHours( 2 ) ;
ZonedDateTime stop = start.plus( d ) ;
If you want to store the Duration, call toString and store its generated String using standard ISO 8601 format.
Half-Open logic
Use Half-Open logic, where beginning is inclusive while the ending is exclusive. This entirely avoids your mess with “from the midnight of the same date until one minute before it starts?”.
String sql = "INSERT INTO event ( start , stop , duration , … ) VALUES ( ? , ? , ? , ...) ;" ;
… make prepared statement
myPreparedStatement.setObject( 1 , start.toInstant() ) ;
myPreparedStatement.setObject( 2 , stop.toInstant() ) ;
myPreparedStatement.setObject( 3 , duration.toString() ) ;
myPreparedStatement.setObject( … , … ) ;
Query is similar.
String sql = "SELECT start , stop , duration , pkey , … FROM event WHERE pkey = ? ;" ) ;
… execute query, get ResultSet
… loop the resulting rows
Instant start = myResultSet.get( 1 , Instant.class ) ;
Instant stop = myResultSet.get( 2 , Instant.class ) ;
Duration d = Duration.parse( myResultSet.getString( 3 ) ) ;
Compare to the current moment.
The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant now = Instant.now() ;
Boolean eventScheduled = now.isBefore( start ) ;
Boolean eventOccurring = ( ! now.isBefore( start ) ) && now.isBefore( stop ) ;
Boolean eventExpired = ! now.isBefore( stop ) ; // "Not before stop" means either (a ) now is exactly the moment of expiration (using half-open logic we consider that past), or (b) now is later, after the moment of expiration.
If you want to query for events not yet started:
String sql = "SELECT start , stop , duration , pkey , … FROM event WHERE start > ? ; " ) ; // Find events starting after the passed moment.
…
myPreparedStatement.setObject( 1 , Instant.now() ) ;
The above code assumes your JDBC driver complies with JDBC 4.2 or later for supporting java.time types directly.

Categories

Resources