問題描述
CodeReview:java Dates diff(以天為單位) (CodeReview: java Dates diff (in day resolution))
Please your opinion on the following code.
I need to calculate the diff in days between 2 Date objects. It is assured that both Date objects are within the same TimeZone.
public class DateUtils {
public final static long DAY_TIME_IN_MILLIS = 24 * 60 * 60 * 1000;
/**
* Compare between 2 dates in day resolution.
*
* @return positive integer if date1 > date2, negative if date1 < date2. 0 if they are equal.
*/
public static int datesDiffInDays(final Date date1, final Date date2){
long date1DaysMS = date1.getTime() - (date1.getTime() % DAY_TIME_IN_MILLIS);
long date2DaysMS = date2.getTime() - (date2.getTime() % DAY_TIME_IN_MILLIS);
long timeInMillisDiff = (date1DaysMS - date2DaysMS);
int ret = (int) (timeInMillisDiff / DAY_TIME_IN_MILLIS);
return ret;
}
Can you point to a problem that I might have missed ?
EDIT: @mmyers asked if pass my unit test. Well - Yes. But I have no real experience with dates and I know that is a big subject. Posted below the unit test that I'm using.
public class TestMLDateUtils {
@Test
public final void testDatesDiffInDays() {
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
// 00:00:00.000 1.1.1970
Calendar cal1970 = Calendar.getInstance();
cal1970.setTimeInMillis(0);
Calendar tested = Calendar.getInstance();
tested.setTimeInMillis(0);
// Add 1 millisecond, date = 00:00:00.001 1.1.1970
tested.add(Calendar.MILLISECOND, 1);
assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == 0);
// Add 1 second, date = 00:00:01.001 1.1.1970
tested.add(Calendar.SECOND, 1);
assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == 0);
// Add 1 minute, date = 00:01:01.001 1.1.1970
tested.add(Calendar.MINUTE, 1);
assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == 0);
// Add 1 hour, date = 01:01:01.001 1.1.1970
tested.add(Calendar.HOUR_OF_DAY, 1);
assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == 0);
// date = 23:59:59.999 1.1.1970
tested.setTimeInMillis(0);
tested.add(Calendar.MILLISECOND, 999);
tested.add(Calendar.SECOND, 59);
tested.add(Calendar.MINUTE, 59);
tested.add(Calendar.HOUR_OF_DAY, 23);
//System.out.println("D: " + tested.getTime());
assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == 0);
// date = 00:00:00.000 2.1.1970
tested.setTimeInMillis(0);
tested.add(Calendar.DAY_OF_MONTH, 1);
assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == -1);
assertTrue(DateUtils.datesDiffInDays(tested.getTime(), cal1970.getTime()) == 1);
// date = 00:00:00.001 2.1.1970
tested.add(Calendar.MILLISECOND, 1);
assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == -1);
assertTrue(DateUtils.datesDiffInDays(tested.getTime(), cal1970.getTime()) == 1);
// date = 00:00:01.001 2.1.1970
tested.add(Calendar.SECOND, 1);
assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == -1);
assertTrue(DateUtils.datesDiffInDays(tested.getTime(), cal1970.getTime()) == 1);
// date = 00:01:01.001 2.1.1970
tested.add(Calendar.MINUTE, 1);
assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == -1);
assertTrue(DateUtils.datesDiffInDays(tested.getTime(), cal1970.getTime()) == 1);
// date = 01:01:01.001 2.1.1970
tested.add(Calendar.HOUR_OF_DAY, 1);
assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == -1);
assertTrue(DateUtils.datesDiffInDays(tested.getTime(), cal1970.getTime()) == 1);
// date = 13:01:01.001 2.1.1970
tested.add(Calendar.HOUR_OF_DAY, 12);
assertTrue(DateUtils.datesDiffInDays(cal1970.getTime(), tested.getTime()) == -1);
assertTrue(DateUtils.datesDiffInDays(tested.getTime(), cal1970.getTime()) == 1);
}
}
參考解法
方法 1:
Immediate problem: days can have less than or more than 24 hours due to daylight saving time changes.
Secondary problem: normally when people think in days, they really mean "human days" rather than "periods of 24 hours". In other words, many people would say that 7pm-7am the next day is a difference of a day, whereas 7am-7pm the same day is a difference of zero days. Both are 12 hours. At that point, you really need to know the calendar that is being considered.
Of course, this may not matter for your situation, but we don't really know what that is.
- Third problem: you're using the built-in calendar API instead of Joda Time. That's almost never a good idea - it's horrible and riddled with gotchas and problems. And yes, the regulars here will tell you that's always part of my answer when it comes to Java dates and times - and for good reason. It's really that important.
EDIT: Your test sets the default time zone to be UTC. That's not really a good idea (especially without resetting it in a finally statement). Time zones are tricky, but you should really think about what values you've got, what they mean, and what time zones are involved.
方法 2:
The time zone, if any, within the Date object is irrelevant, since you're using getTime()
; that "[r]eturns the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this Date object."
However, you aren't accounting for leap seconds, which some implementations may return. Thus, if the day range you give has one or more leap seconds in it, and your times are near enough to the same time of day, your calculation may be wrong by a day. That said, there appears to be no way to see if any particular implementation accounts for leap seconds or not (I expect that most don't), and the difference is pretty darn trivial anyway.
方法 3:
Another problem which hasn't been mentioned yet is leap seconds. Days may have more or less than 24 * 60 * 60
seconds due to adjustments in UTC time to keep it more or less in synch with the mean solar year. Probably not a big deal for your usage, but you should at least be aware of the possibility.
A good API is what you need if you have non-trivial requirements for dealing with dates and times. The link to Joda Time in Jon Skeet's answer appears to be broken, so here is a link that does work.
方法 4:
There are many dimensions to a code review; rather than correctness, addressed by others, let me focus a little on style. This will of course be somewhat more subjective than a review concentrating on correctness.
I would inline the "ret" variable. It increases the size of the method without enhancing readability.
I would consider separating the conversion between milliseconds and days into a separate function. Your full class probably performs that division in multiple places. Even if not, it's helpful in that it's easier to name functions that do only one thing.
Speaking of naming, I would rename the function, perhaps to "dayDifference" - abbreviations cause many problems, not least of which is the difficulty of remember which abbreviation was used in which circumstance. If you use none, ever, that particular source of confusion is eliminated. Similarly, I would rename the constant to MILLISECONDS_PER_DAY
.
(by Maxim Veksler、Jon Skeet、cjs、A. Levy、Carl Manaster)