java.util.Timestamp.after() wrong when comparing milliseconds? - java

I am pulling dates out of an Oracle database. They are set on a java.util.Date field and they are in reality java.sql.Timestamp instances (which is a subclass of Java.util.Date). If I compare two of these timestamps from two different database records by calling after() on the first date and compare it to the second, I get the wrong answer when all parts of the date are the same except for the milliseconds.
All of the following should result in "true", however the second set of numbers does not:
firstDate = 1/1/2000 12:00:20:00
secondDate = 1/1/2000 12:00:10:00
result = firstDate.after(secondDate);
result is TRUE <-- EXPECTED RESULT
firstDate = 1/1/2000 12:00:00:10
secondDate = 1/1/2000 12:00:00:00
result = firstDate.after(secondDate);
result is FALSE <-- NOT EXPECTED, result should be TRUE
I know nanos are stored separately from the Date instance in the Timestamp class and I am curious if this is the issue.

You can compare them, but only by comparing millis. While it's quite ugly, it seems to work in all cases (regardless of which is java.sql.Timestamp or java.util.Date).
if(date1.getTime() > date2.getTime()) {
//...
}

The key to the problem here is when the Timestamps are cast up to Date objects. The after method of Date is being used instead of the after method in Timestamp. When not cast to Date, the after method of Timestamp will work correctly.
I guess I need a lesson now on covariant method parameters as to why the after method in Timestamp isn't called in the following code.
java.sql.Timestamp one = new java.sql.Timestamp(1266873627200L);
java.sql.Timestamp two = new java.sql.Timestamp(1266873627000L);
java.util.Date oneDate = (java.util.Date) one;
java.util.Date twoDate = (java.util.Date) two;
System.out.println("one: " + oneDate.getTime());
System.out.println("two: " + twoDate.getTime());
if (oneDate.after(twoDate)) {
System.out.println(oneDate.getTime() + " after " + twoDate.getTime());
} else {
System.out.println(twoDate.getTime() + " after " + oneDate.getTime());
}
results
one: 1266873627200
two: 1266873627000
1266873627000 after 1266873627200

It looks like the problem you're having is that firstDate and secondDate are set to Java.util.Date objects but the JavaDoc for java.sql.Timestamp mentions that it is a composite of java.util.Date and a separate nanoseconds value.
This is why it is returning false when you are trying to compare the two. If you changed firstDate and secondDate over to actual Timestamp objects they should work.

Without the actual code this is a speculation, but I think one of your objects is java.util.Date and the other is java.sql.Timestamp you cannot really compare them, as the millis field in 'Timestamp' is truncated to a second and the remainder is stored in nanos field. It's really unfortunate that Timestamp is a subclass of Date. This definitely leads to a problem that you are experiencing.
EDIT.
One more possibility, is that your DB driver returns its own subclass of Timestamp. Because the class is not final it's quite possible that their implementation screwed up Timestamp's compare, not that it's particularly hard to do.

Related

Compare database time with current time

