Consider the following code to only determine if the time component of one Date object is before the time component of another Date object:
private boolean validStartStopTime( Date start, Date stop ) {
Calendar startCal = Calendar.getInstance();
Calendar stopCal = Calendar.getInstance();
startCal.clear();
stopCal.clear();
startCal.setTime( start );
stopCal.setTime( stop );
startCal.set( Calendar.YEAR, 2011 );
stopCal.set( Calendar.YEAR, 2011 );
startCal.set( Calendar.MONTH, 1 );
stopCal.set( Calendar.MONTH, 1 );
startCal.set( Calendar.DAY_OF_YEAR, 1 );
stopCal.set( Calendar.DAY_OF_YEAR, 1 );
return startCal.before( stopCal );
}
Would this insure that time comparison is correct? Is there a better alternative (Joda is not an option)? I believe that this is equivalent to setting the Calendar objects to current date/time and manually copying over the hour, minutes, and milliseconds component. You can assume that timezone are the same.
EDIT: To clarify what I mean by comparing only the time component of a Date object. I mean that when looking specifically at the time portion, the start time is before the stop time. The date portion is ABSOLUTELY irrelevant (in that start="Jan 2 20011 10AM" and end="Jan 1 2011 11AM" is perfectly fine), if I had a choice I'd simply use something that contained just the time but a Date object is what I'm given. I'd like to not write a sequence of if-else which is why I have the approach above but I welcome a cleaner/better approach.
Your code should work fine. You could also format just the time components in a zero-based string notation and compare them lexicographically:
public static boolean timeIsBefore(Date d1, Date d2) {
DateFormat f = new SimpleDateFormat("HH:mm:ss.SSS");
return f.format(d1).compareTo(f.format(d2)) < 0;
}
[Edit]
This is assuming that the dates have the same timezone offset. If not you'll have to adjust them manually beforehand (or as part of this function).
There are 86,400,000 milliseconds in a day, why not just use that to figure it out?
You could just mod timeInMilliseconds with that number and compare the results.
Related
I am having problem with java Calendar dates.
Bassically I have a list of holidays and want to check if a date belong
to the list. Still confused about how the Calendar class
is creating and formating its dates.
Please see the folowing code sample:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class CompareCalendarDates {
public static void main(String args[]) {
Date date1 = null;
try {
date1= new SimpleDateFormat("yyyy-MM-dd").parse("2019-12-25");
} catch (ParseException e) {
date1 = null;
}
Calendar calendar1 = Calendar.getInstance();
calendar1.clear();
calendar1.setTime(date1);
Calendar calendar2 = Calendar.getInstance();
calendar2.clear();
calendar2.setLenient(false); // Don't automatically convert invalid date.
calendar2.set(2019, 11, 25, 0, 0, 0);
calendar2.getTimeInMillis();
boolean isEqual = calendar1.equals(calendar2);
System.out.println ("Are to dates equal: " + isEqual);
}
}
I'm creating in 2 dates using different methods from Calendar class.
In my opinion the dates should be equal but there are not.
What am I missing? What is the difference between the methods
setTime () and set ?
You should use
boolean isEqual = calendar1.getTime().equals(calendar2.getTime());
Instead of
boolean isEqual = calendar1.equals(calendar2);
because the equals() method in Calendar class looks like these:
public boolean equals(Object obj) {
return obj instanceof Date && getTime() == ((Date) obj).getTime();
}
So it checks if the dates in these two calendars are the same objects, and in your case they are not the same objects, they jut have the same value.
tl;dr
LocalDate
.parse( "2019-12-25" )
.isEqual(
LocalDate.of( 2019 , Month.DECEMBER , 25 )
)
true
Details
Never use Date/Calendar. Use only java.time classes.
LocalDate::isEqual
For a date-only value without time-of-day and without time zone, use LocalDate.
LocalDate ld = LocalDate.parse( "2019-12-25" ) ;
LocalDate other = LocalDate.of( 2019, 11, 25 ) ;
Compare using isEqual, isBefore, isAfter.
boolean sameDates = ld.isEqual( other ) ;
This has been addressed many many times already on Stack Overflow. So I am keeping this Answer brief. Search to learn more.
December versus November
Unlike those legacy classes, the java.time classes use sane numbering. Months are counted 1-12 for January-December.
So notice that your pair of inputs differ, one for December, and one for November.
For clarity, you can use Month enum rather than an integer. Another benefit of enums is to ensure valid values.
LocalDate other = LocalDate.of( 2019 , Month.NOVEMBER , 25 ) ;
java.time
If you have got a Calendar object from a legacy API that you cannot afford to upgrade to java.time, the modern Java date and time API, just now, and you want to know whether it denotes the same date as some string, convert both to LocalDate and compare using the isEqual method:
String date1Str = "2019-12-25";
Calendar calendar2 = new GregorianCalendar(2019, Calendar.DECEMBER, 25);
LocalDate date1 = LocalDate.parse(date1Str);
LocalDate date2 = ((GregorianCalendar) calendar2).toZonedDateTime().toLocalDate();
boolean isEqual = date1.isEqual(date2);
System.out.println ("Are to dates equal: " + isEqual);
Output:
Are to dates equal: true
A LocalDate is a date without time of day, time zone, and all the other things that an old-fashioned Calendar object carries with it. So comparing two LocalDate will give you the result that you had expected.
What went wrong in your code?
While your two Calendar objects do denote the same point in time and also the same calendar date (which is not the same thing to ask), there are some differences, for example:
calendar1 has got all its fields set while calendar2 has got some uncomputed fields including for example era, week of year, AM/PM, millisecond of second and zone offset.
calendar1 is lenient, calendar2 is not.
I believe that this is more than enough that the objects are not considered equal. I haven’t checked the documentation for the exact criteria. You may do that out of curiosity, but I suggest that you don’t need to because you are not going to need to compare two Calendar objects for equality.
To answer your question, it seems that setTime() sets all fields while set obviuosly only sets some. I was surprised to see that getTimeInMillis() didn’t calculate the rest, but it doesn’t.
Link
Oracle tutorial: Date Time explaining how to use java.time.
the following method is written using the deprecated android Time class
// To make it easy to query for the exact date, we normalize all dates that go into
// the database to the start of the the Julian day at UTC.
public static long normalizeDate(long startDate) {
// normalize the start date to the beginning of the (UTC) day
Time time = new Time();
time.set(startDate);
int julianDay = Time.getJulianDay(startDate, time.gmtoff);
return time.setJulianDay(julianDay);
}
you can find this method here at line 47
please help me to understand it...
I tried different (unix, UTC) values for startDate argument such as 1464174000 and 1464433200 just to understand the output of the method but the method always return 1458000000 which is equivalent to:
03/15/2016 # 12:00am (UTC)
see the output here
so what is the purpose of the method if it always return the same value ?
i want to understand it so that i can write it again with the GregorianCalendar class that is not deprecated
From here
Callers must pass the time in UTC millisecond (as can be returned by toMillis(boolean) or normalize(boolean)) and the offset from UTC of the timezone in seconds (as might be in gmtoff).
So startDate should be in millisecond, not in second. Call with proper value. For example-
public static long normalizeDate(long startDate) {
// normalize the start date to the beginning of the (UTC) day
Time time = new Time();
time.set(1464181063013);
int julianDay = Time.getJulianDay(1464181063013, time.gmtoff);
return time.setJulianDay(julianDay);
}
I hope it will give you a different result.
I have a date and a number and want to check if this date and this number occurs in a list of other dates within:
+-20 date intervall with the same number
so for example 1, 1.1.2013 and 1,3.1.2013 should reuturn false.
I tried to implement the method something like that:
private List<EventDate> dayIntervall(List<EventDate> eventList) throws Exception {
List<EventDate> resultList = new ArrayList<EventDate>();
for (int i = 0; i < eventList.size(); i++) {
String string = eventList.get(i).getDate();
Date equalDate = new SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN).parse(string);
for (int j = 0; j < eventList.size(); j++) {
String string1 = eventList.get(i).getDate();
Date otherDate = new SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN).parse(string1);
if (check number of i with number of j && check Date) {
//do magic
}
}
}
return resultList;
}
The construction of the iteration method is not that hard. What is hard for me is the date intervall checking part. I tried it like that:
boolean isWithinRange(Date testDate, Date days) {
return !(testDate.before(days) || testDate.after(days));
}
However that does not work because days are not takes as days. Any suggestions on how to fix that?
I really appreciate your answer!
You question is difficult to follow. But given its title, perhaps this will help…
Span Of Time In Joda-Time
The Joda-Time library provides a trio of classes to represent a span of time: Interval, Period, and Duration.
Interval
An Interval object has specific endpoints that lie on the timeline of the Universe. A handy contains method tells if a DateTime object occurs within those endpoints. The beginning endpoint in inclusive while the last endpoint is exclusive.
Time Zones
Note that time zones are important, for handling Daylight Saving Time and other anomalies, and for handling start-of-day. Keep in mind that while a java.util.Date seems like it has a time zone but does not, a DateTime truly does know its own time zone.
Sample Code
Some code off the top of my head (untested)…
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Berlin" );
DateTime dateTime = new DateTime( yourDateGoesHere, timeZone );
Interval interval = new Interval( dateTime.minusDays( 20 ), dateTime.plusDays( 20 ) );
boolean didEventOccurDuringInterval = interval.contains( someOtherDateTime );
Whole Days
If you want whole days, call the withTimeAtStartOfDay method to get first moment of the day. In this case, you probably need to add 21 rather than 20 days for the ending point. As I said above, the end point is exclusive. So if you want whole days, you need the first moment after the time period you care about. You need the moment after the stroke of midnight. If this does not make sense, see my answers to other questions here and here.
Note that Joda-Time includes some "midnight"-related methods and classes. Those are no longer recommended by the Joda team. The "withTimeAtStartOfDay" method takes their place.
DateTime start = dateTime.minusDays( 20 ).withTimeAtStartOfDay();
DateTime stop = dateTime.plusDays( 21 ).withTimeAtStartOfDay(); // 21, not 20, for whole days.
Interval interval = new Interval( start, stop );
You should avoid java.util.Date if at all possible. Using the backport of ThreeTen (the long awaited replacement date/time API coming in JDK8), you can get the number of days between two dates like so:
int daysBetween(LocalDate start, LocalDate end) {
return Math.abs(start.periodUntil(end).getDays());
}
Does that help?
You can get the number of dates in between the 2 dates and compare with your days parameter. Using Joda-Time API it is relatively an easy task: How do I calculate the difference between two dates?.
Code:
SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN);
Date startDate = format.parse("1.1.2013");
Date endDate = format.parse("3.1.2013");
Days d = Days.daysBetween(new DateTime(startDate), new DateTime(endDate));
System.out.println(d.getDays());
Gives,
2
This is possible using Calendar class as well:
Calendar cal = Calendar.getInstance();
cal.setTime(startDate);
System.out.println(cal.fieldDifference(endDate, Calendar.DAY_OF_YEAR));
Gives,
2
This 2 can now be compared to your actual value (20).
Calculating the difference between two dates (java.util.Date) in terms of no. of days look like very simple and we can find different ways to do that. I used the following code to calculate the date difference:
public static long daysBetween(Calendar startDate, Calendar endDate) {
Calendar date = (Calendar) startDate.clone();
long daysBetween = 0;
while (date.before(endDate)) {
date.add(Calendar.DAY_OF_MONTH, 1);
daysBetween++;
}
return daysBetween;
}
In main(), I used the following two dates :
Calendar c1 = Calendar.getInstance();
c1.set(2011, 1, 1);
Calendar c2 = Calendar.getInstance();
c2.set(2011, 1, 31);
long difference = daysBetween(c1, c2); //
But the value of the variable difference is not consistent. It is sometimes 30 and sometimes 31. So, why that might have happened.
Is there any solution to use the method results a consistent output ?
You're setting the date part of the calendars, but not the time part.
Sometimes the clock will tick between the calls to getInstance() and sometimes it won't, hence the inconsistency.
Options:
Set the time as well as the date, e.g. to midnight
Use a better date/time library - Joda Time - which has a more suitable representation (LocalDate). An important moral here is that if you can find a type which represents the exact information you have, and nothing else, that's likely to be a good fit and cause fewer complications.
Using LocalDate, you wouldn't even have to do the loop as Joda Time has good support for computing the differences between two values anyway.
LocalDate date1 = new LocalDate(2011, 1, 1);
LocalDate date2 = new LocalDate(2011, 1, 31);
Days period = Days.daysBetween(days1, days2);
int days = period.getDays();
You are only setting the year, month and day. The hours, minutes, seconds and milli-seconds are the current time (and thus different every time you run it)
I suggest you use Joda Time's LocalDate instead as it appears to does exactly what you want.
My program needs to represent this date as a java.sql.date object , but it seems that when I create a new date (using the calendar) and set it to '9999-12-31' and finally convert this java.util.date object to a java.sql.date object, this date is converted to something like '000-01-31'.
Calendar calendar = Calendar.getInstance();
calendar.set(9999, 12, 31);
infinityDate = new java.sql.Date(normalizeDate(calendar.getTime()).getTime());
infinityDate should be 31-12-9999
but when my code reaches here :
if(otherDate.equals(infinityDate))
{// Do stuff}
It never goes into the if condition as the infinityDate has for some reason been changed to 31-01-000, even though otherDate is infact '31-12-9999'.
The fact that otherDate is 31-12-9999 tells me that java can represent this dates , but for some reason , when I construct it using a calendar it changes the date. (otherDate comes from a jdbc statement which fetches data from a database)
This reference date '31-12-9999' has been fixed by some client , so it cannot be changed and my program has to be able to compare some incoming date values with this.
Does anyone know why this is happening , I realize that http://en.wikipedia.org/wiki/Year_10,000_problem may be a problem for dates after year 9999 , but I should be safe by a day.
EDIT : The Normalize date method only "normalizes the given date to midnight of that day"
private static java.util.Date normalizeDate(java.util.Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
date = calendar.getTime();
return date;
}
But , this issue was appearing before I was normalizing the date , I normalized it in an attempt to fix this.
Months are zero indexed. Use 11 for December, not 12. This is why you are rolling over the year.
Calendar.MONTH is zero-based. The call
calendar.set(9999, 12, 31);
sets the date to "the 31st day in the 13th month of the year 9999", which is then implicitly converted to the 1st month of the year 10000. It would result in an exception if you first called
calendar.setLenient(false);
Check hours, minutes, seconds and milliseconds that are held into these 2 date objects. I believe they are different.
If your want to compare the date (year, month, day) only you should probably create your custom Comparator and use it.