I need to compare current time with the time that i am getting from database. I am getting time from database in Java class in java.sql.Time format (16:12:00).
I just need to display a error message if current time matches with the time present in DB.
When dealing with dates and times, you can use one of the many libraries like Joda Time, or you can simply consider a time as a given millisecond since 1/1/1970 (unix epoch), expressed as a normal long.
To convert a java.util.Date, or a java.sql.Time,Date etc.. that extends from java.util.Date, to a simple long, you can call getTime() : http://docs.oracle.com/javase/6/docs/api/java/util/Date.html#getTime()
Current time, expressed as milliseconds from unix epoch so comparable with results of getTime(), can be obtained with System.currentTimeMillis();
Once you have that, comparing it is very easy :
Time dbTime = // the time you obtained from the db
long dbLong = dbTime.getTime();
long now = System.currentTimeMillis();
if (dbLong < now) // data in the db is in the past
if (dbLong > now) // data in the db is in the future
if (dbLong == now) // data in the db is exactly now
Take care of the dbLong == now, cause it's precise to the millisecond, so it will rarely happen in practice, unless you use a range or reduce the precision, say, to the second or minute :
long dbLongSeconds = dbLong / 1000;
long dbLongMinutes = dbLong / (60*1000);
long nowSeconds = now / 1000;
long nowMinutes = now / (60*1000);
if (dbLongSeconds == nowSeconds) // data in the db is in this second
if (dbLongMinutes == nowMinutes) // data in the db is in this minute
If you need more sophisticated comparisons, like day or month, you should use either a library like Joda Time, or built in classes like Calendar, cause the math is way more complex given how western calendar divides the year.
To compare your current time with the time from the database you could simply construct a sql.Time from System.currentTimeMillis() and compare the two toString()s like so:
java.sql.Time serverTime = getServerTime();
java.sql.Time currentTime = new java.sql.Time(System.currentTimeMillis());
if(serverTime.toString().compareTo(currentTime.toString()) == 0)
{
//yay
}
else //nay
You could also compare the two sql.Time's directly using it's compareTo method, but this is trickier.
This is because even though sql.Time's setDate/Year/Month is deprecated and will throw an exception if you use them( which makes sense because they're not a date, only a time) the sql.Time's compareTo uses its superclass implementation, which means it compares not only the time but also the date, which sucks 'cus your database sql.Time object will probably always have the date 1970.01.01 whereas any sql.Time you construct off of System.currentTimeMillis() will have the current date. You can get around this by using a Calendar object as shown.
Calendar tmp = new GregorianCalendar();
tmp.setTimeInMillis(System.currentTimeMillis());
tmp.set(Calendar.YEAR, 1970);
tmp.set(Calendar.MONTH, 0); // 0 == January
tmp.set(Calendar.DATE, 1);
tmp.set(Calendar.MILLISECOND, 0);
java.sql.Time currentTime = new java.sql.Time(c.toInstant().toEpochMilli());
java.sql.Time serverTime = getServerTime();
if(currentTime.compareTo(serverTime) == 0)
{
//yay
}
else //nay
Or you could compare the long times directly as in Simone Gianni's answer, which would probably be the more efficient solution.

Comparing dates with JUnit testing

Hello I'm new to the site and have a issue with my application using JUnit testing. My issue is when I try to compare the Date method with itself it always fails. I printed the Date object in the test to see the problem and always end up with the package name and random letters. Here is the Date constructor:
public class Date
{
SimpleDateFormat dformat = new SimpleDateFormat("dd-MM-yyyy");
private int day;
private int month;
private int year;
public Date()
{
String today;
Calendar present = Calendar.getInstance();
day = present.get(Calendar.DAY_OF_MONTH);
month = present.get(Calendar.MONTH);
year = present.get(Calendar.YEAR);
present.setLenient(false);
present.set(year, month - 1, day, 0, 0);
today = dformat.format(present.getTime());
System.out.println(today);
}
Here is my test:
#Test
public void currentDay()
{
Date current = new Date();
System.out.println(current);
assertEquals("today:", current, new Date());
}
Yet the result always fails and I get something on the lines of:
comp.work.wk.Date#d3ade7
Any help would be appreciated.
Overriding default equals method is not necessary. You simply use Date.compareTo() to compare two date objects.
Although the answer by #Shrikanth solves it, this issue also arises with normal Date objects. Two possible solutions are given here:
Make use of DateUtils.truncate (or even DateUtils.truncatedEquals) to compare the dates. This is something you could use in your equals method, or for normal Date objects directly in you assertEquals/assertTrue.
assertEquals(DateUtils.truncate(date1,Calendar.SECOND),
DateUtils.truncate(date2,Calendar.SECOND));
Don't check whether the dates are the same, but whether they are close enough to eachother (for JUnit test sake):
assertTrue("Dates aren't close enough to each other!",
Math.abs(date2.getTime() - date1.getTime()) < 1000);
The default equals object compares memory locations. If both objects are pointing to same memory location then only it will print equals which is not the case in your program. since they are pointing to two different memory locations it is always giving false which is expected.
If you feel your assertEquals(date1,date2) method should return true since the contents are equal then you should override the equals method. And when ever you override equals you should override hashcode() method also to ensure that you can confidently use your class instance as a key in any hashing based collection like HashMap or HashSet.
Here is a link explaining how to override equals() and hashcode() method
http://javarevisited.blogspot.in/2011/02/how-to-write-equals-method-in-java.html
And don't name your class same as any API class as Jon Skeet suggested.
Hope this helps.
Update The Joda-Time project is now in maintenance mode, with the team recommending migration to the java.time classes.
This kind of work is much easier with the Joda-Time library instead of the notoriously troublesome Date/Calendar classes.
Example Code in Joda-Time 2.3
Set up some data…
DateTime now = new DateTime();
DateTime yesterday = now.minusDays( 1 );
DateTime nowAgain = new DateTime( now.toString() ); // New object, but same value inside.
Compare…
boolean isNowEqualToYesterday = now.equals( yesterday );
boolean isNowEqualToNowAgain = now.equals( nowAgain );
Dump to console…
System.out.println( "now: " + now );
System.out.println( "yesterday: " + yesterday );
System.out.println( "nowAgain: " + nowAgain );
System.out.println( "isNowEqualToYesterday: " + isNowEqualToYesterday );
System.out.println( "isNowEqualToNowAgain: " + isNowEqualToNowAgain );
When run…
now: 2014-02-06T01:31:43.157-08:00
yesterday: 2014-02-05T01:31:43.157-08:00
nowAgain: 2014-02-06T01:31:43.157-08:00
isNowEqualToYesterday: false
isNowEqualToNowAgain: true
Convert
You can convert in and out of Joda-Time if need be.
org.joda.time.DateTime dateTime = new DateTime( date ); // From Date to DateTime.
java.util.Date date = dateTime.toDate(); // From DateTime to Date.
You need to override both equals() and toString(). If you override equals, always override hashcode() so that your maps work properly.
I have this solution to test date order
// date1 has to be before date2
if( !date1.before(date2) ) Assert.fail("Date1 is not before date2");
// date1 has to be before or equals to date2
if( date1.after() ) Asssert.fail("Date1 is after date2")
java.time.LocalDate
For production code never create your own Date class (as an exercise it’s fine). Use LocalDate from java.time, the modern Java date and time API.
For example:
ZoneId zone = ZoneId.of("Africa/Niamey");
LocalDate current = LocalDate.now(zone);
System.out.println(current);
System.out.println("Is equal? " + current.isEqual(LocalDate.now(zone)));
Output when I ran this code just now:
2021-04-07
Is equal? true
If the code happens to run just over midnight so now() is called just before and just after the day changes, then you will get two different dates, and the second line will be Is equal? false.
Since it is never the same date in all time zones, specifying the desired time zone is crucial.
If this was for an exercise
If you are doing an exercise requiring you to write your own date class, then it’s a fine exercise. In this case the answers stating that in your date class you should override equals(), hashCode() and toString() are the correct ones.
In your own date class you should not base its functionality on Calendar nor SimpleDateformat. Calendar is poorly designed, SimpleDateFormat is notoriously troublesome, and both are long outdated. Instead use LocalDate and DateTimeFormatter from java.time.
Tutorial link
Oracle tutorial: Date Time explaining how to use java.time.

Comparing dates stored in date objects and getting a boolean returned

I am having trouble comparing dates in Java I have tried:
(today == actDate)
(today.equals(actDate))
Both always seem to evaluate to false:
In the image above the first date is today and the second is actDate.
Both are date objects:
Date today = new Date(System.currentTimeMillis());
Date actDate = new Date(taskHours.get(j).getDate().getTime());
Then I tried using compareTo, but this appears to return a 1 if the date is greater and a -1 if lower.
What am I doing wrong?
java.util.Date and java.sql.Date differ. This might be the reason.
your problem is Date today = new Date(System.currentTimeMillis());. With this you use the current system-time in milliseconds , which are stored in the date object. Your second date doesn't hold the information with milliseconds and this why every comparison fails.
Working with dates is a bit tricky. You need to use compareTo as JDBC works with java.sql.Timestamp and within application you usually work with java.util.Date. Two objects are (usually) not considered to be equal if they are instances of two different classes (even thou they might be from the same hierarchy).
What you want to do is:
public boolean isSameDate(Date date1, Date date2) {
return date1.compareTo(date2) == 0;
}
If you want to make your life easier when null values come in play, use commons-lang:
public boolean isSameDate(Date date1, Date date2) {
return ObjectUtils.compare(date1, date2) == 0;
}
If you want to compare just the date information (not the time), then I suggest you to use Joda Time library (namely LocalDate class). Converting timestamps to calendar date objects is not a straghtforward (one line of code) task with standard Java components.
You can use the Date#after(Date when) and Date#before(Date otherDate) methods to compare dates and achive some order.
You are comparing 2 different dates (maybe seconds are different), so equals method return false which is expected. Using equals is the right way to compere them.
Use a Calendar object instead. And manually compare day, month and year separately.
Calendar cal1 = new Calendar();
cal1.setTime(date1);
Calendar cal2 = new Calendar();
cal2.setTime(date2);
boolean equal = true;
equal &= cal1.get(Calendar.DAY_OF_MONTH) == cal2.get(Calendar.DAY_OF_MONTH);
equal &= cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH);
equal &= cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR);

Comparing two dates using Joda time

I want to compare two dates, however I'm running into trouble. 1 date is created from a java.util.date object and the other is manually crafted. The following code is an example:
Date ds = new Date();
DateTime d = new DateTime(ds);
DateTime e = new DateTime(2012,12,07, 0, 0);
System.out.println(d.isEqual(e));
However the test turns out false. I am guessing that it is because of the time. How can I check if these two dates are equal to each other (I mean the Year, month, date are identical)?
System.out.println(d.toDateMidnight().isEqual(e.toDateMidnight()));
or
System.out.println(d.withTimeAtStartOfDay().isEqual(e.withTimeAtStartOfDay()));
You should use toLocalDate():
date1.toLocalDate().isEqual(date2.toLocalDate())
This will get rid of the Time part of the DateTime.
There is another approach, but it does not account for the case where the two dates have a different timezone, so it's less reliable:
date1.withTimeAtStartOfDay().isEqual(date2.withTimeAtStartOfDay())
return DateTimeComparator.getDateOnlyInstance().compare(first, second);
Via How to compare two Dates without the time portion?
If you want to ignore time components (i.e. you want to compare only dates) you can use DateMidnight class instead of Date Time. So your example will look something like this:
Date ds = new Date();
DateMidnight d = new DateMidnight(ds);
DateMidnight e = new DateMidnight(2012, 12, 7);
System.out.println(d.isEqual(e));
But beware, it will print "true" only today :)
Also note that by default JDK Date and all Joda-Time instant classes (DateTime and DateMidnight included) are constructed using default timezone. So if you create one date to compare in code, but retrieve another one from the DB which probably stores dates in UTC you may encounter inconsistencies assuming you are not in UTC time zone.
As they're DateTime objects, their time parts are also taken into consideration when you're comparing them. Try setting the time parts of the first date to 0, like:
d = d.withTime(0, 0, 0, 0);
I stumbled into this question while looking for a comparison with today. Here's how you can compare a date to today :
date1.toLocalDate().isBeforeNow() // works also with isAfterNow
This is a static method which works for me.
public static boolean isSameDay(DateTime date1, DateTime date2){
return date1.withTimeAtStartOfDay().isEqual(date2.withTimeAtStartOfDay());
}
DateTimeComparator.getDateOnlyInstance().compare(obj1, obj2);
obj1 and obj2 can be a String, Long, Date(java.util)... For the details see
http://www.joda.org/joda-time/apidocs/index.html?org/joda/time/DateTimeComparator.html
Write your own method
public boolean checkEqual(DateTime first,DateTime second){
if(first.<getterforyear> == second.<getterforyear> && first.<getterformonth> == second.<getterformonth> && first.<getterforday> == second.<getterforday>){
return true;
}
return false;
}

Best way to get maximum Date value in java?

I'm writing a bit of logic that requires treating null dates as meaning forever in the future (the date in question is an expiration date, which may or may not exist). Instead of putting in special cases for a null date throughout the code, I want to just convert null into the maximum possible Date. I don't see any obvious ways to get such a value without hard coding it. What's the best way to get the maximum value of whatever Date implementation is being used?
Try
new Date(Long.MAX_VALUE)
which should give you the longest possible date value in Java.
Encapsulate the functionality you want in your own class, using Long.MAX_VALUE will most likely cause you problems.
class ExpirationDate {
Date expires;
boolean hasExpiration() {
return expires == null;
}
Date getExpirationDate() {
return expires;
}
boolean hasExpired(Date date) {
if (expires == null) {
return true;
} else {
return date.before(expires);
}
}
...
}
+1 to the Long.MAX_VALUE suggestions. It seems that this would help you if you sort stuff by your date field.
However, instead of constructing a date from some the large constant value where ever you need the date, use a globally visible singleton to hold a Date instance that represents your special value:
class DateUtil
{
public static final Date NO_EXPIRE = new Date( Long.MAX_VALUE );
}
Then you can use simple identity comparison (mydate == DateUtils.NO_EXPIRE) to test if a particular date is of your special case instead of obj.equals(); (ie. mydate.equals ( DateUtils.NO_EXPIRE ); )
Here is what I do:
public static final TimeZone UTC;
// 0001.01.01 12:00:00 AM +0000
public static final Date BEGINNING_OF_TIME;
// new Date(Long.MAX_VALUE) in UTC time zone
public static final Date END_OF_TIME;
static
{
UTC = TimeZone.getTimeZone("UTC");
final Calendar c = new GregorianCalendar(UTC);
c.set(1, 0, 1, 0, 0, 0);
c.set(Calendar.MILLISECOND, 0);
BEGINNING_OF_TIME = c.getTime();
c.setTime(new Date(Long.MAX_VALUE));
END_OF_TIME = c.getTime();
}
Note that if the TimeZone is NOT UTC you will get offsets from the "end of time", which won't be maximal values. These are especially useful for inserting into Database fields and not having to have NULL dates.
have you considered adopting the use of Joda Time?
It's slated to be included in java 7 as the basis for JSR-310
The feature that may interest you is ZeroIsMaxDateTimeField
which basically swaps zero fields for the maximum value for that field within the date-time.
From Java SE 8 you could use:
LocalDate.MAX
One problem I see is that for sorting on expiration date, using a null isn't easily sortable. So replacing with an actual value (even if it's an arbitrary sentry value well into the future) may be needed.
I suppose another way of treating "no expiration" is simply to say something expires 100 years in the future... Unless your database is dealing with long-term contracts!
I like Instant.MAX because it is more likely to be supported in the future than Long.MAX_VALUE.
Note that as of today, though, Instant.MAX.toEpochMilli() throws an overflow error.
Perhaps one option is to use the maximal system date. You can get it by using:
System.out.println(new Date(Long.MAX_VALUE).toString())
//Output:
//Sun Aug 17 12:42:55 IST 292278994

Categories